函数调用
函数只是Object的一种类型。
所有Function对象都有调用和应用方法,用于执行被调用的Function对象。
当调用时,这些方法的第一个参数指定在函数执行期间this关键字将引用的对象——如果它为空或未定义,则使用全局对象window。
因此,调用函数…
whereAmI = "window";
function foo()
{
return "this is " + this.whereAmI + " with " + arguments.length + " + arguments";
}
...加上括号- foo() -等价于foo.call(undefined)或foo.apply(undefined),这实际上与foo.call(window)或foo.apply(window)相同。
>>> foo()
"this is window with 0 arguments"
>>> foo.call()
"this is window with 0 arguments"
要调用的附加实参作为函数调用的实参传递,而要apply的单个附加实参可以将函数调用的实参指定为类似array的对象。
因此,foo(1,2,3)等价于foo。调用(null, 1,2,3)或foo。应用(null,[1,2,3])。
>>> foo(1, 2, 3)
"this is window with 3 arguments"
>>> foo.apply(null, [1, 2, 3])
"this is window with 3 arguments"
如果函数是对象的属性…
var obj =
{
whereAmI: "obj",
foo: foo
};
...通过对象访问函数的引用并使用圆括号- obj.foo() -等价于foo.call(obj)或foo.apply(obj)。
然而,作为对象属性持有的函数并不“绑定”到这些对象。正如您在上面obj的定义中所看到的,由于函数只是对象的一种类型,所以它们可以被引用(因此可以通过引用传递给函数调用或通过引用从函数调用返回)。当传递一个函数的引用时,不会携带关于它从哪里传递的额外信息,这就是为什么会发生以下情况:
>>> baz = obj.foo;
>>> baz();
"this is window with 0 arguments"
对函数引用baz的调用没有为调用提供任何上下文,因此它实际上与bazs .call(undefined)相同,因此这最终引用了window。如果我们想让baz知道它属于obj,我们需要在调用baz时以某种方式提供该信息,这是调用或应用的第一个参数和闭包发挥作用的地方。
作用域链
function bind(func, context)
{
return function()
{
func.apply(context, arguments);
};
}
当函数被执行时,它会创建一个新的作用域,并具有对任何封闭作用域的引用。在上面的例子中创建匿名函数时,它有一个对创建它的作用域的引用,也就是bind的作用域。这就是所谓的“闭包”。
[global scope (window)] - whereAmI, foo, obj, baz
|
[bind scope] - func, context
|
[anonymous scope]
当您试图访问一个变量时,这个“作用域链”将遍历以查找具有给定名称的变量——如果当前作用域不包含该变量,则查看链中的下一个作用域,以此类推,直到到达全局作用域。当匿名函数返回并且bind完成执行时,匿名函数仍然有一个对bind作用域的引用,因此bind的作用域不会“消失”。
以上所述,你现在应该能够理解scope在下面的例子中是如何工作的,以及为什么在“pre-bound”函数被调用时传递一个具有特定this值的函数的技术是有效的:
>>> baz = bind(obj.foo, obj);
>>> baz(1, 2);
"this is obj with 2 arguments"