2

(问题 1) 在 Flanagan 的 JS Definitive Guide 中,他定义了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);
    };
}

他用一个例子来说明它的用法(我已经修改了它以将第 3 行从 更改f.bind(o)):

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)上面才被评估。也就是说,我想,当你返回一个函数时,你只是返回那个函数的源代码,不是吗?直到它被评估?所以我通过尝试一个稍微不同版本的绑定来测试这个理论:

function mybind2(f, o) {
    var arguments = 6;
    return function() {          // Otherwise, bind it like this
        return f.apply(o, arguments);
    };
}

如果它只是简单地返回未评估的函数源,那么arguments = 6在以后评估时它就无法存储,对吧?在检查之后,我仍然得到g(2)=> 3。但后来我意识到——如果它只是返回未评估的代码,那么oin是return f.apply(o, arguments)如何通过的?

所以我决定必须发生的事情是这样的:

和变量(即使等于 6)传递给函数o。只是当函数最终被调用时,变量被解释器重新定义为(in ) 的参数,因此我试图传递的参数的原始值被替换了。但这意味着在此之前它只是将函数存储为文本,因为否则将只是程序中的数据,而不是可以被覆盖的变量。这个解释正确吗?argumentsargumentsgargumentsgg(2)oarguments

(问题 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吗?或者这两件事是一回事吗?

4

2 回答 2

2

问题 1

对于您的第一个问题,让我们简化示例,以便清楚所做的工作:

function bind(func, thisArg) {
    return function () {
        return func.apply(thisArg, arguments);
    };
}

这里发生的是创建了一个允许访问原始函数并this传递其值的闭包。返回的匿名函数会将原始函数保留在其范围内,最终如下所示:

var func = function () {};
var thisArg = {};
func.apply(thisArg, [/*arguments*/]);

关于您的问题arguments,该变量隐式定义在创建的所有函数的范围内,因此内部arguments将隐藏外部arguments,使其按预期工作。

问题2

您的问题是您对后期绑定的误解this——对于习惯于更多面向对象的语言(也有自己的this关键字)的人来说,这是关于 JavaScript 的更令人困惑的事情之一。的值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值的原理仍然是一样的——它会返回父对象,而不是你定义的函数。如果你做一个简单的任务:

var obj = {};
obj.foo = function () { return this; };
obj.foo(); // obj

它执行预期的操作,在调用时返回父对象。将代码放在嵌套函数中在这方面没有任何区别。

一些不错的资源

如果您想了解更多关于用 JavaScript 编写代码的信息,我强烈建议您看一看Cody Lindley 撰写的完全理解this关键字——它更详细地介绍了this关键字在不同上下文中的行为方式以及您所做的事情可以用它。

于 2013-12-09T02:15:53.390 回答
2

但这意味着在此之前它只是将函数存储为文本,因为否则 o 和 arguments 将只是程序中的数据,而不是可以被覆盖的变量。这个解释正确吗?

thisarguments并且只是在执行函数时隐式设置的特殊变量。他们不遵守正常的“关闭规则”。函数定义本身仍会立即计算并返回一个函数对象。 您可以通过以下方式轻松验证这一点: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指的是我们定义的函数,而不是对象o吗?或者这两件事是一回事吗?

this 从不引用函数本身(除非您明确设置它)。的值this完全取决于函数的调用方式。这就是为什么this通常被称为“上下文”。MDN 文档提供了有关this.


阅读材料:

于 2013-12-09T02:21:36.330 回答