验证我对范围链的理解

时间:2013-12-09 02:14:03

标签: javascript scope-chain

(问题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)如何通过?

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

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吗?或者这两件事是一样的吗?

2 个答案:

答案 0 :(得分: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将遮蔽外部{ {1}} ,让它按预期工作。

问题2

你的问题是你对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和参数将只是程序中的数据,而不是可能被覆盖的变量。这个解释是否正确?

没有。 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这里不会引用我们定义的函数,而不是对象吗?或者这两件事是一样的吗?

this 从不指的是函数本身(除非你明确地设置它)。 this的值完全取决于 函数的调用方式。这就是this通常被称为“背景”的原因。 The MDN documentation provides extensive information about this


阅读材料: