Статьи

Ключевое слово «это»

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

Давайте посмотрим на объект cody из предыдущей статьи:

Образец: sample98.html

01
02
03
04
05
06
07
08
09
10
11
12
<!DOCTYPE html><html lang=»en»><body><script>
 
    var cody = {
        living: true,
        age: 23,
        gender: ‘male’,
        getGender: function () { return cody.gender;
    };
 
    console.log(cody.getGender());
 
</script></body></html>

Обратите внимание, что внутри функции getGender мы получаем доступ к свойству gender с помощью точечной нотации ( cody.gender ) на cody объекте cody . Это можно переписать, используя this для доступа к объекту cody потому что this указывает на объект cody .

Образец: sample99.html

01
02
03
04
05
06
07
08
09
10
11
12
<!DOCTYPE html><html lang=»en»><body><script>
 
    var cody = {
        living: true,
        age: 23,
        gender: ‘male’,
        getGender: function () { return this.gender;
    };
 
    console.log(cody.getGender());
 
</script></body></html>

this.gender используемый в this.gender просто ссылается на объект cody, над которым работает функция.

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

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

В отличие от arguments и любых параметров, отправляемых функции, this ключевое слово (не свойство) в объекте вызова / активации.


Значение this , переданное всем функциям, основано на контексте, в котором функция вызывается во время выполнения. Обратите внимание, потому что это одна из тех причуд, которые вам просто нужно запомнить.

Объект myObject в следующем примере кода получает свойство sayFoo, которое указывает на функцию sayFoo . Когда функция sayFoo вызывается из глобальной области видимости, this относится к объекту window . Когда он вызывается как метод myObject, this относится к myObject .

Поскольку myObject имеет свойство с именем foo , это свойство используется.

Образец: sample100.html

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
<!DOCTYPE html><html lang=»en»><body><script>
 
    var foo = ‘foo’;
    var myObject = { foo: ‘I am myObject.foo’ };
 
    var sayFoo = function () {
        console.log(this[‘foo’]);
    };
 
    // Give myObject a sayFoo property and have it point to the sayFoo function.
    myObject.sayFoo = sayFoo;
 
    myObject.sayFoo();
    sayFoo();
 
</script></body></html>

Ясно, что значение this зависит от контекста, в котором вызывается функция. Учтите, что и myObject.sayFoo и sayFoo указывают на одну и ту же функцию. Однако, в зависимости от того, откуда (контекст) sayFoo() , значение this может быть другим.

Если это поможет, вот тот же код с явно используемым объектом head ( window ).

Образец: sample101.html

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
<!DOCTYPE html><html lang=»en»><body><script>
 
    window.foo = ‘foo’;
    window.myObject = { foo: ‘I am myObject.foo’ };
 
    window.sayFoo = function () {
        console.log(this.foo);
    };
 
    window.myObject.sayFoo = window.sayFoo;
 
    window.myObject.sayFoo();
    window.sayFoo();
 
</script></body></html>

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

Все переменные, кроме this и arguments следуют лексической области видимости


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

В следующем коде this внутри func2 и func3 теряет свой путь и ссылается не на myObject а вместо этого на объект head.

Образец: sample102.html

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
<!DOCTYPE html><html lang=»en»><body><script>
 
    var myObject = {
        func1: function () {
            console.log(this);
            var func2 = function () {
                console.log(this) // Logs window, and will do so from this point on.
                var func3 = function () {
                    console.log(this);
                } ();
            } ();
        }
    }
 
    myObject.func1();
 
</script></body></html>

Хорошая новость заключается в том, что это будет исправлено в ECMAScript 5. На данный момент вы должны знать об этом затруднительном положении, особенно когда вы начинаете передавать функции в качестве значений другим функциям.

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

Образец: sample103.html

01
02
03
04
05
06
07
08
09
10
11
12
<!DOCTYPE html><html lang=»en»><body><script>
 
    var foo = {
        func1: function (bar) {
            bar();
            console.log(this);
        }
    }
 
    foo.func1(function () { console.log(this) });
 
</script></body></html>

Теперь вы никогда не забудете: this значение всегда будет ссылкой на объект head, когда его основная функция инкапсулирована внутри другой функции или вызывается в контексте другой функции (опять же, это исправлено в ECMAScript 5).


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

Образец: sample104.html

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
<!DOCTYPE html><html lang=»en»><body><script>
 
var myObject = {
    myProperty: ‘I can see the light’,
    myMethod : function(){
        var that = this;
        var helperFunction = function() { // Child function.
            // Logs ‘I can see the light’ via scope chain because that = this.
            console.log(that.myProperty);
            console.log(this);
        }();
    }
}
 
myObject.myMethod();
 
</script></body></html>

Значение this обычно определяется из контекста, в котором вызывается функция (кроме случаев, когда новое ключевое слово используется более подробно об этом через минуту), но вы можете перезаписать и контролировать значение this с помощью apply() или call() для определить, на какой объект this указывает при вызове функции. Использование этих методов все равно что сказать: «Эй, вызовите функцию X, но скажите функции использовать объект Z в качестве значения для this ». При этом способ по умолчанию, при котором JavaScript определяет значение this , переопределяется.

В следующем примере мы создаем объект и функцию. Затем мы вызываем функцию через call() так что значение this внутри функции использует myObject качестве контекста. Тогда операторы внутри функции myFunction будут заполнять свойства myObject вместо заполнения объекта head. Мы изменили объект, на который ссылается this (внутри myFunction ).

Образец: sample105.html

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
<!DOCTYPE html><html lang=»en»><body><script>
 
    var myObject = {};
 
    var myFunction = function (param1, param2) {
        // Set via call(), ‘this’ points to myObject when function is invoked.
        this.foo = param1;
        this.bar = param2;
        console.log(this) // Logs Object {foo = ‘foo’, bar = ‘bar’}
    };
 
    myFunction.call(myObject, ‘foo’, ‘bar’);
 
    console.log(myObject) // Logs Object {foo = ‘foo’, bar = ‘bar’}
 
</script></body></html>

В предыдущем примере мы использовали call() , но можно использовать apply() . Разница между ними заключается в том, как передаются параметры для функции. Используя call() , параметры являются просто значениями, разделенными запятыми. Используя apply() , значения параметров передаются внутри массива, как показано в следующем примере.

Образец: sample106.html

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
<!DOCTYPE html><html lang=»en»><body><script>
 
    var myObject = {};
 
    var myFunction = function (param1, param2) {
        // Set via apply(), this points to myObject when function is invoked.
        this.foo = param1;
        this.bar = param2;
        console.log(this) // Logs Object {foo = ‘foo’, bar = ‘bar’}
    };
 
    myFunction.apply(myObject, [‘foo’, ‘bar’]);
 
    console.log(myObject) // Logs Object {foo = ‘foo’, bar = ‘bar’}
 
</script></body></html>

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


Когда функция вызывается с new ключевым словом, значение this указанное в конструкторе, относится к самому экземпляру. Говорят по-другому: в функции конструктора мы можем использовать объект до того, как объект будет создан. В этом случае значение по умолчанию изменяется таким же образом, как при использовании call() или apply() .

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

Образец: sample107.html

01
02
03
04
05
06
07
08
09
10
11
<!DOCTYPE html><html lang=»en»><body><script>
 
    var Person = function (name) {
        this.name = name ||
    }
 
    var cody = new Person(‘Cody Lindley’);
 
    console.log(cody.name);
 
</script></body></html>

Опять же, this относится к «объекту, который должен быть», когда функция конструктора вызывается с использованием new ключевого слова. Если бы мы не использовали ключевое слово new , значением this было бы контекст, в котором вызывается Person — в данном случае объект head. Давайте рассмотрим следующий сценарий:

Образец: sample108.html

01
02
03
04
05
06
07
08
09
10
11
12
13
<!DOCTYPE html><html lang=»en»><body><script>
 
    var Person = function (name) {
        this.name = name ||
    }
 
    var cody = Person(‘Cody Lindley’);
 
    console.log(cody.name);
 
    console.log(window.name);
 
</script></body></html>

При использовании в функциях, добавленных в свойство prototype конструкторов, this относится к экземпляру, в котором вызывается метод. Скажем, у нас есть пользовательская функция конструктора Person() . В качестве параметра требуется полное имя лица. В случае, если нам нужно получить доступ к полному имени человека, мы добавляем метод whatIsMyFullName к Person.prototype чтобы все экземпляры Person наследовали метод. При использовании this метод может ссылаться на экземпляр, вызывающий его (и, следовательно, на его свойства).

Здесь я демонстрирую создание двух объектов Person ( cody и lisa ) и унаследованного метода whatIsMyFullName который содержит ключевое слово this для доступа к экземпляру.

Образец: sample109.html

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html><html lang=»en»><body><script>
 
    var Person = function (x) {
        if (x) { this.fullName = x };
    };
 
    Person.prototype.whatIsMyFullName = function () {
        return this.fullName;
    }
 
    var cody = new Person(‘cody lindley’);
    var lisa = new Person(‘lisa lindley’);
 
    // Call the inherited whatIsMyFullName method, which uses this to refer to the instance.
    console.log(cody.whatIsMyFullName(), lisa.whatIsMyFullName());
 
    /* The prototype chain is still in effect, so if the instance does not have a fullName property, it will look for it in the prototype chain.
 
    Object.prototype.fullName = ‘John Doe’;
    var john = new Person();
    console.log(john.whatIsMyFullName());
 
</script></body></html>

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

Если экземпляр или объект, на который указывает this , не содержат свойства, на которое ссылаются, применяются те же правила, которые применяются к любому поиску свойства, и свойство будет «найдено» в цепочке прототипов. Таким образом, в нашем примере, если свойство fullName не содержалось в нашем экземпляре, для fullName Person.prototype.fullName , а затем Object.prototype.fullName .