Вопрос

Я только что наткнулся на интересную ситуацию в 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
}
Это было полезно?

Решение

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

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

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

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

Как Метод

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

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

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

Как Функция

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

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

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

Многие люди решают проблему, делая что-то вроде, гм, этого

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

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

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

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

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

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

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

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

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

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

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

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

Другие советы

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

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

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

При вызове первый аргумент этих методов указывает объект, на который будет ссылаться 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 это будет иметь место, когда это будет вызвано works:

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

Является ли это определенным поведением?Это кросс-браузер безопасным?

ДА.И да.

Есть ли какие-либо рассуждения, объясняющие, почему так оно и есть...

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

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

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

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

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

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

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

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

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

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

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

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

Ссылка Javascript-ядро и Глава 3-это

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top