函数调用
函数只是对象的一种类型。
所有 Function 对象都有调用和应用方法来执行调用它们的 Function 对象。
调用时,这些方法的第一个参数指定在函数执行期间关键字将引用的对象 - 如果它是 或 ,则全局对象 , 用于 。this
null
undefined
window
this
因此,调用函数...
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"
其他参数将作为参数传递给函数调用,而单个附加参数 to 可以将函数调用的参数指定为类似数组的对象。call
apply
因此,等效于 或 。foo(1, 2, 3)
foo.call(null, 1, 2, 3)
foo.apply(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
baz.call(undefined)
this
window
baz
obj
baz
call
apply
范围链
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 的工作原理,以及为什么在调用函数时,在具有特定值的“预绑定”周围传递函数的技术将具有 works:this
>>> baz = bind(obj.foo, obj);
>>> baz(1, 2);
"this is obj with 2 arguments"