(三)我的JavaScript系列:不同调用方式的this指向
人生自是有情痴,此恨不关风与月
今天所写的内容,是对之前的内容的总结和扩展。老实说,对于自己之前的一些杜撰和臆测,我并不是很满意。所以这篇博文,我希望能来点干货。
不同调用方式的this指向
在JavaScript中,我们可以用this来指代当前的对象。这种感觉就像使用Java一样。不过与Java不同的是,在Java中,this的指代总是很明显的;然而在JavaScript中,this的指代在不同的调用方式下,其指代往往不同。我为此分为四类:
1. 作为函数调用,指代的是全局对象
如果我们定义以下函数:
function f() {
console.log(this);
}
然后将其作为普通对象调用,即f()
,此时this指代的是全局对象window。
2. 作为对象的方法调用,指代是调用对象本身
如果我们将f作为一个对象的方法,也就是说作如下改造:
var a = {};
a.f = f;
然后通过对象a调用方法f,即a.f()
,此时this指代的就是调用者a。
3. 作为构造器函数使用,指代的隐含的新建对象
如果我们用new语句调用函数f,即new f()
, 则此时this指代的是新建的对象。
4. call和apply的方式
除了上述几种方式外,我们还可以任意指定this的指代,这就是call和apply发挥作用的地方了。call和apply是每个函数对象都拥有的方法,其第一个参数就是要指定的this值,后面是函数正常的参数值。其细微的差别在于参见下面的示例:
function g(name, age) {
this.name = name;
this.age = age
}
var a = {};
g.call(a, 'xiaoming', 18);
g.apply(a, ['xiaoming', 18]);
即call的非this参数只需在后面列出就可以了,apply要把它们封装到一个数组里面去。
从某种程度上说,前面的三种函数调用形式都是call方式的一种语法糖:
f() === f.call(window)
a.f() === f.call(a)
new f() === var _a = {}; f.call(_a)
call有很强大的能力,我们常常使用的函数借用就是利用call可以动态指定this这一特性的。例如Array.prototype.slice.call(a)
可以将看起来像数组的对象a转化为实际的数组对象。
var a={length:2,0:'first',1:'second'};
Array.prototype.slice.call(a);// ["first", "second"]
var a={length:2};
Array.prototype.slice.call(a);// [undefined, undefined]