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