Непоследовательное поведение this в строгом режиме JavaScript

Обновить

Для ясности: @FelixKing: Да, я ожидал, что this по-прежнему будет неопределенным при вызове window.foo(), и вот почему: поскольку в JavaScript:

function foo()
{
    console.log('I am a function');
}

Это (почти) то же самое, что и:

var foo = function()
{
    console.log('I am a function');
}

и foo === window.foo оценивается как true, я ожидаю, что они оба будут вести себя одинаково. Если функции являются переменными, а JS возвращается к глобальному объекту (внутри функции x переменная не объявляется, но вы ее используете, JS пузырится вверх по всем областям, вплоть до глобального объекта, пока он находит рассматриваемую переменную или создает ее на глобальном уровне), не имеет значения, указывает ли объект window заранее или нет. Тем не менее, когда вы это делаете, поведение меняется, чего я не ожидал.
У меня есть интуитивное чувство, почему это так (я определяю методы, а не переменные ), но опять же, для глобальных объектов между ними очень мало различий. var c = 'Variable'; console.log(window.c === c); регистрирует true и т. д... но я хотел бы знать, в чем разница на самом деле и как она работает (на всех уровнях).

Я готов зайти так далеко, что смирюсь с тем, что foo.apply(this,[]); или foo.apply(window,[]); позволяют заставить this указывать на глобальный объект, но не window.foo();. Если strict существует для защиты глобального объекта, я бы сказал, что это оставляет черный ход широко открытым. Иногда я вызываю функции, например, в зависимости от значения переменной. Для этого я использую window[myVar]();, строгий или нет, это означает, что this будет указывать на глобальный объект, тогда как если я вызываю функцию напрямую, это не так. Это, на мой взгляд, несоответствие.


Я столкнулся со странным поведением ключевого слова this в строгом режиме. Не поймите меня неправильно, я знаю, что this — это undefined в строгих функциях. Что меня сбивает с толку, так это то, что this можно принудительно указать на глобальный объект (или любой другой объект, если на то пошло), эффективно подрывая систему безопасности, обеспечиваемую строгим режимом. Это имеет и другие последствия. Рассмотрим этот код:

'use strict';//using strict everywhere
(function(Global)
{
    var closureObject = {};
    Global.foo = function()
    {
        closureObject.setIn = closureObject.setIn || 'Set in foo';
        console.log(this);
        console.log(this === Global);
        bar();
        bar.apply(this);
        return (this !== Global ? this : undefined);
    };
    Global.bar = function()
    {
        closureObject.setIn = closureObject.setIn || 'set in bar';
        console.log(this);
        return (this !== Global ? this : undefined);
    };
})(this);
var undef = (Math.ceil(Math.random()*10)%2 ? foo() : bar());
foo();//undefined --- false​​​​​​​​​​​​​​​​​ --- undefined --- undefined
window.foo();//Window --- true --- undefined --- window
foo.apply(this,[]);//same as window.foo

То же самое относится и к пользовательским объектам:

function Foo(n)
{
    this.name = n;
}
Foo.prototype.func = foo;//or window.foo
//other objects:
var d = new Date();
foo.apply(d,[]);//Date --- false --- undefined --- date

Это, на мой взгляд, возможный источник хаков и ловушек. Более того: довольно сложно определить, откуда исходит вызов: если foo() был вызван из глобального объекта (window.foo();), этот контекст, конечно, не передается bar, если только bar не вызывается с использованием bar.apply(this,[]);.

Причина, по которой мне может понадобиться простой, безопасный и надежный способ определения контекста вызывающей стороны, проста: я использую замыкание, чтобы избежать этих надоедливых глобальных переменных, но в то же время я настраиваю пару функции, которые служат обработчиками событий.
Я знаю, что не использовать строгий режим или установить глобальный режим — это легко исправить, но строгий режим никуда не денется, и мне нравится то, что он привносит на вечеринку (ну, большую часть). Я твердо верю, что JS будет развиваться именно так, и мне бы очень не хотелось плакать из-за собственного неработающего кода из-за того, что я не хочу возиться с strict. Возможно, это произойдет не так скоро, но я просто хочу поддерживать свои знания в актуальном состоянии.

Я прочитал страницы MDN на strict, а также сообщение в блоге Джона Резига, я посмотрел довольно много видео DC и прочитал много его статей, и я не нашел окончательного объяснения поведения, которое я описал выше. . Я не читал весь стандарт ECMAScript (Боже, это настолько сухо, что может осушить пустыню Сахара), но, возможно, кто-то здесь может указать мне правильное направление, чтобы помочь мне лучше понять это.


person Elias Van Ootegem    schedule 03.09.2012    source источник
comment
В чем именно непоследовательное поведение здесь? Ожидали ли вы, что this тоже будет undefined, если вы позвоните window.foo()?   -  person Felix Kling    schedule 03.09.2012
comment
Тот же вопрос от меня, так как this можно с радостью упомянуть, когда он не в функциях (в JS нет main()). И как я вижу, это именно то, что здесь происходит.   -  person raina77ow    schedule 03.09.2012
comment
Из того, что я прочитал, this не указывает на undefined в строгом режиме, чтобы избежать установки глобальных переменных, когда они не должны (например, конструкторы без ключевого слова new). Я как бы ожидал, что window.foo() не изменит это, если весь смысл не в установке глобальных переменных, то глобальный контекст должен быть закрыт, не так ли? Я имею в виду, что согласованность это суть строгого режима, верно? зачем тогда включать хакерские обходные пути?   -  person Elias Van Ootegem    schedule 03.09.2012
comment
Весь смысл в том, чтобы не устанавливать глобальный объект в качестве контекста, когда нет прямого определения. Когда вы делаете object.method, вы напрямую определяете this. Если бы это привело к undefined, это было бы серьезной дырой в прототипном наследии WRT.   -  person gray state is coming    schedule 03.09.2012
comment
Я согласен с user160068... неявная ссылка на глобальный объект была опасной/запутанной/подверженной ошибкам/и т. д. Но явная ссылка на глобальный объект — это нормально, и я было бы более запутанным, если бы это не сработало.   -  person Felix Kling    schedule 03.09.2012
comment
@FelixKling @user160068: Я обновил свой вопрос: бывают случаи, когда вы непреднамеренно используете window.foo(); для вызова своей функции, устанавливая this для указания на глобальный объект, чего на самом деле не должно быть. На мой взгляд, открытие глобального объекта должно быть возможным, но только с вызовом функции, который показывает определенное намерение (например, strictFunction.call(window); или что-то в этом роде.   -  person Elias Van Ootegem    schedule 03.09.2012


Ответы (1)


Я не уверен, правильно ли я понимаю ваш вопрос, но кажется, что у вас возникли проблемы с тем фактом, что func.apply(context, arguments) может принимать context в качестве параметра, который затем будет передан this внутри функции.

Я бы сказал, что передача контекста необходима, и без нее JavaScript не может работать должным образом.

Внезапно, поскольку нет super, единственный способ правильно использовать наследование - это использовать что-то вроде BaseClass.prototype.baseFunction.apply(this, arguments).

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

person Otto Allmendinger    schedule 03.09.2012
comment
Извините, я думаю, что мой вопрос мог бы быть более ясным. Дело не в apply или call, даже не в настройке контекста. Дело в том, что window.foo() открывает глобальный объект, а foo() — нет, хотя foo() подразумевает ближайшее определение foo, в данном случае: global. Разрешить window.foo(); устанавливать this для указания на глобальный несколько рискованно. Многие люди используют window[someVar]() для вызова функций. Это открывает глобальный объект и является рискованным. Это должно быть возможно, но только с call и apply до н.э., что показывает явное намерение, не так ли? - person Elias Van Ootegem; 03.09.2012
comment
@Elias: вы можете использовать тот же аргумент для a.b(). Я использую foo.bar(), потому что хочу, чтобы this указывал на foo внутри bar. Это также показывает мое ясное намерение. Конечно, вы можете видеть window.foo() как исключение, потому что window является объектом, а также глобальным объектом. В любом случае, я не думаю, что ТАК правильное место для обсуждения этого, может быть, это лучше подходит для какого-то списка рассылки ECMA. Или чего ожидать от ответа? Объяснение, почему это так? (потому что так работают вызывающие функции, назначенные свойствам); Почему window не был включен из этого? Или согласие? - person Felix Kling; 04.09.2012
comment
@Elias, если бы this в контексте window.foo() не указывало бы на window, я бы назвал это непоследовательным. - person Otto Allmendinger; 04.09.2012
comment
@FelixKling: вы правы, когда я обновлял свой вопрос, я немного больше думал об этом несоответствии, в этом есть смысл. Хотя window[someVar](); по-прежнему несколько опасен, это не столько вопрос, сколько тема для списка рассылки ECMA. Я хотел бы +1 всем, кто участвовал, но я собираюсь закрыть этот вопрос, потому что это скорее обсуждение, чем вопрос. Я оставлю это открытым на пару часов, чтобы каждый мог опубликовать ответ и получить голос за свой вклад. Спасибо! - person Elias Van Ootegem; 04.09.2012