原文地址:http://www.zhangyunling.com/?p=251
概述
首先有一點要先明確,那就是this是一個引用類型,也就是說,它是一個對象。OK,概述結束。
基礎示例
首先,讓我們先看下,this到底是指向的哪些對象呢?那就先看一些例子:
function testCallback(){
if(this === window){
console.log("this === window");
}else if(this === document){
console.log("this === document");
}else if(this === a){
console.log("this === a");
}
}
testCallback(); //this===window
document.onclick = testCallback; //this === document
var a = {};
a.callback = testCallback;
a.callback(); //this === a
上述三種最簡單的情況,應該也算是我們最常見的了吧。說是三種,其實也只能算是兩種吧,因爲document.onclick和a.callback的兩種,其實質是相同的,只是對象名和屬性名是不同的。
所以,我們可以先根據上面的情況,把this的指向,定義爲兩種:
1:一種是如果顯示的在一個對象上直接調用了一個方法,那麼其中的this就是指向這個顯示的對象。
2:第二種是,如果沒有顯示的調用的話,比如之前代碼中,直接使用testCallback()執行函數,那麼其中的this,就是指向window對象的。
爲什麼沒有顯示的對象調用時,this是指向window對象呢?記得我們在最初學習JS時有過這樣的描述吧,如果沒有使用var定義一個變量,即便是在局部作用域定義,這也是一個全局變量,而全局變量是會被添加到window對象中,作爲其中的一個屬性的,所以,之前的testCallback方法,其實是存在於window對象中的一個方法。所以,這裏不算是顯示的調用,但是如果把這種調用方法寫的完全的話,應該是window.testCallback()的寫法,所以,按照顯示調用的說法,也是正確的。
OK,上述的兩種說法,只是在我們平常的一些基本的寫法的,比如,在使用DOM2級事件綁定中,回調函數的內部,其this的指向,就和我們常說的有些區別。
例如:
function testCallback(){
if(this === window){
console.log("this === window");
}else if(this === document){
console.log("this === document");
}
}
function addEvent(obj,type,callback){
if(obj.addEventListener){
obj.addEventListener(type,callback,false);
}else if(obj.attachEvent){
obj.attachEvent("on"+type,callback);
}else{
var fn = obj["on"+type];
obj["on"+type] = function(){
fn && fn();
callback();
}
}
}
addEvent(document,"click",testCallback);
這個時候,當你點擊當前頁的任意位置,都可以觸發該事件,這時你會發現些什麼問題呢?這個時候,你就會發現一個問題,在IE9+的瀏覽器中,當你點擊時,會出現"this === document"的顯示,這個時候,也就是,我們這個事件在哪個HTML對象上綁定,那麼回調函數中的this就是指向這個綁定的對象的,之前的代碼中,點擊事件是綁定在document對象上的,所以,this等於document是很正常的了,也是我們希望的結果。
而在IE9-的瀏覽器中,顯示的結果卻是“this === window”,這就導致了一個問題,那就是,我們並不能確認自己的代碼只會在IE9+的瀏覽器中運行,所以這個時候,就不能直接使用this進行處理,但是如果不使用this,獲取到對應的觸發元素,又有些麻煩(這裏只考慮原生的JS,不考慮框架中的實現),所以,爲了減少這種錯誤的情況出現,只能放棄使用this了。
前面之所以在IE9-的瀏覽器事件的回調函數中,this指向window的原因,也是因爲,在IE9-的瀏覽器中,HTML的DOM元素的對象,並不是繼承自Object對象的,所以和其他瀏覽器下,DOM元素的對象繼承自Object對象,是有很大的差距的。
只需要下面的一行代碼,既可以驗證DOM元素的對象是否繼承自Object對象。
console.log(document instanceof Object);
練習一下
基礎的東西,暫時就能想到這麼些了,自我感覺應該也是差不多了,現在給幾個例子,看下是否能正確的理解到this的指向問題:
例子1,請看其中註釋處,分別打印出的結果是什麼:
var a = {
name:"zhang",
sayName:function(){
console.log("this.name="+this.name);
}
};
var name = "ling";
function sayName(){
var sss = a.sayName;
sss(); //this.name = ?
a.sayName(); //this.name = ?
(a.sayName)(); //this.name = ?
(b = a.sayName)();//this.name = ?
}
sayName();
例子2:
var sex = "male";
var saySex = {
sex:"female",
saySex:function(){
function getSex(){
console.log("this.sex="+this.sex);
}
getSex();
}
}
saySex.saySex(); //this.sex = ?
var ccc = saySex.saySex;
ccc();//this.sex = ?
例子3:該例和例2只有一點點變化:
var sex = "male";
var saySex = {
sex:"female",
saySex:function(){
function getSex(){
console.log("this.sex="+this.sex);
}
getSex.call(this);
//與例2只有這個地方的變化
}
}
saySex.saySex(); //this.sex = ?
var ccc = saySex.saySex;
ccc();//this.sex = ?
例4:
var name = "ling";
function sayName(){
var a = {
name:"zhang",
sayName:getName
};
function getName(){
console.log(this.name);
}
getName(); //this.name = ?
a.sayName(); //this.name = ?
getName.call(a);//this.name = ?
}
sayName();
例5(當點擊頁面時,就會觸發):
var name = "ling";
var obj = {
name:"zhang",
sayName:function(){
console.log("this.name="+this.name);
},
callback:function(){
var that = this;
return function(){
var sayName = that.sayName;
that.sayName(); //this.name = ?
sayName();//this.name = ?
}
}
}
function addEvent(obj,type,callback){
if(obj.addEventListener){
obj.addEventListener(type,callback,false);
}else if(obj.attachEvent){
obj.attachEvent("on"+type,callback);
}else{
var fn = obj["on"+type];
obj["on"+type] = function(){
fn && fn();
callback();
}
}
}
addEvent(document,"click",obj.callback());
這裏,暫時就能想到這麼些使用的地方,其實也可以歸納一下:
首先,不管是函數最初定義在哪裏,它其中的this指向,只和這個函數的調用方法有關。
fn()這種寫法的this,肯定是指向window對象的。
obj.fn();這樣寫法的this,肯定是指向obj對象的。
這裏不考慮call和apply強行改變this指向的關係,