Как работает это ключевое слово?

1195

Я заметил, что, похоже, нет четкого объяснения того, что такое ключевое слово this и как оно правильно (и неправильно) используется в JavaScript.

Я наблюдал за этим очень странное поведение и не понял, почему это произошло.

Как работает this и когда он должен использоваться?

Показать ещё 7 комментариев
Теги:
this

23 ответа

1271
Лучший ответ

Я рекомендую сначала прочитать статью Майка Уэста " Область применения в JavaScript (зеркало)". Это отличное, дружественное введение в концепции this и цепочки областей действия в JavaScript.

Как только вы начинаете привыкать к this, правила на самом деле довольно просты. Стандарт ECMAScript 5.1 определяет this:

§11.1.1 Ключевое слово this

Ключевое слово this оценивает значение ThisBinding текущего контекста выполнения.

ThisBinding - это то, что поддерживает интерпретатор JavaScript при оценке кода JavaScript, например, специальный регистр ЦП, который содержит ссылку на объект. Интерпретатор обновляет ThisBinding каждый раз, когда устанавливает контекст выполнения только в одном из трех разных случаев:

1. Начальный глобальный контекст выполнения

Это касается кода JavaScript, который оценивается на верхнем уровне, например, когда он находится непосредственно внутри <script>:

<script>
  alert("I'm evaluated in the initial global execution context!");

  setTimeout(function () {
      alert("I'm NOT evaluated in the initial global execution context.");
  }, 1);
</script>

При оценке кода в начальном глобальном контексте выполнения ThisBinding устанавливается на глобальный объект, window (§10.4.1.1).

Ввод электронного кода

  • … Прямым вызовом eval() ThisBinding остается без изменений; это то же значение, что и ThisBinding для вызывающего контекста выполнения (§10.4.2 (2) (a)).

  • … Если не прямым вызовом eval()
    ThisBinding устанавливается для глобального объекта, как если бы он выполнялся в начальном глобальном контексте выполнения (§10.4.2 (1)).

§15.1.2.1.1 определяет, что такое прямой вызов eval(). По сути, eval(...) - это прямой вызов, тогда как что-то вроде (0, eval)(...) или var indirectEval = eval; indirectEval(...); var indirectEval = eval; indirectEval(...); это косвенный вызов eval(). Смотрите ответ chuckj на (1, eval) ('this') против eval ('this') в JavaScript? и Дмитрий Сошниковых ECMA-262-5 в деталях. Глава 2. Строгий режим. когда вы можете использовать косвенный вызов eval().

Ввод кода функции

Это происходит при вызове функции. Если функция вызывается для объекта, например, в obj.myMethod() или эквивалентном obj["myMethod"](), тогда ThisBinding устанавливается на объект (obj в примере; §13.2.1). В большинстве других случаев ThisBinding устанавливается на глобальный объект (§10.4.3).

Причина написания "в большинстве других случаев" заключается в том, что есть восемь встроенных функций ECMAScript 5, которые позволяют указывать ThisBinding в списке аргументов. Эти специальные функции принимают так называемый thisArg который становится ThisBinding при вызове функции (§10.4.3).

Эти специальные встроенные функции:

  • Function.prototype.apply( thisArg, argArray )
  • Function.prototype.call( thisArg [, arg1 [, arg2,... ] ] )
  • Function.prototype.bind( thisArg [, arg1 [, arg2,... ] ] )
  • Array.prototype.every( callbackfn [, thisArg ] )
  • Array.prototype.some( callbackfn [, thisArg ] )
  • Array.prototype.forEach( callbackfn [, thisArg ] )
  • Array.prototype.map( callbackfn [, thisArg ] )
  • Array.prototype.filter( callbackfn [, thisArg ] )

В случае Function.prototype они вызываются для объекта функции, но вместо установки ThisBinding для объекта функции ThisBinding устанавливается в thisArg.

В случае функций Array.prototype данный callbackfn вызывается в контексте выполнения, где ThisBinding установлен в thisArg если он предоставлен; в противном случае к глобальному объекту.

Это правила для простого JavaScript. Когда вы начнете использовать библиотеки JavaScript (например, jQuery), вы можете обнаружить, что некоторые библиотечные функции манипулируют значением this. Разработчики этих библиотек JavaScript делают это, потому что это имеет тенденцию поддерживать самые распространенные варианты использования, и пользователи библиотеки обычно считают такое поведение более удобным. При передаче функций обратного вызова, ссылающихся на this библиотечным функциям, вы должны ссылаться на документацию для любых гарантий того, каково значение this при вызове функции.

Если вам интересно, как библиотека JavaScript манипулирует значением this, библиотека просто использует одну из встроенных функций JavaScript, принимающих thisArg. Вы также можете написать свою собственную функцию с функцией обратного вызова и thisArg:

function doWork(callbackfn, thisArg) {
    //...
    if (callbackfn != null) callbackfn.call(thisArg);
}

Это особый случай, о котором я еще не упомянул. При создании нового объекта с помощью оператора new интерпретатор JavaScript создает новый пустой объект, устанавливает некоторые внутренние свойства и затем вызывает функцию конструктора для нового объекта. Таким образом, когда функция вызывается в контексте конструктора, значением this является новый объект, созданный интерпретатором:

function MyType() {
    this.someData = "a string";
}

var instance = new MyType();
// Kind of like the following, but there are more steps involved:
// var instance = {};
// MyType.call(instance);

Функции стрелок

Функции стрелок (введенные в ECMA6) изменяют объем this. См. Существующий канонический вопрос: функция Arrow против объявления/выражения функции: являются ли они эквивалентными/заменяемыми? для дополнительной информации. Но вкратце:

Функции стрелки не имеют свои собственные this.... переплета. Вместо этого эти идентификаторы разрешаются в лексической области, как и любая другая переменная. Это означает, что внутри функции стрелки this... ссылается на значения this в среде, в которой определена функция стрелки.

Просто для удовольствия, проверьте свое понимание с некоторыми примерами

Чтобы раскрыть ответы, наведите курсор мыши на светло-желтые прямоугольники.

  1. Какова ценность this в отмеченной линии? Зачем?

    window - отмеченная строка оценивается в начальном глобальном контексте выполнения.

    if (true) {
        // What is 'this' here?
    }
    
  2. Каково значение this в отмеченной строке при выполнении obj.staticFunction()? Зачем?

    obj - при вызове функции для объекта ThisBinding устанавливается для объекта.

    var obj = {
        someData: "a string"
    };
    
    function myFun() {
        return this // What is 'this' here?
    }
    
    obj.staticFunction = myFun;
    
    console.log("this is window:", obj.staticFunction() == window);
    console.log("this is obj:", obj.staticFunction() == obj);
      
  3. Какова ценность this в отмеченной линии? Зачем?

    window

    В этом примере интерпретатор JavaScript вводит код функции, но поскольку myFun/obj.myMethod не вызывается для объекта, ThisBinding установлен в window.

    Это отличается от Python, в котором доступ к методу (obj.myMethod) создает связанный объект метода.

    var obj = {
        myMethod: function () {
            return this; // What is 'this' here?
        }
    };
    var myFun = obj.myMethod;
    console.log("this is window:", myFun() == window);
    console.log("this is obj:", myFun() == obj);
      
  4. Какова ценность this в отмеченной линии? Зачем?

    window

    Этот был хитрым. При оценке eval-кода this obj. Однако в eval-коде myFun не вызывается для объекта, поэтому ThisBinding установлен в window для вызова.

    function myFun() {
        return this; // What is 'this' here?
    }
    var obj = {
        myMethod: function () {
            eval("myFun()");
        }
    };
    
  5. Какова ценность this в отмеченной линии? Зачем?

    obj

    Строка myFun.call(obj); вызывает специальную встроенную функцию Function.prototype.call(), которая принимает thisArg в качестве первого аргумента.

    function myFun() {
        return this; // What is 'this' here?
    }
    var obj = {
        someData: "a string"
    };
    console.log("this is window:", myFun.call(obj) == window);
    console.log("this is obj:", myFun.call(obj) == obj);
      
  • 0
    что означают эти знаки? §10.4.1.1
  • 6
    @ Али: Это ссылки на разделы в редакции 5.1 стандарта ECMAScript, ECMA-262 . Я предоставляю их, чтобы вы могли прочитать Стандарт для технических деталей, если хотите.
Показать ещё 4 комментария
130

Ключевое слово this ведет себя по-разному в JavaScript по сравнению с другим языком. В объектно-ориентированных языках ключевое слово this относится к текущему экземпляру класса. В JavaScript значение this определяется главным образом контекстом вызова функции (context.function()) и где он вызывается.

