Как работает ключевое слово "this"? - PullRequest
1210 голосов
/ 27 июня 2010

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

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

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

Ответы [ 22 ]

1269 голосов
/ 27 июня 2010

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

Как только вы начнете привыкать к this, правила на самом деле довольно просты. ECMAScript 5.1 Standard определяет 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

  • …прямым вызовом 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(...); - это косвенный вызов 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.См. Существующий канонический вопрос, Функция стрелки против объявления / выражения функции: эквивалентны ли они / могут быть заменены? для получения дополнительной информации.Вкратце:

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

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

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

  1. Какое значение this наотмеченная линия?Почему?

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

    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 в отмеченной строке?Почему?

    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 в отмеченной строке?Почему?

    function myFun() {
        return this; // What is `this` here?
    }
    var obj = {
        myMethod: function () {
            eval("myFun()");
        }
    };
    
  5. Какое значение this на отмеченной линии?Почему?

    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);
      
141 голосов
/ 07 июля 2013

Ключевое слово 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's 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's prototype but 
//`this.a` inside fun() retrieves obj3.a   

6. Внутри функции call (), 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>

8.this в функции стрелки ES6

В функции стрелки this будет вести себя как общие переменные: она будет унаследована от своей лексической области видимости.Функция this, в которой определена функция стрелки, будет this.

функции стрелки. Таким образом, это то же поведение, что и:

(function(){}).bind(this)

См. Следующий код:

const globalArrowFunction = () => {
  return this;
};

console.log(globalArrowFunction()); //window

const contextObject = {
  method1: () => {return this},
  method2: function(){
    return () => {return this};
  }
};

console.log(contextObject.method1()); //window

const contextLessFunction = contextObject.method1;

console.log(contextLessFunction()); //window

console.log(contextObject.method2()()) //contextObject

const innerArrowFunction = contextObject.method2();

console.log(innerArrowFunction()); //contextObject 
56 голосов
/ 26 октября 2014

Javascript's this

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

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

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

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

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

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

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

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

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

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

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

Если Object содержит свойство, которое содержит 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.

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

Довольно просто, а?

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

Вызов функций в функциях: call и apply

Так что да, поскольку function s также 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, установленное вызовом метода точки, который мы обсуждали во втором пуле. Достаточно просто:)

Представляя .... 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. Умно, а?

Индекс:

  1. Что содержится в this по умолчанию?
  2. Что если мы вызовем функцию как метод с нотацией Object-dot?
  3. Что если мы используем ключевое слово new?
  4. Как нам манипулировать this с call и apply?
  5. Использование bind.
  6. Копирование this для решения проблем с вложенными областями.
47 голосов
/ 27 июня 2010

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

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

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

Так что здесь, внутри каждого (), «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);

Попробуйте; вы начнете любить эту модель программирования

16 голосов
/ 30 октября 2014

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

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

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

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)

table

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

Определено в 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)


Ссылки:

  1. это & ​​Прототипы объектов , автор Кайл Симпсон. © 2014 Getify Solutions.
  2. javascriptissexy.com - http://goo.gl/pvl0GX
  3. Ангус Кролл - http://goo.gl/Z2RacU
16 голосов
/ 06 октября 2013

this в JavaScript всегда относится к «владельцу» выполняемой функции .

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

Так что, если бы я сделал

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

element.onclick = someKindOfFunction;

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

<element onclick="someKindOfFunction()">

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

12 голосов
/ 27 июня 2010

Каждая функция контекст выполнения в javascript имеет scope context этот параметр, который устанавливается с помощью:

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

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

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

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

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

10 голосов
/ 29 ноября 2015

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

Вот сводка:

  • globalthis

    В браузере в глобальном масштабе this является window объектом

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

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

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

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

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

За исключением случая DOMобработчики событий или когда предоставляется thisArg (см. далее), как в узле, так и в браузере, используя 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 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 this

Внутри атрибутов 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"
  • jЗапрос этого

во многих местах 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>
9 голосов
/ 31 января 2017

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

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

  1. Global (т.е. вне всех функций)
  2. Внутри прямого вызова «Неограниченная функция» (то есть функция, которая не была связана с вызовом functionName.bind )
  3. Внутренний косвенный вызов «Не связанная функция» - functionName.call и functionName.apply
  4. Внутри «Связанной функции» Вызов (то есть функция, с которой была связана путем вызова functionName.bind )
  5. При создании объекта с помощью «нового» * ​​1057 *
  6. Внутри встроенного обработчика событий DOM

Далее описывается каждый изэто контексты по одному:

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

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

  2. Inside Direct "Неограниченная функция«Call :

    Внутри прямого вызова« Неограниченная функция », объект, вызвавший вызов функции, становится« текущим объектом » (и, следовательно, значением "это" ).Если функция вызывается без явного текущего объекта , текущий объект является либо "оконным" объектом (для нестрого режима), либо undefined (для строгого режима).Любая функция (или переменная), определенная в Global Context , автоматически становится свойством объекта "window" . Например, предположим, что функция определена в Global Context как

    function UserDefinedFunction(){
        alert(this)
        }
    

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

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

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

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

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

    UserDefinedFunction() // displays undefined
    

    Однако явный вызов ее с использованием объекта window приведет к следующему

    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 не определяет эти свойства.

  3. Внутренний косвенный вызов «Неограниченная функция» - functionName.call и functionName.apply :

    Когда «Не связанная функция» вызывается через 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» Связанную функцию »можно изменить с помощью call / apply . Также, если «this» параметр явно не передается call / apply , «текущий объект» (и, следовательно, значение «this») установлено на "окно" в нестрогом режиме и "неопределенное" в строгом режиме.

  4. Внутри «Связанной функции» Вызов (то есть функция, связанная с вызовом 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" ) установлено в "window" в Non строгий режим и "undefined" в строгом режиме. Еще кое-что. Привязка уже связанной функции не меняет значение «this» . Он остается установленным в качестве значения, установленного первой функцией связывания.

  5. При создании объекта через «новый» :

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

  6. Внутри встроенного обработчика событий 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, на который нажали. Однако значение «это» в OnDivClick НЕ ДОЛЖЕН ссылаться на нажатой div элемент. Он должен быть установлен как "оконный объект" или "undefined" in Не строгие и Строгие режимы соответственно (если OnDivClick является несвязанной функцией ) или установлено к заранее определенному Связанное значение (если OnDivClick является связанной функцией )

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

  1. В глобальном контексте "this" всегда относится к "окну" объекту

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

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

  4. Значение "this" в пределахНе связанные функции могут быть переопределены вызовом и применением методов функции.

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

  6. Связывание и уже связанная функция выполняетНе меняйте значение «это».Он остается установленным в качестве значения, установленного первой функцией связывания.

  7. Значение "this" в конструкторе является объектом, который создается и инициализируется

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

9 голосов
/ 23 апреля 2013

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

В двух словах this в JavaScript указывает объект, от которого (или из чьего контекста выполнения)текущая функция была запущена и всегда доступна только для чтения, вы все равно не можете ее установить (такая попытка заканчивается сообщением «Invalid left-side in assignment».

Для обработчиков событий: встроенные обработчики событий, такие как <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)">
...