引用匿名vs命名函数的属性

时间:2017-03-13 14:32:01

标签: javascript

阅读John Resig的Learning Advanced Javascript,我遇到了两张我不太了解的幻灯片。

幻灯片#13 - 引用作为匿名函数的属性。第二个断言失败。

var ninja = { 
  yell: function(n){ 
    return n > 0 ? ninja.yell(n-1) + "a" : "hiy"; 
  } 
}; 
assert( ninja.yell(4) == "hiyaaaa", "A single object isn't too bad, either." ); // PASS

var samurai = { yell: ninja.yell }; 
var ninja = null; 

try { 
  samurai.yell(4); 
} catch(e){ 
  assert( false, "Uh, this isn't good! Where'd ninja.yell go?" ); // FAIL
}

Slide#14 - 引用属于已定义函数的属性。第二个断言传递。

var ninja = { 
  yell: function yell(n){ 
    return n > 0 ? yell(n-1) + "a" : "hiy"; 
  } 
}; 
assert( ninja.yell(4) == "hiyaaaa", "Works as we would expect it to!" ); // PASS

var samurai = { yell: ninja.yell }; 
var ninja = {}; 
assert( samurai.yell(4) == "hiyaaaa", "The method correctly calls itself." ); // PASS

这里唯一的区别是yell是幻灯片14中的命名函数。为什么即使在将原始对象(ninja)设置为a之后,对定义为命名函数的属性的引用仍然存在新对象(甚至是null)?

2 个答案:

答案 0 :(得分:1)

函数表达式计算为对函数的引用。

foo = function () { ... };

foo的值现在是对函数的引用。您可以将该值复制到任何您喜欢的位置。

命名的函数表达式在其内部创建一个 的局部变量,其名称与函数相同。

foo = function baz () { ... };

此处foo是对全局范围中的函数的引用。 baz是对函数范围内函数的引用。

您可以删除或覆盖任何变量或属性的值,而不会影响包含该引用副本的任何其他变量或属性。

两种情况之间的区别在于,第二种情况是在另一个变量中以第二个引用开始,并且(内部)使用 引用而不是更宽范围内的引用。

答案 1 :(得分:1)

  

这里唯一的区别是yell是幻灯片14中的命名函数。

不,这不是唯一的区别,也不是最重要的区别(虽然它与重要区别有关)。

在第一个中,请注意ninja.yell的定义方式:

var ninja = { 
  yell: function(n){ 
    return n > 0 ? ninja.yell(n-1) + "a" : "hiy"; 
// ----------------^^^^^^^^^^^^^^^
  } 
}; 

它本身明确使用ninja.yell。因此,调用它会查找变量ninja,然后尝试在yell上查找ninja作为属性。

将其与第二个例子中的定义进行比较:

var ninja = { 
  yell: function yell(n){ 
    return n > 0 ? yell(n-1) + "a" : "hiy"; 
// ----------------^^^^^^^^^
  } 
};

yell不再使用变量ninja,它只使用范围内的标识符(通过命名函数表达式为函数赋予名称而创建的标识符)。

这就是为什么在第一个例子中ninja被清空的原因:

var ninja = null; 

......第一个开始失败;您无法在yell上查找null

但是当第二个替换ninaj时(我不明白为什么John在这两种情况下都没有使用null,但无论如何):

var ninja = {};

... yell并不关心,因为它没有使用ninja

附注:从ES2015开始,yell的任何版本都不是匿名函数,即使第一个版本是使用匿名函数表达式创建的。在ES2015中,函数可以根据上下文获取其名称,并且获取名称的方法之一是分配给对象初始值设定项中的对象属性。但是,这对这个问题没有帮助,因为名称不是函数中的范围内标识符,就像使用命名函数表达式时一样。