1. При использовании в глобальном контексте

Когда вы используете this в глобальном контексте, он привязан к глобальному объекту (window в браузере)

document.write(this);  //[object Window]

Когда вы используете this внутри функции, определенной в глобальном контексте, this по-прежнему привязан к глобальному объекту, так как функция фактически является методом глобального контекста.

function f1()
{
   return this;
}
document.write(f1());  //[object Window]

Выше f1 делается метод глобального объекта. Таким образом, мы можем также называть его объектом window следующим образом:

function f()
{
    return this;
}

document.write(window.f()); //[object Window]

2. При использовании внутри метода объекта

Когда вы используете ключевое слово this внутри метода объекта, this привязан к "непосредственному" закрывающему объекту.

var obj = {
    name: "obj",
    f: function () {
        return this + ":" + this.name;
    }
};
document.write(obj.f());  //[object Object]:obj

Выше я положил слово в двойные кавычки. Следует сказать, что если вы вставляете объект в другой объект, то this привязан к непосредственному родительскому объекту.

var obj = {
    name: "obj1",
    nestedobj: {
        name:"nestedobj",
        f: function () {
            return this + ":" + this.name;
        }
    }            
}

document.write(obj.nestedobj.f()); //[object Object]:nestedobj

Даже если вы явно добавляете функцию к объекту в качестве метода, он по-прежнему следует выше правил, то есть this все еще указывает на непосредственный родительский объект.

var obj1 = {
    name: "obj1",
}

function returnName() {
    return this + ":" + this.name;
}

obj1.f = returnName; //add method to object
document.write(obj1.f()); //[object Object]:obj1

3. При вызове функции без контекста

Когда вы используете внутреннюю функцию this, которая вызывается без какого-либо контекста (т.е. не на каком-либо объекте), она привязана к глобальному объекту (window в браузере) (даже если функция определена внутри объекта).

var context = "global";

var obj = {  
    context: "object",
    method: function () {                  
        function f() {
            var context = "function";
            return this + ":" +this.context; 
        };
        return f(); //invoked without context
    }
};

document.write(obj.method()); //[object Window]:global 

Пробовал все с функциями

Мы также можем попробовать выше точки с функциями. Однако есть некоторые отличия.

  • Выше мы добавили элементы к объектам с использованием объектной нотации. Мы можем добавлять элементы к функциям с помощью this. для их указания.
  • Обозначение литерала объекта создает экземпляр объекта, который мы можем использовать немедленно. С помощью функции нам может понадобиться сначала создать свой экземпляр с помощью оператора new.
  • Также в объектном литеральном подходе мы можем явно добавлять членов к уже определенному объекту с помощью точечного оператора. Это добавляется только к конкретному экземпляру. Однако я добавил переменную в прототип функции, чтобы она отражалась во всех экземплярах функции.

Ниже я опробовал все, что мы делали с Object и this выше, но сначала создав функцию вместо прямой записи объекта.

/********************************************************************* 
  1. When you add variable to the function using this keyword, it 
     gets added to the function prototype, thus allowing all function 
     instances to have their own copy of the variables added.
*********************************************************************/
function functionDef()
{
    this.name = "ObjDefinition";
    this.getName = function(){                
        return this+":"+this.name;
    }
}        

obj1 = new functionDef();
document.write(obj1.getName() + "<br />"); //[object Object]:ObjDefinition   

/********************************************************************* 
   2. Members explicitly added to the function protorype also behave 
      as above: all function instances have their own copy of the 
      variable added.
*********************************************************************/
functionDef.prototype.version = 1;
functionDef.prototype.getVersion = function(){
    return "v"+this.version; //see how this.version refers to the
                             //version variable added through 
                             //prototype
}
document.write(obj1.getVersion() + "<br />"); //v1

/********************************************************************* 
   3. Illustrating that the function variables added by both above 
      ways have their own copies across function instances
*********************************************************************/
functionDef.prototype.incrementVersion = function(){
    this.version = this.version + 1;
}
var obj2 = new functionDef();
document.write(obj2.getVersion() + "<br />"); //v1

obj2.incrementVersion();      //incrementing version in obj2
                              //does not affect obj1 version

document.write(obj2.getVersion() + "<br />"); //v2
document.write(obj1.getVersion() + "<br />"); //v1

/********************************************************************* 
   4. `this` keyword refers to the immediate parent object. If you 
       nest the object through function prototype, then `this` inside 
       object refers to the nested object not the function instance
*********************************************************************/
functionDef.prototype.nestedObj = { name: 'nestedObj', 
                                    getName1 : function(){
                                        return this+":"+this.name;
                                    }                            
                                  };

document.write(obj2.nestedObj.getName1() + "<br />"); //[object Object]:nestedObj

/********************************************************************* 
   5. If the method is on an object prototype chain, `this` refers 
      to the object the method was called on, as if the method was on 
      the object.
*********************************************************************/
var ProtoObj = { fun: function () { return this.a } };
var obj3 = Object.create(ProtoObj); //creating an object setting ProtoObj
                                    //as its prototype
obj3.a = 999;                       //adding instance member to obj3
document.write(obj3.fun()+"<br />");//999
                                    //calling obj3.fun() makes 
                                    //ProtoObj.fun() to access obj3.a as 
                                    //if fun() is defined on obj3

4. При использовании внутри конструкторской функции.

Когда функция используется как конструктор (то есть, когда она вызывается с ключевым словом new), тело this внутри тела функции указывает на новый объект, который создается.

var myname = "global context";
function SimpleFun()
{
    this.myname = "simple function";
}

var obj1 = new SimpleFun(); //adds myname to obj1
//1. `new` causes `this` inside the SimpleFun() to point to the
//   object being constructed thus adding any member
//   created inside SimipleFun() using this.membername to the
//   object being constructed
//2. And by default `new` makes function to return newly 
//   constructed object if no explicit return value is specified

document.write(obj1.myname); //simple function

5. При использовании внутренней функции, определенной в цепочке прототипов

Если метод находится в цепочке прототипов объекта, this внутри такого метода ссылается на объект, на который был вызван метод, как если бы метод был определен на объекте.

var ProtoObj = {
    fun: function () {
        return this.a;
    }
};
//Object.create() creates object with ProtoObj as its
//prototype and assigns it to obj3, thus making fun() 
//to be the method on its prototype chain

var obj3 = Object.create(ProtoObj);
obj3.a = 999;
document.write(obj3.fun()); //999

//Notice that fun() is defined on obj3 prototype but 
//`this.a` inside fun() retrieves obj3.a   

6. Внутри вызовов(), apply() и bind() функции

  • Все эти методы определены на Function.prototype.
  • Эти методы позволяют написать функцию один раз и вызвать ее в другом контексте. Другими словами, они позволяют указать значение this, которое будет использоваться во время выполнения функции. Они также принимают любые параметры, которые должны быть переданы исходной функции при ее вызове.
  • fun.apply(obj1 [, argsArray]) Устанавливает obj1 как значение this внутри fun() и вызывает fun() передачу элементов argsArray в качестве своих аргументов.
  • fun.call(obj1 [, arg1 [, arg2 [,arg3 [, ...]]]]) - устанавливает obj1 как значение this внутри fun() и вызывает fun() передачу arg1, arg2, arg3, ... в качестве своих аргументов.
  • fun.bind(obj1 [, arg1 [, arg2 [,arg3 [, ...]]]]). Возвращает ссылку на функцию fun с this внутри fun, привязанную к obj1, а параметры fun связаны с указанными параметрами arg1, arg2, arg3,....
  • Теперь разница между apply, call и bind должна стать очевидной. apply позволяет указывать аргументы для функции как объект, подобный массиву, то есть объект с числовым свойством length и соответствующими неотрицательными целыми свойствами. В то время как call позволяет напрямую указывать аргументы функции. Оба apply и call немедленно вызывают функцию в указанном контексте и с указанными аргументами. С другой стороны, bind просто возвращает функцию, связанную с указанным значением this и аргументами. Мы можем записать ссылку на эту возвращенную функцию, присвоив ее переменной, и позже мы можем ее вызвать в любое время.
function add(inc1, inc2)
{
    return this.a + inc1 + inc2;
}

var o = { a : 4 };
document.write(add.call(o, 5, 6)+"<br />"); //15
      //above add.call(o,5,6) sets `this` inside
      //add() to `o` and calls add() resulting:
      // this.a + inc1 + inc2 = 
      // `o.a` i.e. 4 + 5 + 6 = 15
document.write(add.apply(o, [5, 6]) + "<br />"); //15
      // `o.a` i.e. 4 + 5 + 6 = 15

