this安全策略

课后整理 2020-12-10

由于this的不确定性,这给开发带来很多风险,因此使用this时,应该时刻保持谨慎。锁定this有两种基本方法:

下面结合3个案例进行说明。

【示例1】使用this作为参数来调用函数,可以避免this因环境变化而变化的问题。

例如,下面做法是错误的,因为this会始终指向Window对象,而不是当前按钮对象。

<input type="button" value="按钮1"  onclick="func()" />
<input type="button" value="按钮2"  onclick="func()" />
<input type="button" value="按钮3"  onclick="func()" />
<script>
function func(){
    console.log(this.value);
}
</script>

如果把this作为参数进行传递,那么它就会代表当前对象。

<input type="button" value="按钮1"  onclick="func(this)" />
<input type="button" value="按钮2"  onclick="func(this)" />
<input type="button" value="按钮3"  onclick="func(this)" />
<script>
function func(obj){
    console.log(obj.value);
}
</script>

【示例2】使用私有变量存储this,设计静态指针。

例如,在构造函数中把this存储在私有变量中,然后在方法中使用私有变量来引用构造函数的this,这样在类型实例化后方法内的this不会发生变化。

function Base(){                                        //基类 
    var _this = this;                                  //当初始化时,存储实例对象的引用指针 
    this.func = function(){
        return _this;                                //返回初始化时实例对象的引用 
    };
    this.name = "Base";
}
function Sub(){                                         //子类 
    this.name = "Sub";
}
Sub.prototype = new Base();                      //继承基类 
var sub = new Sub();                                 //实例化子类 
var _this = sub.func();
console.log(_this.name);                            //this始终指向基类实例,而不是子类实例 

【示例3】使用call和apply强制固定this的值。

作为一个动态指针,this也可以被转换为静态指针。实现方法:使用call或apply方法强制指定this的指代对象。

【实现代码】

// 把this转换为静态指针 
// 参数obj表示预设置this所指代的对象,返回一个预备调用的函数 
Function.prototype.pointTo = function(obj){
    var _this = this;                                  //存储当前函数对象 
    return function(){                               //返回一个闭包函数 
        return  _this.apply(obj, arguments); //返回执行当前函数,并强制设置为指定对象 
    }
}

为Function扩展一个原型方法pointTo(),该方法将在指定的参数对象上调用当前函数,从而把this绑定到指定对象上。

【应用代码】

下面利用这个扩展方法,以实现强制指定对象obj1的方法func()中的this始终指向obj1。具体如下:

var obj1 ={
    name : "this = obj1"
}
obj1.func = (function(){
    return this;
}).pointTo(obj1);                                       //把this绑定到对象obj1身上 
var obj2 ={
    name : "this = obj2",
    func : obj1.func
}
var _this = obj2.func();
console.log(_this.name);                            //返回"this=obj1",说明this指向obj1,而不是obj2

【拓展】

可以扩展new命令的替代方法,从而间接实现自定义实例化类。

// 把构造函数转换为实例对象 
// 参数func表示构造函数,返回构造函数func的实例对象 
function instanceFrom(func){
    var _arg =  [].slice.call(arguments, 1);   //获取构造函数可能需要的初始化参数 
    func.prototype.constructor = func;       //设置构造函数的原型构造器指向自身 
    func.apply(func.prototype, _arg);         //在原型对象上调用构造函数, 
                                                               //此时this指代原型对象,相当实例对象 
    return func.prototype;                         //返回原型对象 
}

下面使用这个实例化类函数把一个简单的构造函数转换为具体的实例对象。

function F(){
    this.name = "F";
}
var f = instanceFrom(F);
console.log(f.name);

call和apply具有强大的功能,它不仅能够执行函数,也能够实现new命令的功能。