Понимание разницы Javascript между вызовом функции и возвратом функции, но ее выполнением позже - PullRequest
5 голосов
/ 10 января 2011

Я пытаюсь понять разницу между foo.bar() и var fn = foo.bar; fn();

Я собрал небольшой пример, но я не совсем понимаю, почему на самом деле терпят неудачу.

var Dog = function() {
    this.bark = "Arf";
};

Dog.prototype.woof = function() {
    $('ul').append('<li>'+ this.bark +'</li>');
};

var dog = new Dog();


// works, obviously
dog.woof();

// works
(dog.woof)();

// FAILS
var fnWoof = dog.woof;
fnWoof();


// works
setTimeout(function() {
    dog.woof();
}, 0);

// FAILS
setTimeout(dog.woof, 0);

Который производит:

  • Арфа
  • Арфа
  • не определено
  • Арфа
  • не определено

На JSFiddle: http://jsfiddle.net/D6Vdg/1/

Похоже, что отключение функции приводит к удалению ее контекста. Хорошо. Но почему тогда (dog.woof)(); работает?

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

Ответы [ 4 ]

5 голосов
/ 10 января 2011

Проблема связана с контекстом и ключевым словом this.

Функции по своей природе не «принадлежат» объекту.Например, я могу создать объект cat и скопировать функцию woof:

var cat = {
    bark: "meow",
    woof = Dog.prototype.woof
};

Теперь cat.woof даст мне «мяу».Из-за этой гибкости функций, подлежащих копированию и переназначению, var fnWoof = dog.woof; отделяет fnWoof от dog - это не имеет контекста.Поэтому контекст и ключевое слово this по умолчанию имеют значение window.Так как window не имеет свойства bark, вы получите undefined.

Если вы предоставите окну свойство лая:

window.bark = "Arf";

Тогда ваш код будет работать (хотяошибочно):

fnWoof(); // "Arf"

Чтобы заставить его работать должным образом, вы можете передать в контексте explicity :

fnWoof.call(dog);
0 голосов
/ 10 января 2011

Это особенно запутанный фрагмент кода по ряду причин, которые я попытаюсь объяснить.

Во-первых ... метод setTimeOut - это немного PITA, как описано здесь: http://ifhere.org/javascript

Проблема, с которой вы столкнулись с setTimeout(dog.woof, 0);, связана с setTimeOut, а не с вашим кодом (ну, не полностью с вашим кодом).

Основная проблема заключается в том, что dog.woofПри выполнении, значение bark не определено, и причина, по которой он не определен, заключается в том, что вы передаете саму функцию в качестве параметра, а не функцию, поскольку она присоединена к переменной dog.

в обоих:

var fnWoof = dog.woof;

и

setTimeout(dog.woof, 500);

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

0 голосов
/ 10 января 2011

dog.woof должен быть запущен в контексте экземпляра Dog, потому что он зависит от this.bark, как вы, кажется, понимаете

Dog.prototype.woof2 = function() {$('ul').append('<li>'+ 'arf' +'</li>'); }; 

Если вы замените тех, кто не работает woofс woof2 он может работать где угодно

Что касается случая (dog.woof)(); против var fnWoof = dog.woof; fnWoof();, то это немного нелогично.Разница в назначении.В первом случае он по-прежнему относится к dog, а во втором - нет.Это время присваивания, которое принимает woof и переводит его в другую константу, а не просто обращается к ней.

0 голосов
/ 10 января 2011

В foo.bar() функция вызывается со значением foo в качестве значения this. При var fn = foo.bar; fn(); функция вызывается с null в качестве значения this, которое автоматически приводится к глобальному объекту при оценке this.

Соответствующая часть спецификации ниже. См. Особенно 7. Если это ссылка на свойство (то есть выражение типа a.b или a[b]), то thisValue становится базой ссылки на свойство.

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

Производственные аргументы CallExpression: MemberExpression оцениваются следующим образом:

  1. Элемент списка
  2. Пусть ref будет результатом вычисления MemberExpression.
  3. Пусть func будет GetValue (ref).
  4. Пусть argList будет результатом вычисления Аргументов и создания внутреннего списка значений аргументов (см. 11.2.4).
  5. Если Type (func) не является Object, генерировать исключение TypeError.
  6. Если IsCallable (func) имеет значение false, генерировать исключение TypeError.
  7. Если Тип (ref) является Ссылкой, тогда

а. Если IsPropertyReference (ref) имеет значение true, то пусть thisValue будет GetBase (ref).

б. Иначе, основанием ref является запись среды, поэтому пусть thisValue будет результатом вызова конкретного метода ImplicitThisValue метода GetBase (ref).

...