var g = add.bind(o, 5, 6);       //g: `o.a` i.e. 4 + 5 + 6
document.write(g()+"<br />");    //15

var h = add.bind(o, 5);          //h: `o.a` i.e. 4 + 5 + ?
document.write(h(6) + "<br />"); //15
      // 4 + 5 + 6 = 15
document.write(h() + "<br />");  //NaN
      //no parameter is passed to h()
      //thus inc2 inside add() is `undefined`
      //4 + 5 + undefined = NaN</code>

7. this внутри обработчиков событий

  • Когда вы назначаете функцию непосредственно обработчикам событий элемента, использование this непосредственно внутри функции обработки событий относится к соответствующему элементу. Такое прямое назначение функций может быть выполнено с использованием метода addeventListener или с помощью традиционных методов регистрации событий, таких как onclick.
  • Аналогично, когда вы используете this непосредственно внутри свойства события (например, <button onclick="...this..." >) элемента, он ссылается на элемент.
  • Однако использование this косвенно через другую функцию, вызываемую внутри функции обработки событий или свойства события, разрешается глобальному объекту window.
  • Такое же поведение выше, когда мы присоединяем функцию к обработчику событий с использованием метода модели регистрации событий Microsoft attachEvent. Вместо того, чтобы назначать функцию обработчику события (и тем самым создавать функциональный метод элемента), он вызывает функцию в событии (фактически вызывая его в глобальном контексте).

Я рекомендую лучше попробовать это в JSFiddle.

<script> 
    function clickedMe() {
       alert(this + " : " + this.tagName + " : " + this.id);
    } 
    document.getElementById("button1").addEventListener("click", clickedMe, false);
    document.getElementById("button2").onclick = clickedMe;
    document.getElementById("button5").attachEvent('onclick', clickedMe);   
</script>

<h3>Using `this` "directly" inside event handler or event property</h3>
<button id="button1">click() "assigned" using addEventListner() </button><br />
<button id="button2">click() "assigned" using click() </button><br />
<button id="button3" onclick="alert(this+ ' : ' + this.tagName + ' : ' + this.id);">used `this` directly in click event property</button>

<h3>Using `this` "indirectly" inside event handler or event property</h3>
<button onclick="alert((function(){return this + ' : ' + this.tagName + ' : ' + this.id;})());">`this` used indirectly, inside function <br /> defined & called inside event property</button><br />

<button id="button4" onclick="clickedMe()">`this` used indirectly, inside function <br /> called inside event property</button> <br />

IE only: <button id="button5">click() "attached" using attachEvent() </button>
<script> 
    function clickedMe() {
       alert(this + " : " + this.tagName + " : " + this.id);
    } 
    document.getElementById("button1").addEventListener("click", clickedMe, false);
    document.getElementById("button2").onclick = clickedMe;
    document.getElementById("button5").attachEvent('onclick', clickedMe);   
</script>

<h3>Using `this` "directly" inside event handler or event property</h3>
<button id="button1">click() "assigned" using addEventListner() </button><br />
<button id="button2">click() "assigned" using click() </button><br />
<button id="button3" onclick="alert(this+ ' : ' + this.tagName + ' : ' + this.id);">used `this` directly in click event property</button>

<h3>Using `this` "indirectly" inside event handler or event property</h3>
<button onclick="alert((function(){return this + ' : ' + this.tagName + ' : ' + this.id;})());">`this` used indirectly, inside function <br /> defined & called inside event property</button><br />

<button id="button4" onclick="clickedMe()">`this` used indirectly, inside function <br /> called inside event property</button> <br />

IE only: <button id="button5">click() "attached" using attachEvent() </button>
  • 0
    «Когда вы используете это внутри функции, определенной в глобальном контексте, она все еще привязана к глобальному объекту, поскольку функция фактически сделана методом глобального контекста». это неверно. это определяется тем, как функция вызывается или bind , а не тем, где она определена. Вызов любой функции без базовой ссылки ( «контекст») будет по умолчанию это глобальный объект или остаются неопределенными в строгом режиме.
  • 0
    @RobG хм, может быть, но я нашел это на MDN : в этом случае значение this не устанавливается вызовом. Так как код не находится в строгом режиме, значение this всегда должно быть объектом, поэтому по умолчанию используется глобальный объект. И на самом деле именно поэтому я подумал, что мы можем напрямую вызвать window.f1() , так что это означает, что f1() уже присоединена к объекту window , я имею в виду перед вызовом. Я правильно понял?
Показать ещё 7 комментариев
51

Javascript this

Простой вызов функции

Рассмотрим следующую функцию:

function foo() {
    console.log("bar");
    console.log(this);
}
foo(); // calling the function

Обратите внимание, что мы запускаем это в обычном режиме, т.е. строгий режим не используется.

При запуске в браузере значение this будет регистрироваться как window. Это связано с тем, что window является глобальной переменной в области веб-браузера.

Если вы запустите этот же фрагмент кода в среде, такой как node.js, this будет ссылаться на глобальную переменную в вашем приложении.

Теперь, если мы запустим это в строгом режиме, добавив оператор "use strict"; в начало объявления функции, this больше не будет ссылаться на глобальную переменную в любом из envirnoments. Это делается для того, чтобы избежать путаницы в строгом режиме. this, в этом случае просто log undefined, потому что это то, что он есть, он не определен.

В следующих случаях мы увидим, как манипулировать значением this.

Вызов функции для объекта

Существуют разные способы сделать это. Если вы вызывали собственные методы в Javascript, например forEach и slice, вы уже должны знать, что переменная this в этом случае относится к Object, на которую вы вызывали эту функцию (обратите внимание, что в javascript, примерно все это Object, включая Array и Function s). Возьмите следующий код, например.

var myObj = {key: "Obj"};
myObj.logThis = function () {
    // I am a method
    console.log(this);
}
myObj.logThis(); // myObj is logged

Если a Object содержит свойство, которое содержит a Function, свойство называется методом. Этот метод при вызове всегда будет иметь переменную this, установленную в Object, с которой она связана. Это верно как для строгих, так и для нестрогих режимов.

Обратите внимание, что если метод хранится (или, скорее, копируется) в другой переменной, ссылка на this больше не сохраняется в новой переменной. Например:

// continuing with the previous code snippet

var myVar = myObj.thisMethod;
myVar();
// logs either of window/global/undefined based on mode of operation

Учитывая более практичный сценарий:

var el = document.getElementById('idOfEl');
el.addEventListener('click', function() { console.log(this) });
// the function called by addEventListener contains this as the reference to the element
// so clicking on our element would log that element itself

Ключевое слово new

Рассмотрим функцию-конструктор в Javascript:

function Person (name) {
    this.name = name;
    this.sayHello = function () {
        console.log ("Hello", this);
    }
}

var awal = new Person("Awal");
awal.sayHello();
// In `awal.sayHello`, `this` contains the reference to the variable `awal`

Как это работает? Хорошо, посмотрим, что произойдет, когда мы используем ключевое слово new.

  • Вызов функции с ключевым словом new немедленно инициализирует Object типа Person.
  • Конструктор этого Object имеет свой конструктор, установленный в Person. Также обратите внимание, что typeof awal будет возвращать только Object.
  • Этому новому Object будет присвоен protoype Person.prototype. Это означает, что любой метод или свойство прототипа Person будет доступен для всех экземпляров Person, включая awal.
  • Теперь сама функция Person вызывается; this является ссылкой на вновь построенный объект awal.

Pretty straighforward, eh?

Обратите внимание, что в официальной спецификации ECMAScript нет где указано, что такие типы функций являются действительными constructor функциями. Они являются просто нормальными функциями, а new может использоваться для любой функции. Просто мы используем их как таковые, поэтому мы называем их только такими.

Функции вызова для функций: call и apply

Так что да, поскольку Function также Objects (и, фактически, переменные первого класса в Javascript), даже функции имеют методы, которые... ну, выполняют свои функции.

Все функции наследуются от глобального Function, а два из его многочисленных методов - call и apply, и оба могут использоваться для управления значением this в функции, на которую они вызывают.

function foo () { console.log (this, arguments); }
var thisArg = {myObj: "is cool"};
foo.call(thisArg, 1, 2, 3);

Это типичный пример использования call. Он в основном принимает первый параметр и устанавливает this в функции foo как ссылку на thisArg. Все остальные параметры, переданные в call, передаются функции foo в качестве аргументов.
Таким образом, приведенный выше код будет записывать {myObj: "is cool"}, [1, 2, 3] в консоли. Довольно красивый способ изменить значение this в любой функции.

