(问题1)
在Flanagan的JS权威指南中,他定义了Function
方法bind()
,以防它不可用(在ECMAScript 3中不可用)。
看起来像这样:
function bind(f, o) {
if (f.bind) return f.bind(o); // Use the bind method, if there is one
else return function() { // Otherwise, bind it like this
return f.apply(o, arguments);
};
}
他举例说明了它的用法(我已经修改了它来改变f.bind(o)
的第3行):
function f(y) { return this.x + y; } // This function needs to be bound
var o = { x : 1 }; // An object we'll bind to
var g = bind(f, o); // Calling g(x) invokes o.f(x)
g(2) // => 3
当我第一次看到这个时,我想“不会arguments
引用我们正在定义的绑定函数中的arguments变量吗?但我们想要最终应用它的函数的arguments属性,就像g在上面的例子中......“
我确认他的示例确实有用,并且推测在return f.apply(o, arguments)
以上之前不会评估var g = bind(f, o)
行。也就是说,我想,当你返回一个函数时,你只是返回该函数的源代码,不是吗?直到评估?所以我通过尝试稍微不同的bind版本来测试这个理论:
function mybind2(f, o) {
var arguments = 6;
return function() { // Otherwise, bind it like this
return f.apply(o, arguments);
};
}
如果它只是返回未评估的函数源,那么在以后评估时它无法存储arguments = 6
,对吧?检查后,我仍然得到g(2)
=>但后来我才意识到 - 如果只是返回未评估的代码,o
中的return f.apply(o, arguments)
如何通过?
所以我决定必须发生的事情是:
o
和arguments
变量(即使arguments
等于6) 传递给该函数。只是当最终调用函数g
时,解释器将arguments
变量重新定义为g
的参数(在g(2)
中),因此原始值我试图传递的论点被替换了。但这意味着它在某种程度上将函数存储为文本,因为否则o
和arguments
将只是程序中的数据,而不是可能被覆盖的变量。这个解释是否正确?
(问题2)在同一页面上,他定义了以下函数,该函数使用apply
方法跟踪调试函数:
function trace(o, m) {
var original = o[m]; // Remember original method in the closure.
o[m] = function() { // Now define the new method.
console.log(new Date(), "Entering:", m); // Log message.
var result = original.apply(this, arguments); // Invoke original.
console.log(new Date(), "Exiting:", m); // Log message.
return result; // Return result.
};
}
this
这里不会引用我们定义的函数,而不是对象o
吗?或者这两件事是一样的吗?
答案 0 :(得分:2)
对于你的第一个问题,让我们简化一下这个例子,这样可以清楚地知道做了什么:
function bind(func, thisArg) {
return function () {
return func.apply(thisArg, arguments);
};
}
这里发生的是创建一个闭包,允许访问原始函数和传递的this
的值。返回的匿名函数将原始函数保留在其范围内,最终如下所示:
var func = function () {};
var thisArg = {};
func.apply(thisArg, [/*arguments*/]);
关于arguments
的问题,该变量在所有创建的函数范围内隐式定义,因此内部arguments
将遮蔽外部{ {1}} ,让它按预期工作。
你的问题是你对arguments
的后期绑定的误解 - 对于习惯于更多面向对象语言的人而言,这是一个比较令人困惑的事情之一,这些语言也有自己的this
关键词。 this
的值仅在调用时设置,并且调用它时调用this
的值。它只是父对象的值,从函数调用时的位置,除了覆盖this
值的情况。
例如,请看:
this
如果在定义函数时设置了function a() {return this;};
a(); // global object;
var b = {};
b.a = a;
b.a(); // object b
,则调用this
将导致全局对象,而不是b.a
对象。让我们简化第二个给出的函数:
b
在这种情况下会发生的是原始方法存储在闭包中。分配方法最初所在的对象不会覆盖function trace(obj, property) {
var method = obj[property]; // Remember original method in the closure.
obj[property] = function () { // Now define the new method.
console.log(1); // Log message.
// Invoke original method and return its result
return original.apply(this, arguments);
};
}
对象。就像普通的方法赋值一样,method
值的原理仍然有效 - 它将返回父对象,而不是您定义的函数。如果你做了一个简单的任务:
this
它执行预期的操作,在调用时返回父对象。将代码放在嵌套函数中在这方面没有任何区别。
如果您想了解有关使用JavaScript编写代码的更多信息,我强烈建议您查看Cody Lindley的Fully Understanding the this
Keyword - 它会更详细地介绍var obj = {};
obj.foo = function () { return this; };
obj.foo(); // obj
关键字在不同的上下文中表现,以及你可以用它做的事情。
答案 1 :(得分:2)
但这意味着它在某种程度上将函数存储为文本直到那时,因为否则o和参数将只是程序中的数据,而不是可能被覆盖的变量。这个解释是否正确?
没有。 this
和arguments
只是特殊变量,当函数执行时隐式设置。他们不遵守正常的“关闭规则”。函数定义本身仍然立即被评估,bind
返回一个函数对象
您可以使用以下方法轻松验证:
var g = bind(f, o);
console.log(typeof g);
这是一个更简单的例子,它不涉及更高阶函数:
var arguments = 42;
function foo() {
console.log(arguments);
}
foo(1, 2);
我认为你看到foo
的定义是按照你的期望进行评估的。但是,console.log(arguments)
记录[1, 2]
而非42
。
this
这里不会引用我们定义的函数,而不是对象吗?或者这两件事是一样的吗?
this
从不指的是函数本身(除非你明确地设置它)。 this
的值完全取决于 函数的调用方式。这就是this
通常被称为“背景”的原因。 The MDN documentation provides extensive information about this
阅读材料: