Почему это поведение для javascript-кода?
Недавно один из моих друзей спросил меня о выходе следующего кода
var length = 10;
function fn() {
console.log(this.length);
}
var obj = {
length: 5,
method: function(fn) {
fn();
arguments[0]();
}
};
obj.method(fn, 1);
Ответы
Ответ 1
Разница заключается в том, что this
контекст каждого вызова метода.
В первом случае, поскольку вызов является просто fn();
, this
контекст - Window
. Длина var length = 10;
объявление переменной наверху происходит в контексте root/Window, поэтому window.length
должно быть 10
, следовательно, 10
в консоли от первого вызова функции.
Поскольку arguments
не являются массивом, но на самом деле являются объектом типа Arguments
, вызывающие arguments[0]()
означает, что this
контекст вызова функции будет из родительского объекта, поэтому this.length
эквивалентен arguments.length
, следовательно 2
(поскольку есть 2 аргумента). (См. @Travis J для более подробного объяснения этой части.)
Если бы вы добавили
this.fn = fn;
this.fn();
к функции method()
результат будет равен 5
.
Ответ 2
Это связано с тем, как this
работает в различных областях, на которые оно ссылается.
Обратите внимание на выходы this.toString()
и вы увидите, на что ссылается цель.
Начиная с вызова функции f
непосредственно из окна, this
будет ссылаться на Window
и поэтому длина будет Window.length
которая была объявлена равной 10
.
Переходим к, если мы назначим f
непосредственно как метод obj
, тогда this
будет ссылаться на obj
и, следовательно, длина будет obj.length
которая была объявлена равной 5
.
Там, где это становится интересным/запутанным, вы передаете f
в качестве параметра для использования method
obj
.
ПРИМЕЧАНИЕ. Результат здесь будет специфичным для браузера. Запустите его в Safari и Chrome и обратите внимание на разные выходы.
В обоих браузерах arguments[0]()
псевдо эквивалентны arguments.0()
(хотя и не разрешены синтаксически для arguments
), что является точно таким же поведением, которое наблюдалось ранее с obj.fn()
что означает, что arguments
являются эталонной целью. Который, как заметил, - количество аргументов, переданных obj.method
.
Выполнение fn
внутри method
- это функция обратного вызова, для которой вы можете найти более обширный ответ здесь.
var length = 10;
function f() {
console.log(this.toString());
console.log(this.length);
}
var obj = {
length: 5,
fn: f,
method: function(fn) {
fn();
arguments[0]();
}
};
f()
f(1);
obj.fn();
obj.fn(1);
obj.method(f, 1);
obj.method(f, 1, 2);
Ответ 3
@musicnothing является полностью правильным, this
привязка отличается между двумя различными вызовами fn
.
Тем не менее, до сих пор, кажется, некоторая путаница в рассуждения, почему arguments
теперь this
цель, которую я обращусь.
this
привязки MDN хранятся внутри Execution Contexts ECMA, которые по существу управляют областью в JavaScript.
Когда вызывается функция, создается объект arguments
. Объект arguments
имеет свой собственный контекст выполнения, что означает, что он имеет свою привязку, собственную переменную среду и собственную лексическую среду. Когда построено, то arguments
объект значения сохраняются в переменной среде, что делают любые ссылки из этой точки по отношению к arguments
объекта контекста исполнения.
По дизайну объект arguments
является подобным массиву, что в основном означает, что он согласен иметь свойство length и более неопределенно, что доступ к индексу, который меньше свойства length, должен иметь значение. В результате вы можете получить доступ к своим ссылкам с индексами через фасад, однако важно иметь в виду, где они находятся в этом месте.
Ответ 4
Ответ равен 10, 2 из-за разных значений "this".
- когда вызвано fn(), это время будет ссылаться на объект окна. а в длину объекта окна - это свойство, значение которого равно 10.
- когда аргументы 0 называются, это относится к методу. где метод является функцией, и каждая функция имеет свойство length, значение которого представляет собой количество параметров, переданных во время вызова.