回家的路上,我试图忘掉玛利亚·塞克斯,当我爸跟我说晚安的时候--他每次喝完一瓶啤酒就会醉过去,可今天他喝了两瓶(惊叹!)--我试着忘记玛利亚·塞克斯。半小时以后,伊奇来到我的房里,刚洗完淋浴,散发着清新的味道,穿着破烂的“探险家朵拉”睡衣,给我脸颊上来了一个邋遢的湿吻,我试着忘记她;一个小时之后,我妈来到房间门外,说:“我真为你骄傲,萨姆。”这时,我还在想着她。 --《忽然七日》
每个函数内部都有两个特殊的对象,分别是arguments对象和this对象。对于arguments对象,我们知道他是一个类数组,里面的项是传递进来的参数;还需要注意的地方是它还具有一个callee属性,arguments.callee属性的值就是函数本身。同时每个函数都包含有下面三个属性:length,prototype,caller。length属性表明函数所期望接收的参数个数;prototype属性就不解释了;caller属性的值是调用该函数的那个函数的一个引用。
而我们今天要谈的就是函数内部的一个特殊的对象——this。this引用的是函数据以执行的环境对象,对于非箭头函数来说:具体指向哪个对象取决于函数调用时所处的环境,而不是函数被声明时的那个环境;但是对于的箭头函数的情况便是和此相反:具体指向哪个对象取决于函数被声明时所处的那个环境,而不是被调用时所处的环境。
分析普通函数的调用情况,大致可以分为下面几种:
- 1.作为对象的方法调用;
- 2.作为普通函数被调用;
- 3.作为构造器函数被调用;
- 4.被某些能够延长作用域链的函数间接调用,比如Function.prototype.call和Function.prototype.bind函数。
这里既然提到了Function.prototype.apply(),Function.prototype.call()的话,那就打个岔吧,在加入Function.prototype.bind(),下面捋一捋他们的区别。
首先,三个函数都能够改变函数所处的环境对象。其中apply()和call()是改变后立马执行的,这两者之间的区别是apply()能够接收数组以及类数组对象作为第二个参数以便传入多个参数;而call()就不行,他必须显式的把每一个参数分别作为call的第二参数,第三参数依次传递过去。而对于bind函数来说,这个方法会创建一个函数的实例,他的this值会被绑定到那个传入的参数上。和前两者的关键区别在于,他不执行。需要注意的地方是:如果我们对上面三个函数传入的环境对象是null或者undefined的话,那么函数的实际执行环境将会是全局执行环境,在浏览器中就是window,在node环境里就是global。下面可以举个例子看一下:
var a = 999;
function fun(){console.log(this.a);}
fun.apply(null);//9
言归正传,下面来讨论一下在不同的调用函数的方式下this指向哪个对象的分析方法:
- 1.当作为对象方法被调用时,this指向该对象;
- 2.当作为普通函数调用时,this总是指向全局对象。这一点是很让人迷惑的,看看下面的这个例子:
var a = 999;
var obj = {"a" : 111, "fun": function(){
console.log(this.a);
var test = function(){console.log(this.a);
test();
}};
obj.fun();//111/n999
可以很明显的看到,我们的fun()被调用是返回的是999,说明他的执行环境是全局环境。对于这种情况,有的时候的确是会给我们带来挺多不必要的麻烦,比如说下面这个例子:
window.id = "window";
var callback = function(){
console.log(this.id);
};
document.getElementById("div1").onclick = function(){
console.log(this.id);//"div1"
callback();//"window"
}
对于这个callback而言,我们最初是希望它的环境对象是div1的DOM对象的,然而实际上却被绑定到了全局对象window上,而这并不是我们所希望的。当然,这样做也是具有解决办法的,像下面这样。
window.id = "window";
document.getElementById("div1").onclick = function(){
var that = this;
console.log(this.id);//"div1"
var callback = function(){console.log(that.id);};
callback();//"div1"
}
或者通过call和apply后者bind来改变环境对象。
window.id = "window";
var callback = function(){
console.log(this.id);
};
document.getElementById("div1").onclick = function(){
console.log(this.id);//"div1"
callback.apply(this);//"div1"
}
- 3.作为构造器函数调用:凡是正常的构造器函数,他们之中的this都是引用着构造器所返回的对象的。
- 4.当函数利用apply()或者call()以及bind()改变环境对象时,函数的环境对象为这三个方法所传入的那第一个参数对象。
拓展:思考下面这样这样会不会出错
var getId = document.getElementById;
getId("someID");
答案是会出错的。首先,需要明确的是:函数的名字仅仅是一个包含指针的变量而已。因此,即使在不同的环境中执行,他们所指向的都是同一个函数。因此,我们的document.getElementById和getId都是指向同一个函数的,不同的时他们所指向的环境对象的不同,对于前者来说他的环境对象时document而后者的环境对象确实window。而对于该函数的内部实现机理来说,我们时必须要求为document的,所以调用getID的话会出错。
我们活着的每一刻背后都隐藏着成千上万个不一样的瞬间。