日期:2014-05-16 浏览次数:20368 次
2011.8.11
多年以来,我看到大量关于javascript函数调用的困惑。尤其,许多人抱怨函数调用中“this”的语意是混乱的。
在我看来,大量这样的混乱可以通过理解核心函数调用原语被清理,然后再看所有其他在原语之上进行包装的调用函数的方法。实际上,这正好是ECMAScript规格对这个问题的考虑。在某些领域,这个是一个规格的简化,但基本思想是一样的。
核心原语
首先,我们来看核心函数调用原语,一个函数的调用方法[1]。这个调用方法是相对直线向前的(The call method is relatively straightforward.)。
1. 构造参数列表(argList)从参数1到最后
2. 第一个参数是thisValue
3. 把this 赋值给thisValue 并用argList 作为参数列表调用函数
例如:
function hello(thing) { console.log(this + " says hello " + thing); } hello.call("Yehuda", "world") //=> Yehuda says hello world
正如你看到的,我们通过把this赋值给 "Yehuda"和一个单一参数来调用hello 方法。这就是javascript函数调用核心原语。你能想象所有其他的函数调用都是对这个原语包装。(包装是使用一个便利的语法和按照更基本的核心原语描述它)
[1] In the ES5 spec, the call method isdescribed in terms of another, more low level primitive, but it’s a
very thinwrapper on top of that primitive, so I’m simplifying a bit here. See the end ofthis post for more information.
简单函数调用
很明显,任何时候使用call 调用函数都是相当的烦人的。Javascript允许我们使用括弧直接调用函数(hello("world"))。我们这样做的时候,调用包装为:
function hello(thing) { console.log("Hello " + thing); } // this: hello("world") // desugars to: hello.call(window, "world");
这个行为在ECMAScript中只有当使用严格模式时改变了:
// this: hello("world") // desugars to: hello.call(undefined, "world");
短版本:函数调用fn(...args)和fn.call(window [ES5-strict: undefined], ...args)等同。
需要注意的是,函数内联声明也是正确的:(function() {})()和(function(){}).call(window [ES5-strict: undefined)等同。
[2]实际上,我说了点谎。ECMAScript 5规格说一般(大多情况)传递的是undefined ,但被调用的函数在非严格模式时应该改变它的thisValue 为全局对象。这允许严格模式调用者避免破坏现存的非严格模式库。
成员函数
下一个非常常见的方法是调用作为对象的成员方法(person.hello())。在这种情况下,调用包装为:
var person = { name: "Brendan Eich", hello: function(thing) { console.log(this + " says hello " + thing); } } // this: person.hello("world") // desugars to this: person.hello.call(pe