apply почти такой же, как call, принимает, что он принимает только два параметра: thisArg и массив, содержащий аргументы, которые должны быть переданы функции. Таким образом, вышеуказанный вызов call можно перевести на apply следующим образом:

foo.apply(thisArg, [1,2,3])

Обратите внимание, что call и apply могут переопределять значение this, установленное вызовом метода dot, которое мы обсуждали во второй пуле. Достаточно просто:)

Представление.... bind!

bind является братом call и apply. Это также метод, унаследованный всеми функциями из глобального конструктора Function в Javascript. Разница между bind и call/apply заключается в том, что как call, так и apply будут фактически вызывать функцию. bind, с другой стороны, возвращает новую функцию с предварительно установленными параметрами thisArg и arguments. Возьмем пример, чтобы лучше понять это:

function foo (a, b) {
    console.log (this, arguments);
}
var thisArg = {myObj: "even more cool now"};
var bound = foo.bind(thisArg, 1, 2);
console.log (typeof bound); // logs `function`
console.log (bound);
/* logs `function () { native code }` */

bound(); // calling the function returned by `.bind`
// logs `{myObj: "even more cool now"}, [1, 2]`

Посмотрите разницу между тремя? Это тонкий, но они используются по-разному. Подобно call и apply, bind также превысит значение this, установленное вызовом метода-точки.

Также обратите внимание, что ни одна из этих трех функций не выполняет никаких изменений в исходной функции. call и apply вернет значение из только что построенных функций, а bind вернет только что созданную функцию, готовую к вызову.

Дополнительные материалы, скопируйте это

Иногда вам не нравится тот факт, что this изменяется с областью, особенно вложенной областью. Взгляните на следующий пример.

var myObj = {
    hello: function () {
        return "world"
        },
    myMethod: function () {
        // copy this, variable names are case-sensitive
        var that = this;
        // callbacks ftw \o/
        foo.bar("args", function () {
            // I want to call `hello` here
            this.hello(); // error
            // but `this` references to `foo` damn!
            // oh wait we have a backup \o/
            that.hello(); // "world"
        });
    }
  };

В приведенном выше коде мы видим, что значение this изменилось с вложенной областью, но мы хотели получить значение this из исходной области. Итак, мы скопировали 'this в that и использовали копию вместо this. Умный, а??

Индекс

  • Что хранится в this по умолчанию?
  • Что делать, если мы вызываем функцию как метод с нотографией Object-dot?
  • Что делать, если мы используем ключевое слово new?
  • Как нам манипулировать this с помощью call и apply?
  • Использование bind.
  • Копирование this для решения проблем с вложенными областями.
  • 1
    Почему есть раздел о new ключевом слове? Также этот вопрос был забит до смерти, но у всех нас есть «то время, когда мы пытались оседлать вопрос, на который ответили 4 года назад»
42

"this" - все о сфере видимости. Каждая функция имеет свой собственный объем, и поскольку все в JS является объектом, даже функция может хранить некоторые значения в себе, используя "this". OOP 101 учит, что "this" применим только к экземплярам объекта. Поэтому каждый раз, когда выполняется функция, новый "экземпляр" этой функции имеет новое значение "this".

Большинство людей путают, когда они пытаются использовать "this" внутри анонимных функций закрытия, например:

(function(value) {
    this.value = value;
    $('.some-elements').each(function(elt){
        elt.innerHTML = this.value;        // uh oh!! possibly undefined
    });
})(2);

Итак, внутри each(), "this" не удерживает "значение", которое вы ожидаете от него (от

this.value = value;
над ним). Итак, чтобы справиться с этой проблемой (без каламбуры), разработчик мог:
(function(value) {
    var self = this;            // small change
    self.value = value;
    $('.some-elements').each(function(elt){
        elt.innerHTML = self.value;        // phew!! == 2 
    });
})(2);

Попробуйте; вам понравится эта схема программирования

  • 6
    «все в JS является объектом» не соответствует действительности, JavaScript также имеет примитивные значения, см. bclary.com/2004/11/07/#a-4.3.2
  • 6
    Кажется, что примитивные значения имеют некоторые методы, такие как String # substring (), Number # toString () и т. Д. Итак, возможно, не с той же номенклатурой, что и у этой статьи, они действительно ведут себя так, как если бы они были объектами (они все прототипы, т.е. String # substring (), действительно: String.prototype.substring = function () {...}). Пожалуйста, поправьте меня, если я ошибаюсь.
Показать ещё 4 комментария
15

Поскольку этот поток натолкнулся, я собрал несколько точек для новых читателей для темы this.

Как определяется значение this?

Мы используем это подобно тому, как мы используем местоимения в естественных языках, таких как английский: "Джон работает быстро, потому что он пытается поймать поезд". Вместо этого мы могли бы написать "... John пытается поймать поезд".

var person = {    
    firstName: "Penelope",
    lastName: "Barrymore",
    fullName: function () {

    // We use "this" just as in the sentence above:
       console.log(this.firstName + " " + this.lastName);

    // We could have also written:
       console.log(person.firstName + " " + person.lastName);
    }
}

this не присваивается значение, пока объект не вызовет функцию, в которой он определен. В глобальной области действия все глобальные переменные и функции определены в объекте window. Поэтому this в глобальной функции ссылается на (и имеет значение) глобальный объект window.

Когда use strict, this в глобальном и в анонимных функциях, не связанных с каким-либо объектом, имеет значение undefined.

Ключевое слово this наиболее неправильно понято, когда: 1) мы используем метод, который использует this, 2), мы назначаем метод который использует this для переменной, 3) функция, которая использует this, передается как функция обратного вызова, а 4) this используется внутри замыкания - внутренней функции. (2)

Изображение 2171

Что содержит будущее

Определено в ECMA Script 6, функции-стрелки принимают привязку this от (функция или глобальная).

function foo() {
     // return an arrow function
     return (a) => {
     // `this` here is lexically inherited from `foo()`
     console.log(this.a);
  };
}
var obj1 = { a: 2 };
var obj2 = { a: 3 };

var bar = foo.call(obj1);
bar.call( obj2 ); // 2, not 3!

Хотя функции-стрелки предоставляют альтернативу использованию bind(), важно отметить, что они по существу отключают традиционный механизм this в пользу более широкого понимания лексического охвата. (1)


Литература:

15

this в Javascript всегда ссылается на "владельца" функции , выполняемой.

Если явный владелец не определен, то ссылается на самый верхний владелец - объект окна.

Итак, если я сделал

function someKindOfFunction() {
   this.style = 'foo';
}

element.onclick = someKindOfFunction;

this будет ссылаться на объект элемента. Но будьте осторожны, многие люди делают эту ошибку

<element onclick="someKindOfFunction()">

В последнем случае вы просто ссылаетесь на функцию, а не передаете ее элементу. Поэтому this будет ссылаться на объект окна.

11

Каждый контекст выполнения в javascript имеет область контекст, который задается:

  • Как вызывается функция (в том числе как метод объекта, использование вызова и применения, использование нового)
  • Использование привязки
  • Лексически для функций стрелок (они принимают это из контекста внешнего исполнения)

Каким бы ни был этот контекст области видимости, ссылается на "this".

Вы можете изменить, что установить значение этой области , используя func.call, func.apply или func.bind.

По умолчанию, и что путает большинство новичков, когда вызывающий callback вызывается после того, как событие добавлено в элемент DOM, контекст области это значение функции является элементом DOM.

