理解Javascript调用函数和返回函数但稍后执行它之间的区别

时间:2011-01-10 01:36:33

标签: javascript prototype scope this

我正在尝试理解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);

产生:

  • Arf的
  • Arf的
  • 未定义
  • Arf的
  • 未定义

在JSFiddle上:http://jsfiddle.net/D6Vdg/1/

因此,看起来捕捉一个函数会导致它删除它的上下文。好。但是为什么(dog.woof)();会起作用?

只是有点混乱,弄清楚这里发生了什么。显然有一些我没有得到的核心语义。

4 个答案:

答案 0 :(得分:5)

问题在于上下文和this关键字。

函数本身并不属于对象。例如,我可以创建一个cat对象并将woof函数复制到:

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

现在cat.woof会给我“喵”。由于要复制和重新分配函数的这种灵活性,var fnWoof = dog.woof;fnWoofdog取消关联 - 它没有上下文。因此,上下文和this关键字默认为window。由于window没有bark属性,因此您获得undefined

如果你给窗口一个树皮属性:

window.bark = "Arf";

然后你的代码会起作用(虽然错误):

fnWoof(); // "Arf"

为了使其按预期工作,您可以传递上下文 explicity

fnWoof.call(dog);

答案 1 :(得分:0)

foo.bar()中,调用函数时将值foo作为this的值。 使用var fn = foo.bar; fn();时,使用null作为this的值调用该函数,该值在评估this时自动强制转换为全局对象。

规范的相关部分如下。特别参见7.如果它是属性引用(即a.ba[b]之类的表达式),则thisValue将成为属性引用的基础。

11.2.3函数调用

生产CallExpression:MemberExpression Arguments的计算方法如下:

  1. 列表项
  2. 让ref成为评估MemberExpression的结果。
  3. 让func为GetValue(ref)。
  4. 让argList成为评估Arguments的结果,生成一个参数值的内部列表(见11.2.4)。
  5. 如果Type(func)不是Object,则抛出TypeError异常。
  6. 如果IsCallable(func)为false,则抛出TypeError异常。
  7. 如果Type(ref)是Reference,那么
  8.   

    一个。如果IsPropertyReference(ref)为true,则将thisValue设为GetBase(ref)。

         

    湾否则,ref的基础是一个环境记录,所以让thisValue成为调用GetBase(ref)的ImplicitThisValue具体方法的结果。

答案 2 :(得分:0)

dog.woof必须在Dog的实例上下文中运行,因为它取决于this.bark,因为您似乎已经知道了

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

如果您将失败的woof替换为woof2,则可以随处运行

对于案例(dog.woof)(); vs var fnWoof = dog.woof; fnWoof();,它有点违反直觉。不同之处在于作业。第一种情况仍然将其视为属于dog,而第二种情况则不属于woof。分配时间需要{{1}}并将其置于不同的常量中,而不仅仅是访问它。

答案 3 :(得分:0)

这是一个特别令人困惑的代码,出于多种原因,我将尝试解释。

首先...... setTimeOut方法有点像PITA,如下所述:http://ifhere.org/javascript

您使用setTimeout(dog.woof, 0);时遇到的问题是因为s​​etTimeOut,而不是您的代码(完全不是您的代码)。

主要问题是dog.woof正在执行,它是未定义的bark的值以及它未定义的原因是因为您将函数本身作为参数传递,而不是功能,因为它附加到变量狗。

两者:

var fnWoof = dog.woof;

setTimeout(dog.woof, 500);

您正在传递 函数定义 ,而不是实例化对象及其相关函数。