使用this

课后整理 2020-12-10

this是由JavaScript引擎在执行函数时自动生成,存在于函数内的一个动态指针,指代当前调用对象。具体用法如下:

this[.属性]

如果this未包含属性,则传递的是当前对象。

this用法灵活,其包含的值也是变化多端。例如,下面示例使用call方法不断改变函数内this指代对象。

var x = "window";                                     //定义全局变量x,初始化为字符串"window"
function a(){                                             //定义构造函数a
    this.x = "a";                                       //定义私有属性x,初始化为字符a
}
function b(){                                            //定义构造函数b
    this.x = "b";                                       //定义私有属性x,初始化为字符b
}
function c(){                                             //定义普通函数,提示变量x的值 
    console.log( x );
}
function f(){                                             //定义普通函数,提示this包含的x的值 
    console.log( this.x );
}
f();                                                          //返回字符串"window",this指向Window对象 
f.call( window );                                      //返回字符串"window",this指向Window对象 
f.call( new a() );                                       //返回字符a,this指向函数a的实例 
f.call( new b() );                                       //返回字符b,this指向函数b的实例 
f.call( c );                                                //返回undefined,this指向函数c对象 

下面简单总结this在5种常用场景中的表现,以及应对策略。

普通调用

【示例1】 下面示例演示了函数引用和函数调用对this的影响。

var obj = {                                               //父对象 
    name : "父对象obj",
    func : function(){
        return this;
    }
}
obj.sub_obj = {                                         //子对象 
    name : "子对象sub_obj",
    func : obj.func                                   //引用父父对象obj的方法func
}
var who = obj.sub_obj.func();
console.log(who.name);                             //返回"子对象sub_obj",说明this代表sub_obj

如果把子对象sub_obj的func改为函数调用。

obj.sub_obj = {
    name : "子对象sub_obj",
    func : obj.func()                                 //调用父对象obj的方法func
}

则函数中的this所代表的是定义函数时所在的父对象obj。

var who = obj.sub_obj.func;
console.log(who.name);                             //返回"父对象obj",说明this代表父对象obj

实例化

【示例2】使用new命令调用函数时,this总是指代实例对象。

var obj ={};
obj.func = function(){
    if(this == obj) console.log("this = obj");
    else if(this == window)  console.log("this = window");
    else if(this.constructor ==  arguments.callee) console.log("this = 实例对象");
}
new obj.func;                                            //实例化 

动态调用

【示例3】使用call和apply 可以强制改变this,使其指向参数对象。

function func(){
    //如果this的构造函数等于当前函数,则表示this为实例对象 
    if(this.constructor ==  arguments.callee) console.log("this = 实例对象");
    //如果this等于Window,则表示this为Window对象 
    else if (this == window) console.log("this  = window对象");
    //如果this为其他对象,则表示this为其他对象 
    else console.log("this == 其他对象 \n this.constructor  = " + this.constructor );
}
func();                                                     //this指向Window对象 
new func();                                               //this指向实例对象 
func.call(1);                                              //this指向数值对象 

在上面示例中,直接调用函数func()时,this代表Window。当使用new命令调用函数时,将创建一个新的实例对象,this就指向这个新创建的实例对象。

使用call方法执行函数func()时,由于call方法的参数值为数字1,则JavaScript引擎会把数字1强制封装为数值对象,此时this就会指向这个数值对象。

事件处理

【示例4】在事件处理函数中,this总是指向触发该事件的对象。

<input type="button" value="测试按钮" />
<script>
var button = document.getElementsByTagName("input")[0];
var obj ={};
obj.func = function(){
    if(this == obj) console.log("this  = obj");
    if(this == window) console.log("this  = window");
    if(this == button) console.log("this  = button");
}
button.onclick = obj.func;
</script>

在上面代码中,func()所包含的this不再指向对象obj,而是指向按钮button,因为func()是被传递给按钮的事件处理函数之后,才被调用执行的。

如果使用DOM 2级标准注册事件处理函数:

if(window.attachEvent){                            //兼容IE模型 
     button.attachEvent("onclick", obj.func);
} else{                                                     //兼容DOM标准模型 
     button.addEventListener("click", obj.func, true);
}

在IE浏览器中,this指向Window和button,而在DOM标准的浏览器中仅指向button。因为,在IE浏览器中,attachEvent()是Window对象的方法,调用该方法时,this会指向Window。

为了解决浏览器兼容性问题,可以调用call或apply方法强制在对象obj身上执行方法func(),避免不同浏览器对this解析不同。

if(window.attachEvent){
     button.attachEvent("onclick", function(){    //用闭包封装call方法强制执行func()
        obj.func.call(obj);
    });
}
else{
     button.addEventListener("click", function(){
        obj.func.call(obj);
    }, true);
}

当再次执行时,func()中包含的this始终指向对象obj。

定时器

【示例5】使用定时器调用函数。

var obj ={};
obj.func = function(){
    if(this == obj) console.log("this  = obj");
    else if(this == window) console.log("this  = window");
    else if(this.constructor ==  arguments.callee) console.log("this = 实例对象");
    else console.log("this == 其他对象 \n this.constructor  = " + this.constructor );
}
setTimeout(obj.func, 100);

在IE中this指向Window和Button对象,具体原因与上面讲解的attachEvent()方法相同。在符合DOM标准的浏览器中,this指向Window对象,而不是Button对象。

因为方法setTimeout()是在全局作用域中被执行的,所以this指向Window对象。解决浏览器兼容性问题,可以使用call或apply方法来实现。

setTimeout(function(){
    obj.func.call(obj);
}, 100);