jQuery делает это тривиальным для изменения с помощью jQuery.proxy.

  • 9
    Правильнее будет сказать, что каждый вызов функции имеет область видимости. Другими словами, что сбивает с толку this в Javascript, так это то, что это не внутреннее свойство самой функции, а скорее артефакт того, как эта функция вызывается.
  • 0
    @ Спасибо, спасибо. то, что вызывает наибольшую путаницу в js, это тот факт, что во всех языках, используемых ранее (c #, c ++), - этим нельзя манипулировать n всегда указывает на экземпляр объекта, тогда как в js это зависит и может быть изменено при вызове функции, использующие func.call , func.bind и т. д. - Sushil
Показать ещё 5 комментариев
9

Здесь является одним из хороших источников this в JavaScript.

Вот резюме:

  • global this

    В браузере в глобальной области this находится объект window

    <script type="text/javascript">
      console.log(this === window); // true
      var foo = "bar";
      console.log(this.foo); // "bar"
      console.log(window.foo); // "bar"
    

    В node с использованием repl, this - это верхнее пространство имен. Вы можете ссылаться на него как global.

    >this
      { ArrayBuffer: [Function: ArrayBuffer],
        Int8Array: { [Function: Int8Array] BYTES_PER_ELEMENT: 1 },
        Uint8Array: { [Function: Uint8Array] BYTES_PER_ELEMENT: 1 },
        ...
    >global === this
     true
    

    В node, выполняющемся из script, this в глобальной области запускается как пустой объект. Это не то же самое, что global

    \\test.js
    console.log(this);  \\ {}
    console.log(this === global); \\ fasle
    
  • функция this

За исключением случаев обработчиков событий DOM или когда предоставляется thisArg (см. ниже), как в node, так и в браузере с использованием this в функции, которая не вызывается с помощью new ссылок глобальный охват...

<script type="text/javascript">
    foo = "bar";

    function testThis() {
      this.foo = "foo";
    }

    console.log(this.foo); //logs "bar"
    testThis();
    console.log(this.foo); //logs "foo"
</script>

Если вы используете use strict;, в этом случае this будет undefined

<script type="text/javascript">
    foo = "bar";

    function testThis() {
      "use strict";
      this.foo = "foo";
    }

    console.log(this.foo); //logs "bar"
    testThis();  //Uncaught TypeError: Cannot set property 'foo' of undefined 
</script>

Если вы вызываете функцию с new, this будет новым контекстом, он не будет ссылаться на глобальный this.

<script type="text/javascript">
    foo = "bar";

    function testThis() {
      this.foo = "foo";
    }

    console.log(this.foo); //logs "bar"
    new testThis();
    console.log(this.foo); //logs "bar"

    console.log(new testThis().foo); //logs "foo"
</script>
  • прототип

Функции, которые вы создаете, становятся объектами функции. Они автоматически получают специальное свойство prototype, которое вы можете присвоить значениям. Когда вы создаете экземпляр, вызывая свою функцию с помощью new, вы получаете доступ к значениям, присвоенным свойству prototype. Вы получаете доступ к этим значениям с помощью this.

function Thing() {
  console.log(this.foo);
}

Thing.prototype.foo = "bar";

var thing = new Thing(); //logs "bar"
console.log(thing.foo);  //logs "bar"

Как правило, ошибочно назначать массивы или объекты на prototype. Если вы хотите, чтобы экземпляры имели свои собственные массивы, создайте их в функции, а не прототипе.

function Thing() {
    this.things = [];
}

var thing1 = new Thing();
var thing2 = new Thing();
thing1.things.push("foo");
console.log(thing1.things); //logs ["foo"]
console.log(thing2.things); //logs []
  • объект this

Вы можете использовать this в любой функции объекта, чтобы ссылаться на другие свойства этого объекта. Это не то же самое, что экземпляр, созданный с помощью new.

var obj = {
    foo: "bar",
    logFoo: function () {
        console.log(this.foo);
    }
};

obj.logFoo(); //logs "bar"
  • DOM event this

В обработчике событий HTML DOM this всегда ссылается на элемент DOM, событие было прикреплено к

function Listener() {
    document.getElementById("foo").addEventListener("click",
       this.handleClick);
}
Listener.prototype.handleClick = function (event) {
    console.log(this); //logs "<div id="foo"></div>"
}

var listener = new Listener();
document.getElementById("foo").click();

Если вы не bind контекст

function Listener() {
    document.getElementById("foo").addEventListener("click", 
        this.handleClick.bind(this));
}
Listener.prototype.handleClick = function (event) {
    console.log(this); //logs Listener {handleClick: function}
}

var listener = new Listener();
document.getElementById("foo").click();
  • HTML

Внутри атрибутов HTML, в которые вы можете поместить JavaScript, this является ссылкой на элемент.

<div id="foo" onclick="console.log(this);"></div>
<script type="text/javascript">
document.getElementById("foo").click(); //logs <div id="foo"...
</script>
  • eval this

Вы можете использовать eval для доступа к this.

function Thing () {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
    eval("console.log(this.foo)"); //logs "bar"
}

var thing = new Thing();
thing.logFoo();
  • с этим

Вы можете использовать with, чтобы добавить this в текущую область действия, чтобы читать и записывать значения на this без прямого указания this.

function Thing () {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
    with (this) {
        console.log(foo);
        foo = "foo";
    }
}

var thing = new Thing();
thing.logFoo(); // logs "bar"
console.log(thing.foo); // logs "foo"
  • jQuery this

jQuery во многих местах имеет this ссылается на элемент DOM.

<div class="foo bar1"></div>
<div class="foo bar2"></div>
<script type="text/javascript">
$(".foo").each(function () {
    console.log(this); //logs <div class="foo...
});
$(".foo").on("click", function () {
    console.log(this); //logs <div class="foo...
});
$(".foo").each(function () {
    this.click();
});
</script>
8

Существует много путаницы в отношении того, как "this" ключевое слово интерпретируется в JavaScript. Надеюсь, эта статья заложит все, чтобы отдохнуть раз и навсегда. И многое другое. Пожалуйста, внимательно прочитайте всю статью. Будьте предупреждены, что эта статья длинная.

Независимо от контекста, в котором он используется, "this" всегда ссылается на текущий "текущий объект в Javascript. Однако то, что " текущий объект" отличается в соответствии с контекстом. Контекст может быть в точности 1 из 6 следующим образом:

  • Глобальный (т.е. вне всех функций)
  • Внутри прямой "Non Bound Function" Call (т.е. функция с не была связана вызовом functionName.bind)
  • Внутри косвенной "Не привязанной функции" Вызов через functionName.call и functionName.apply
  • Внутри функции "Связанная функция" (т.е. функция была связана, вызывая functionName.bind)
  • Пока создание объекта через "новый"
  • Внутренний обработчик событий Inline DOM

Ниже описывается каждый из этих контекстов один за другим:

  • Глобальный контекст (т.е. вне всех функций):

    Вне всех функций (т.е. в глобальном контексте) "current объект (и, следовательно, значение "this") всегда "окно" для браузеров.

  • Внутри прямой "Не привязанная функция" Вызов:

    Внутри прямой "Non Bound Function" Call, объект, который вызываемый вызов функции становится "текущим объектом" (и, следовательно, значение "this"). Если функция вызывается без явного текущего объекта, текущий объект является либо "окном (для не строгого режима), либо undefined (для строгого режима). Любая функция (или переменная), определенная в Глобальный контекст автоматически становится свойством объекта "окна. Например, предположим, что функция определена в глобальном контексте как

    function UserDefinedFunction(){
        alert(this)
        }
    

    он становится свойством объекта window, как если бы вы определили это как

    window.UserDefinedFunction=function(){
      alert(this)
    }  
    

    В режиме "Не строгого режима" вызов/вызов этой функции непосредственно через "UserDefinedFunction()" будет автоматически вызывать/вызывать он как "window.UserDefinedFunction()" делает "окно" как "текущий объект" (и, следовательно, значение "this") в пределах "UserDefinedFunction" . Включение этой функции в "Non Strict Mode" будет приведет к следующему

    UserDefinedFunction() // displays [object Window]  as it automatically gets invoked as window.UserDefinedFunction()
    

    В "Строгий режим", вызов/вызов функции непосредственно через "UserDefinedFunction()" будет "NOT" автоматически вызывать/вызывать его как "window.UserDefinedFunction()" .Сохранить текущий объект " (и значение "this") в пределах "UserDefinedFunction" должен быть undefined. Вызов этой функции в "Строгий режим" приведет к следующему

    UserDefinedFunction() // displays undefined
    

    Однако, ссылаясь на явное использование окна-объекта, следующие

    window.UserDefinedFunction() // "always displays [object Window]   irrespective of mode."
    

    Посмотрим на другой пример. Посмотрите следующий код

     function UserDefinedFunction()
        {
            alert(this.a + ","  + this.b + ","  + this.c  + ","  + this.d)
        }
    
    var o1={
                a:1,
                b:2,
                f:UserDefinedFunction
          }
    var o2={
                c:3,
                d:4,
                f:UserDefinedFunction
           }
    
    o1.f() // Shall display 1,2,undefined,undefined
    o2.f() // Shall display undefined,undefined,3,4
    

    В приведенном выше примере мы видим, что когда "UserDefinedFunction" вызывается с помощью o1, "this" принимает значение o1, а значение его свойств "a" и "b" . Значение "c" и "d" были показаны как undefined как o1. не определять эти свойства

    Аналогично, когда "UserDefinedFunction" вызывается через o2 "this" принимает значение o2, а значение его свойств "c" и "d" отображается Значение "a" и "b" было показано как undefined, поскольку o2 не определяет эти свойства.

  • Внутри косвенной "Не привязанной функции" Вызов через functionName.callи functionName.apply:

    Когда "Non Bound Function" вызывается через functionName.call или functionName.apply, "текущий объект (и, следовательно, значение "this") устанавливается на значение "this" (первый параметр) передается call/apply. Следующий код демонстрирует то же самое.

    function UserDefinedFunction()
    {
        alert(this.a + ","  + this.b + ","  + this.c  + ","  + this.d)
    }
    var o1={
                a:1,
                b:2,
                f:UserDefinedFunction
           }
    var o2={
                c:3,
                d:4,
                f:UserDefinedFunction
           }
    
    UserDefinedFunction.call(o1) // Shall display 1,2,undefined,undefined
    UserDefinedFunction.apply(o1) // Shall display 1,2,undefined,undefined
    
    UserDefinedFunction.call(o2) // Shall display undefined,undefined,3,4
    UserDefinedFunction.apply(o2) // Shall display undefined,undefined,3,4
    
    o1.f.call(o2) // Shall display undefined,undefined,3,4
    o1.f.apply(o2) // Shall display undefined,undefined,3,4
    
    o2.f.call(o1) // Shall display 1,2,undefined,undefined
    o2.f.apply(o1) // Shall display 1,2,undefined,undefined
    

    В приведенном выше коде ясно, что значение "this" для любого "NON Связанная функция "может быть изменена с помощью вызова/применения. Кроме того, если "this" не передается явно в вызов/применение, "текущий объект (и, следовательно, значение "this") установлен в "окно" в нестрочном режиме и "undefined" в строгом режиме.

  • Внутри функции "Связанная функция" (т.е. функция, связанная вызовом functionName.bind):

    Связанная функция - это функция, значение которой "this" исправлено. Следующий код показал, как работает "this" в случае связанной функции

    function UserDefinedFunction()
    {
        alert(this.a + ","  + this.b + ","  + this.c  + ","  + this.d)
    }
    var o1={
              a:1,
              b:2,
              f:UserDefinedFunction,
              bf:null
           }
    var o2={
               c:3,
               d:4,
               f:UserDefinedFunction,
               bf:null
            }
    
    var bound1=UserDefinedFunction.bind(o1); // permanantly fixes "this" value of function "bound1" to Object o1
    bound1() // Shall display 1,2,undefined,undefined
    
    var bound2=UserDefinedFunction.bind(o2); // permanantly fixes "this" value of function "bound2" to Object o2
    bound2() // Shall display undefined,undefined,3,4
    
    var bound3=o1.f.bind(o2); // permanantly fixes "this" value of function "bound3" to Object o2
    bound3() // Shall display undefined,undefined,3,4
    
    var bound4=o2.f.bind(o1); // permanantly fixes "this" value of function "bound4" to Object o1
    bound4() // Shall display 1,2,undefined,undefined
    
    o1.bf=UserDefinedFunction.bind(o2) // permanantly fixes "this" value of function "o1.bf" to Object o2
    o1.bf() // Shall display undefined,undefined,3,4
    
    o2.bf=UserDefinedFunction.bind(o1) // permanantly fixes "this" value of function "o2.bf" to Object o1
    o2.bf() // Shall display 1,2,undefined,undefined
    
    bound1.call(o2) // Shall still display 1,2,undefined,undefined. "call" cannot alter the value of "this" for bound function
    
    bound1.apply(o2) // Shall still display 1,2,undefined,undefined. "apply" cannot alter the value of "this" for bound function
    
    o2.bf.call(o2) // Shall still display 1,2,undefined,undefined. "call" cannot alter the value of "this" for bound function
    o2.bf.apply(o2) // Shall still display 1,2,undefined,undefined."apply" cannot alter the value of "this" for bound function
    

    Как указано в приведенном выше коде, значение "this" для любой "Связанной функции" НЕ МОЖЕТ быть изменен с помощью вызова/применения. Кроме того, если "this" параметр явно не передан для привязки, "текущий объект" (и, следовательно, значение "this") установлено в "окно" в Non строгий режим и "undefined" в строгом режиме. Еще кое-что. Привязка уже связанной функции не изменяет значение "this". Он остается установленным как значение, заданное первой функцией связывания.

  • Пока создание объекта через "новый" :

    Внутри функции-конструктора "текущий объект (и, следовательно, значение "this") ссылается на объект, который в настоящее время создается через "новый" , независимо от статуса привязки функции. Однако если конструктор является связанной функцией, он должен быть вызван с предопределенный набор аргументов, заданных для связанной функции.

  • Внутренний обработчик событий Inline DOM:

    Посмотрите на следующий фрагмент HTML

    <button onclick='this.style.color=white'>Hello World</button>
    <div style='width:100px;height:100px;' onclick='OnDivClick(event,this)'>Hello World</div>
    

    "this" в приведенных выше примерах относится к элементу "button", а "div" соответственно.

    В первом примере цвет шрифта кнопки должен быть установлен на белый при нажатии.

    Во втором примере, когда щелкнут элемент "div" , он должен вызовите функцию OnDivClick со своим вторым параметром ссылаясь на элемент с щелчком div. Однако значение "this" в OnDivClick НЕ ДОЛЖЕН ссылаться на нажатый div элемент. Он должен быть установлен как "оконный объект или "undefined" в Нестандартных и Строгих моделях соответственно (если OnDivClick является несвязанной функцией) или установите для предопределенного Связанное значение (если OnDivClick является связанной функцией)

Ниже приведена вся статья

  • В глобальном контексте "this" всегда ссылается на "окно

  • Всякий раз, когда вызывается функция, она вызывается в контексте object ("текущий объект). Если текущий объект явно не указан, текущий объект - это "оконный объект в NON Strict Mode и "undefined" в строгом режиме по умолчанию.

  • Значение "this" внутри функции Non Bound является ссылкой на объект, в контексте которого вызывается функция ("текущий объект" ).

  • Значение "this" внутри функции Non Bound может быть переопределено вызов и применить методы функции.

  • Значение "this" фиксировано для функции Bound и не может быть переопределить вызов и применить методы функции.

  • Связывание и уже связанная функция не изменяет значение "this". Он остается установленным как значение, заданное первой функцией связывания.

  • Значение "this" внутри конструктора - это объект, который создано и инициализировано

  • Значение "this" внутри встроенного обработчика событий DOM является ссылкой к элементу, для которого задан обработчик события.

8

Вероятно, самая подробная и всеобъемлющая статья по this вопросу:

Нежное объяснение ключевого слова "this" в JavaScript

Идея this заключается в том, чтобы понять, что типы вызова функций имеют важное значение для установки this значения.


Когда возникают проблемы с this, не спрашивайте себя:

Откуда this взято?

но спросите себя:

Как вызывается функция?

Для функции стрелки (специальный случай прозрачности контекста) спросите себя:

Какое значение имеет this значение, когда определена функция стрелки?

Это мышление правильное, когда вы имеете дело с this и избавит вас от головной боли.

8

Даниэль, удивительное объяснение! Несколько слов по этому и хорошему списку указателя контекста выполнения this в случае обработчиков событий.

В двух словах this в JavaScript указывает объект, у которого (или из контекста выполнения) текущая функция была запущена, и она всегда доступна только для чтения, вы все равно не можете ее установить (такая попытка закончится с сообщением "Недопустимая левая сторона в присваивании".

Для обработчиков событий: обработчики событий inline, такие как <element onclick="foo">, переопределяют любые другие обработчики, прикрепленные ранее и раньше, поэтому будьте осторожны и лучше избегайте делегирования событий внутри очереди. И благодаря Заре Алавердян, которая вдохновила меня на этот список примеров через несогласные дебаты:)

  • el.onclick = foo; // in the foo - obj
  • el.onclick = function () {this.style.color = '#fff';} // obj
  • el.onclick = function() {doSomething();} // In the doSomething - Window
  • el.addEventListener('click',foo,false) // in the foo - obj
  • el.attachEvent('onclick, function () { // this }') // window, all the compliance to IE :)
  • <button onclick="this.style.color = '#fff';"> // obj
  • <button onclick="foo"> // In the foo - window, but you can <button onclick="foo(this)">
6

Это лучшее объяснение, которое я видел. Понять JavaScripts с помощью Clarity

Эта ссылка ALLWAYS означает (и содержит значение) объект - особый объект - и обычно используется внутри функции или метода, хотя она может использоваться вне функции в глобальной области. Обратите внимание, что когда мы используем строгий режим, это значение не определено в глобальных функциях и в анонимных функциях, которые не привязаны ни к одному объекту.

Существуют четыре условия, в которых это может ввести в заблуждение:

  1. Когда мы передаем метод (который использует это) в качестве параметра, который будет использоваться в качестве функции обратного вызова.
  2. Другой пример, когда это неправильно понимается, - это когда мы используем внутренний метод (закрытие). Важно отметить, что закрытие не может получить доступ к внешним функциям этой переменной, используя это ключевое слово, потому что эта переменная доступна только самой функции, а не внутренними функциями.
  3. Используя это, когда метод присваивается переменной. Это значение привязано к другому объекту, если мы назначим метод, который использует это для переменной
  4. Используя это при использовании методов bind, apply и call.

Он дает примеры кода, объяснения и исправления кода, которые, как я думал, были очень полезными.

5

В псевдоклассических терминах многие лекции обучают ключевому слову 'this' как объект, созданный конструктором класса или объекта. Каждый раз, когда новый объект создается из класса, представьте, что под капотом создается и возвращается локальный экземпляр объекта 'this'. Я помню, как он учил так:

function Car(make, model, year) {
var this = {}; // under the hood, so to speak
this.make = make;
this.model = model;
this.year = year;
return this; // under the hood
}

var mycar = new Car('Eagle', 'Talon TSi', 1993);
// ========= under the hood
var this = {};
this.make = 'Eagle';
this.model = 'Talon TSi';
this.year = 1993;
return this;
5

Трудно получить хорошее представление о JS или написать больше, чем что-либо тривиальное в нем, если вы не понимаете его полностью. Вы не можете просто позволить себе быстро окунуться :) Я думаю, что лучший способ начать работу с JS - сначала посмотреть эти видео-лекции Дугласа Крокфорда - http://yuiblog.com/crockford/, который охватывает это и то, и все остальное о JS.

  • 1
    +1 Крокфорд должен быть первым шагом в пути любого в JS. Его главы в книгах (доступны бесплатно онлайн) очень быстро подняли меня на ноги. Он идет прямо к решающим битам.
  • 2
    Ха, и теперь Крокфорд не нравится и программа без его использования. ;-)
Показать ещё 1 комментарий
4

Значение "this" зависит от "контекста", в котором выполняется функция. Контекстом может быть любой объект или глобальный объект, т.е. Окно.

Таким образом, семантика "this" отличается от традиционных языков ООП. И это вызывает проблемы: 1. когда функция передается другой переменной (скорее всего, обратный вызов); и 2. когда замыкание вызывается из метода-члена класса.

В обоих случаях это устанавливается как окно.

4

this - одна из неправильно понятых концепций в JavaScript, потому что она ведет себя немного иначе, чем место. Просто this относится к "владельцу" функции, которую мы сейчас выполняем.

this помогает получить текущий объект (контекст выполнения a.k.a.), с которым мы работаем. Если вы понимаете, в каком объекте выполняется текущая функция, вы можете легко понять, что текущий this

var val = "window.val"

var obj = {
    val: "obj.val",
    innerMethod: function () {
        var val = "obj.val.inner",
            func = function () {
                var self = this;
                return self.val;
            };

        return func;
    },
    outerMethod: function(){
        return this.val;
    }
};

//This actually gets executed inside window object 
console.log(obj.innerMethod()()); //returns window.val

//Breakdown in to 2 lines explains this in detail
var _inn = obj.innerMethod();
console.log(_inn()); //returns window.val

console.log(obj.outerMethod()); //returns obj.val

Выше мы создаем 3 переменные с одинаковым именем "val". Один в глобальном контексте, один внутри obj и другой внутри innerMethod obj. JavaScript разрешает идентификаторы в определенном контексте, поднимая цепочку областей действия из локального глобального.


Несколько мест, где this можно дифференцировать

Вызов метода объекта

var status = 1;
var helper = {
    status : 2,
    getStatus: function () {
        return this.status;
    }
};

var theStatus1 = helper.getStatus(); //line1
console.log(theStatus1); //2

var theStatus2 = helper.getStatus;
console.log(theStatus2()); //1

Когда выполняется строка1, JavaScript устанавливает контекст выполнения (EC) для вызова функции, устанавливая this объекту , на который ссылается все, что было до последнего ".. поэтому в последней строке вы можете понять, что a() был выполнен в глобальном контексте, который является window.

С конструктором

this может использоваться для ссылки на создаваемый объект

function Person(name){
    this.personName = name;
    this.sayHello = function(){
        return "Hello " + this.personName;
    }
}

var person1 = new Person('Scott');
console.log(person1.sayHello()); //Hello Scott

var person2 = new Person('Hugh');
var sayHelloP2 = person2.sayHello;
console.log(sayHelloP2()); //Hello undefined

Когда выполняется новый Person(), создается совершенно новый объект. Person вызывается, а его this устанавливается для ссылки на этот новый объект.

Вызов функции

function testFunc() {
    this.name = "Name";
    this.myCustomAttribute = "Custom Attribute";
    return this;
}

var whatIsThis = testFunc();
console.log(whatIsThis); //window

var whatIsThis2 = new testFunc();
console.log(whatIsThis2);  //testFunc() / object

console.log(window.myCustomAttribute); //Custom Attribute 

Если мы пропустим ключевое слово new, whatIsThis ссылается на самый глобальный контекст, он может найти (window)

С обработчиками событий

Если обработчик события inline, this относится к глобальному объекту

<script type="application/javascript">
    function click_handler() {
        alert(this); // alerts the window object
    }
</script>

<button id='thebutton' onclick='click_handler()'>Click me!</button>

При добавлении обработчика событий через JavaScript this относится к элементу DOM, который сгенерировал событие.


2

Немного информации об этом ключевом слове

Позвольте записывать this ключевое слово в консоль в глобальной области без какого-либо кода, но

console.log(this)

В Client/Browser this ключевое слово - глобальный объект, который является window

console.log(this === window) // true

а также

В среде выполнения Server/Node/Javascript this ключевое слово также является глобальным объектом, который является module.exports

console.log(this === module.exports) // true
console.log(this === exports) // true

Имейте в виду, что exports - это просто ссылка на module.exports

2

Может ли this помочь? (Большая часть путаницы 'this' в javascript исходит из того факта, что она вообще не связана с вашим объектом, а с текущей исполняемой областью - это может быть не совсем так, как это работает, но мне всегда кажется, см. статью для полного объяснения)

  • 1
    Было бы лучше сказать, что это связано « с текущим контекстом выполнения ». За исключением ES6 (черновик), это изменяется с помощью функций стрелок, где это разрешается во внешнем контексте выполнения.
1

это использование для области видимости как это

  <script type="text/javascript" language="javascript">
$('#tbleName tbody tr').each(function{
var txt='';
txt += $(this).find("td").eq(0).text();
\\same as above but synatx different
var txt1='';
 txt1+=$('#tbleName tbody tr').eq(0).text();
alert(txt1)
});
</script>

значение txt1 и txt равно В приведенном выше примере $ (this) = $('# tbleName tbody tr') - это тот же

0

У меня есть другое мнение об this из других ответов, которые, я надеюсь, будут полезны.

Один из способов взглянуть на JavaScript - это увидеть только 1 способ вызова функции 1. это

functionObject.call(objectForThis, arg0, arg1, arg2, ...);

Для objectForThis всегда objectForThis какое-то значение.

Все остальное является синтаксическим сахаром для functionObject.call

Итак, все остальное можно описать тем, как оно переводится в functionObject.call.

Если вы просто вызываете функцию, то this "глобальный объект", который в браузере является окном

function foo() {
  console.log(this);
}

foo();  // this is the window object

Другими словами,

foo();

был эффективно переведен на

foo.call(window);

Обратите внимание, что если вы используете строгий режим, то this будет undefined

'use strict';

function foo() {
  console.log(this);
}

foo();  // this is the window object

что значит

Другими словами,

foo();

был эффективно переведен на

foo.call(undefined);

В JavaScript есть операторы типа + и - и *. Существует также точка оператора, который есть .

. Оператор, когда используется с функцией справа и объектом слева, фактически означает "передать объект как this функция".

пример

const bar = {
  name: 'bar',
  foo() { 
    console.log(this); 
  },
};

bar.foo();  // this is bar

Другими словами, bar.foo() переводится как const temp = bar.foo; temp.call(bar); const temp = bar.foo; temp.call(bar);

Обратите внимание, что не имеет значения, как была создана функция (в основном...). Все это даст одинаковые результаты

const bar = {
  name: 'bar',
  fn1() { console.log(this); },
  fn2: function() { console.log(this); },
  fn3: otherFunction,
};

function otherFunction() { console.log(this) };

bar.fn1();  // this is bar
bar.fn2();  // this is bar
bar.fn3();  // this is bar

Опять же, все это просто синтаксический сахар для

{ const temp = bar.fn1; temp.call(bar); }
{ const temp = bar.fn2; temp.call(bar); }
{ const temp = bar.fn3; temp.call(bar); }

Еще одна морщина - это прототип цепи. Когда вы используете ab JavaScript сначала просматривает объект, на который ссылается a для свойства b. Если b не найден в объекте, тогда JavaScript будет искать в прототипе объекта, чтобы найти b.

Существуют различные способы определения прототипа объекта, наиболее распространенным в 2019 году является ключевое слово class. Для целей this хотя это не имеет значения. Имеет значение то, что, когда он смотрит на объект a для свойства b если он находит свойство b в объекте или в цепочке прототипов, если b оказывается функцией, то применяются те же правила, что и выше. Ссылки на функцию b будут вызываться с использованием метода call и передачи a качестве objectForThis, как показано в верхней части этого ответа.

Сейчас. Давайте представим, что мы создаем функцию, которая явно устанавливает this перед вызовом другой функции, а затем вызываем ее с помощью . (точка) оператор

function foo() {
  console.log(this);
}

function bar() {
  const objectForThis = {name: 'moo'}
  foo.call(objectForThis);  // explicitly passing objectForThis
}

const obj = {
  bar,
};

obj.bar();  

После перевода для использования call obj.bar() становится const temp = obj.bar; temp.call(obj); const temp = obj.bar; temp.call(obj); , Когда мы входим в функцию bar мы вызываем foo но мы явно передаем другой объект для objectForThis, поэтому, когда мы приходим к foo, this тот самый внутренний объект.

Это то, что эффективно выполняют функции bind и =>. Они более синтаксические сахара. Они эффективно создают новую невидимую функцию в точности как вышеупомянутая bar которая явно устанавливает this прежде чем она вызовет любую указанную функцию. В случае связывания this установлено на то, что вы передаете bind.

function foo() {
  console.log(this);
}

const bar = foo.bind({name: 'moo'});

// bind created a new invisible function that calls foo with the bound object.

bar();  

// the objectForThis we are passing to bar here is ignored because
// the invisible function that bind created will call foo with with
// the object we bound above

bar.call({name: 'other'});

Обратите внимание, что если functionObject.bind не существует, мы можем сделать наш собственный вот так

function bind(fn, objectForThis) {
  return function(...args) {
    return fn.call(objectForthis, ...args);
  };
}

и тогда мы могли бы назвать это так

function foo() {
  console.log(this);
}

const bar = bind(foo, {name:'abc'});

Функции стрелок, оператор => являются синтаксическим сахаром для привязки

const a = () => {console.log(this)};

такой же как

const tempFn = function() {console.log(this)}; 
const a = tempFn.bind(this);

Точно так же, как bind, создается новая невидимая функция, которая вызывает данную функцию со связанным значением для objectForThis но в отличие от bind объект, который должен быть связан, является неявным. Как бы this ни было, когда используется оператор =>.

Так же, как и правила выше

const a = () => { console.log(this); }  // this is the global object
'use strict';
const a = () => { console.log(this); }  // this is undefined
function foo() {
  return () => { console.log(this); }
}

const obj = {
  foo,
};
const b = obj.foo();
b();

obj.foo() переводится как const temp = obj.foo; temp.call(obj); const temp = obj.foo; temp.call(obj); Это означает, что оператор стрелки внутри foo свяжет obj с новой невидимой функцией и вернет ту новую невидимую функцию, которая назначена b. b() будет работать, как всегда, как b.call(window) или b.call(undefined) вызывая новую невидимую функцию, созданную foo. Эта невидимая функция игнорирует this переданный в нее, и передает obj как objectForThis 'в функцию стрелки.

Код выше переводится как

function foo() {
  function tempFn() {
    console.log(this);
  }
  return tempFn.bind(this);
}

const obj = {
  foo,
};
const b = obj.foo();
b.call(window or undefined if strict mode);

1apply это еще одна функция, похожая на call

functionName.apply(objectForThis, arrayOfArgs);

Но с ES6 концептуально вы можете даже перевести это на

functionName.call(objectForThis, ...arrayOfArgs);
0

Резюме this Javascript:

  • Значение this определяется тем, как функция вызывается не, где она была создана!
  • Обычно значение this определяется Объектом, который остается от точки. (window в глобальном пространстве)
  • В обработчиках событий значение this относится к элементу DOM, на котором было названо событие.
  • Когда функция вызывается с ключевым словом new значение this относится к вновь созданному объекту
  • Вы можете манипулировать значением this с помощью функций: call, apply, bind

Пример:

let object = {
  prop1: function () {console.log(this);}
}

object.prop1();   // object is left of the dot, thus this is object

const myFunction = object.prop1 // We store the function in the variable myFunction

myFunction(); // Here we are in the global space
              // myFunction is a property on the global object
              // Therefore it logs the window object
              
             

Примеры прослушивателей событий:

document.querySelector('.foo').addEventListener('click', function () {
  console.log(this);   // This refers to the DOM element the eventListener was invoked from
})


document.querySelector('.foo').addEventListener('click', () => {
  console.log(this);  // Tip, es6 arrow function don't have their own binding to the this v
})                    // Therefore this will log the global object
.foo:hover {
  color: red;
  cursor: pointer;
}
<div class="foo">click me</div>

Пример конструктора:

function Person (name) {
  this.name = name;
}

const me = new Person('Willem');
// When using the new keyword the this in the constructor function will refer to the newly created object

console.log(me.name); 
// Therefore, the name property was placed on the object created with new keyword.
-1

Простой ответ:

Это ключевое слово всегда зависит от контекста вызова. Они упомянуты ниже.

  1. ФУНКЦИЯ ВЫЗЫВАЕТСЯ С НОВЫМ КЛЮЧОМ

    Если функция вызывается с ключевым словом NEW, тогда THIS будет привязан к вновь созданному объекту.

    function Car(){
      this.name="BMW";
    }
    const myCar=new Car();
    myCar.name; // output "BMW"
    

    В приведенном выше примере это будет связано с объектом 'myCar'

  2. ФУНКЦИЯ ВЫЗЫВАЕТСЯ ТОЛЬКО С ИСПОЛЬЗОВАНИЕМ МЕТОДОВ ВЫЗОВА И ПРИМЕНЕНИЯ.

    В этом случае ЭТО будет связано с объектом, который явно передается функции.

    var obj1={"name":"bond"};
    function printMessage(msg){
        return msg+" "+this.name;
    }
    const message=printMessage.call(obj1,"my name is ");
    console.log(message); //HERE THIS WILL BE BOUND TO obj1 WHICH WE PASSED EXPLICITLY. SAME FOR APPLY METHOD ALSO.
    
  3. ЕСЛИ ФУНКЦИЯ ВЫЗЫВАЕТСЯ С ОБЪЕКТОМ, НЕПРАВИЛЬНО, ТОГДА ЭТО БУДЕТ СВЯЗАНО С ЭТИМ ОБЪЕКТОМ

    var obj1={
       "name":"bond",
        getName: function () {
                    return this.name;
                 }
    };
    const newname=obj1.getName();
    console.log(newname); //HERE THIS WILL BE BOUND TO obj1(WHITCHEVER OBJECT IS MENTIONED BEFORE THE DOT THIS WILL BE BOUND TO THAT)
    
  4. КОГДА ФУНКЦИЯ ВЫЗЫВАЕТСЯ БЕЗ ЛЮБОГО КОНТЕКСТА, ЭТО БУДЕТ СВЯЗАНО С ГЛОБАЛЬНЫМ ОБЪЕКТОМ

    const util = {
       name: 'Utility',
       getName: function () {
                    return this.name;
    };
    
    const getName=util.getName;
    const newName=getName();
    console.log(newName); // IF THIS EXECUTED IN BROWSER THIS WILL BE  BOUND TO WINDOW OBJECT. IF THIS EXECUTED IN SERVER THIS WILL BE BOUND TO GLOBAL OBJECT
    
  5. В строгом режиме это будет не определено

    function setName(name){
        "use strict"
        return this.name;
    }
    setName(); //WILL BE ERROR SAYING name IS UNDEFINED. 
    
  • 0
    вы пропустили } в пункте 4
-3

"this" - все о сфере видимости. Каждая функция имеет свой собственный объем, и поскольку все в JS является объектом, даже функция может хранить некоторые значения в себе с помощью "this".

$('#a').click(function(){
console.log($(this).attr('href'));
});

Ещё вопросы

Сообщество Overcoder
Наверх
Меню