Как ключевое слово "this" работает внутри функции?

Я просто наткнулся на интересную ситуацию в JavaScript. У меня есть класс с методом, который определяет несколько объектов с использованием объектно-литеральной нотации. Внутри этих объектов используется указатель this. Из поведения программы я понял, что указатель this ссылается на класс, на который был вызван метод, а не на объект, создаваемый литералом.

Это кажется произвольным, хотя я бы и ожидал, что он сработает. Это определенное поведение? Является ли он межсерверным? Есть ли какие-либо аргументы, объясняющие, почему это так, за исключением того, что "спецификация говорит так" (например, является ли это следствием более широкого дизайнерского решения/философии)? Пример с сокращенным кодом:

// inside class definition, itself an object literal, we have this function:
onRender: function() {

    this.menuItems = this.menuItems.concat([
        {
            text: 'Group by Module',
            rptletdiv: this
        },
        {
            text: 'Group by Status',
            rptletdiv: this
        }]);
    // etc
}

Ответ 1

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

Прежде чем начать, вот самое главное, чтобы иметь в виду Javascript и повторять себя, когда это не имеет смысла. Javascript не имеет классов (ES6 class является синтаксическим сахаром). Если что-то похоже на класс, это умный трюк. Javascript имеет объекты и функции. (что не на 100% точнее, функции - это просто объекты, но иногда это может быть полезно рассматривать их как отдельные вещи).

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

Существует четыре способа вызова функций в javascript. Вы можете вызвать функцию как метод, как функцию, как конструктор и применить.

В качестве метода

Метод - это функция, привязанная к объекту

var foo = {};
foo.someMethod = function(){
    alert(this);
}

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

Как функция

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

 var foo = function(){
    alert(this);
 }
 foo();

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

Многие люди сталкиваются с проблемой, выполняя что-то вроде, um, this

var foo = {};
foo.someMethod = function (){
    var that=this;
    function bar(){
        alert(that);
    }
}

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

ПРИМЕЧАНИЕ. В режиме use strict, если он используется как функция, this не привязан к глобальному. (Это undefined).

Как конструктор

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

Вы вызываете функцию как конструктор с новым ключевым словом.

function Foo(){
    this.confusing = 'hell yeah';
}
var myObject = new Foo();

При вызове в качестве конструктора будет создан новый объект, и это будет связано с этим объектом. Опять же, если у вас есть внутренние функции и они используются как обратные вызовы, вы будете вызывать их как функции, и это будет связано с глобальным объектом. Используйте этот var, который = этот трюк/шаблон.

Некоторые люди считают, что ключевое слово constructor/new было брошено в Java/традиционные программисты OOP, чтобы создать нечто похожее на классы.

С помощью метода Apply

Наконец, каждая функция имеет метод (да, функции - объекты в Javascript) с именем "apply". Применить позволяет определить, что это будет за значение, а также позволяет передавать массив аргументов. Здесь бесполезный пример.

function foo(a,b){
    alert(a);
    alert(b);
    alert(this);
}
var args = ['ah','be'];
foo.apply('omg',args);

Ответ 2

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

Функции - это всего лишь тип объекта.

Все объекты функции call и apply, которые выполняют объект Function, на который они вызывают.

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

Таким образом, вызов функции...

whereAmI = "window";

function foo()
{
    return "this is " + this.whereAmI + " with " + arguments.length + " + arguments";
}

... с круглыми скобками - foo() - эквивалентно foo.call(undefined) или foo.apply(undefined), что фактически совпадает с foo.call(window) или foo.apply(window).

>>> foo()
"this is window with 0 arguments"
>>> foo.call()
"this is window with 0 arguments"

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

Таким образом, foo(1, 2, 3) эквивалентно foo.call(null, 1, 2, 3) или foo.apply(null, [1, 2, 3]).

>>> foo(1, 2, 3)
"this is window with 3 arguments"
>>> foo.apply(null, [1, 2, 3])
"this is window with 3 arguments"

Если функция является свойством объекта...

var obj =
{
    whereAmI: "obj",
    foo: foo
};

... доступ к ссылке на функцию через объект и вызов ее с помощью круглых скобок - obj.foo() - эквивалентен foo.call(obj) или foo.apply(obj).

Однако функции, хранящиеся как свойства объектов, не привязаны к этим объектам. Как вы можете видеть в определении obj выше, поскольку функции являются всего лишь типом объекта, на них можно ссылаться (и, таким образом, они могут передаваться по ссылке на вызов функции или возвращаться по ссылке из вызова функции). Когда передается ссылка на функцию, никакая дополнительная информация о том, куда она была передана, переносится с ней, поэтому происходит следующее:

>>> baz = obj.foo;
>>> baz();
"this is window with 0 arguments"

Вызов нашей ссылки на функцию, baz, не предоставляет контекста для вызова, поэтому он действует так же, как baz.call(undefined), поэтому this заканчивается ссылкой window. Если мы хотим, чтобы baz знал, что он принадлежит obj, нам нужно каким-то образом предоставить информацию, когда вызывается baz, где и начинается первый аргумент call или apply и замыкания.

Цепочки областей

function bind(func, context)
{
    return function()
    {
        func.apply(context, arguments);
    };
}

Когда функция выполняется, она создает новую область и имеет ссылку на любую охватывающую область. Когда анонимная функция создается в приведенном выше примере, она имеет ссылку на область, в которой она была создана, а это bind область. Это называется "закрытием".

[global scope (window)] - whereAmI, foo, obj, baz
    |
    [bind scope] - func, context
        |
        [anonymous scope]

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

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

>>> baz = bind(obj.foo, obj);
>>> baz(1, 2);
"this is obj with 2 arguments"

Ответ 3

Это определенное поведение? Это кросс-браузер безопасен?

Да. И да.

Есть ли какие-либо основания для объяснения причин так оно и есть...

Значение this довольно просто сделать:

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

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

Ответ 4

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

См. "JavaScript: хорошие детали" Дугласа Крокфорда за хорошее объяснение.

Ответ 5

Я нашел хороший учебник об ECMAScript в этом

Это значение является специальным объектом, связанным с исполнением контекст. Следовательно, он может быть назван объектом контекста (то есть объект, в контексте которого контекст выполнения активирован).

Любой объект может использоваться как это значение контекста.

a это значение является свойством контекста выполнения, но не свойство объекта переменной.

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

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

В случае контекста функции это значение в каждом вызове функции может быть различным

Ссылка Javascript-the-core и Chapter-3-this