【问题标题】:Verifying my understanding of the scope chain验证我对范围链的理解
【发布时间】:2013-12-09 02:14:03
【问题描述】:

(问题 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);
    };
}

他用一个例子来说明它的用法(我已经修改了它以将第三行从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 in上面的例子……”

我验证了他的示例确实有效,并推测 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) => 3。但后来我意识到——如果它只是返回未评估的代码,return f.apply(o, arguments) 中的o 是如何通过的?

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

oarguments 变量(即使 arguments 等于 6)正在传递给函数。只是当函数g 最终被调用时,arguments 变量被解释器重新定义为g 的参数(在g(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 对象吗?或者这两件事是一回事?

【问题讨论】:

    标签: javascript scope-chain


    【解决方案1】:

    问题 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 strong>,使其按预期工作。

    问题 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 的 Fully Understanding the this Keyword——它更详细地介绍了 this 关键字在不同环境中的行为方式上下文和你可以用它做的事情。

    【讨论】:

    • 谢谢 - 这清楚了很多。我想我现在明白this 的工作原理了。
    【解决方案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 通常被称为“上下文”。 The MDN documentation provides extensive information about this.


    阅读材料:

    【讨论】:

    • 感谢您的解释和资源。很有帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-24
    • 1970-01-01
    • 2010-11-30
    • 2017-11-16
    • 2015-09-18
    • 2013-10-06
    相关资源
    最近更新 更多