Почему это поведение для 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".

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