作用域安全的构造函数

构造函数其实就是一个使用new操作符调用的函数。当使用new调用时,构造函数内用到的this对象会指向新创建的对象实例,如下面例子所示:

function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
}
var person = new Person("李华",20,"前端开发");

上面这个例子中,Person构造函数使用this对象给三个属性赋值:name、age和job。当和new操作符连用时,则会创建一个新的Person对象,同时会给它分配这些属性。问题出在当没有使用new操作符来调用该构造函数的情况上。由于该this对象是运行时绑定的,所以直接调用Person(),this会映射到全局对象window上,导致错误对象属性的以外增加。例如:

var person = Person("李华",20,"前端开发");
alert(window.name);//前端开发
alert(window.age);//20
alert(window.job);//前端开发

这里,原本针对Person实例的三个属性被加到window对象上,因为构造函数是作为普通函数调用的,忽略了new操作符。这个问题是由this对象的晚绑定造成的,在这里this被解析成了window对象。由于window的name属性是作用于识别链接目标和frame的,所以这里对该属性的偶然覆盖可能会导致该页面上出现其他错误。这个问题的解决方法就是创建一个作用域安全的构造函数。
作用域安全的构造函数在进行任何改变前,首先确认this对象是正确类型的实例。如果不是,那么会创建新的实例并返回。请看下面例子:

function Person(name,age,job){
if(this instanceof Person){
this.name = name;
this.age = age;
this.job = job;
}else{
return new Person(name,age,job);
}
}
var person1 = Person("李华",20,"前端开发");
alert(window.name);//""
alert(person1.name);//"李华"

var person2 = new Person("王明",34,"后端")
alert(person2.name); //"王明"

这段代码中的Person构造函数添加了一个检查并确保this对象是Person实例的if语句,它表示要么使用new操作符,要么在现有的Person实例环境中调用构造函数。任何一种情况下,对象初始化都能正常进行。如果this并非Person的实例,那么会再次使用new操作符调用构造函数并返回实例,这就避免了在全局对象上以外设置属性。
关于作用域安全的构造函数的贴心提示。实现这个模式后,你就锁定了可以调用构造函数的环境。如果你使用构造函数窃取模式的继承且不使用原型链,那么这个继承很可能被破坏。这里有个例子:

funtion Polygon(dides){
if(this instanceof Polygon){
this.sides = sides;
this.getArea= function(){
return 0;
};
}else{
return new Polygon(sides)
}
}

function Rectangle(width,height){
Polygon.call(this,2);
this.width = width;
this.height= height;
this.getArea = function(){
return this.width*this.height
}
}
var rect = new Rectangle(5,10);
alert(rect.sides);//undefined

在这段代码中,Polygon构造函数是作用于安全的,然而Rectangle构造函数则不是。新创建一个Rectangle实例后,这个实例应该通过Polygon.call()来继承Polygon的sides属性。但是,由于Polygon构造函数是作用域安全的,this对象并非Polygon的实例,所以会创建并返回一个新的Polygon对象。Rectangle构造函数中的this对象并没有得到增长,同时Polygon.call()返回的值也没有用到,所以Rectangle实例中就不会有sides属性。
如果构造函数窃取结合使用原型链或者寄生组合则可以解决这个问题。如下面例子:

funtion Polygon(sides){
if(this instanceof Polygon){
this.sides = sides;
this.getArea= function(){
return 0;
};
}else{
return new Polygon(sides)
}
}

function Rectangle(width,height){
Polygon.call(this,2);
this.width = width;
this.height= height;
this.getArea = function(){
return this.width*this.height
}
}
Rectangle.prototype = new Polygon();
var rect = new Polygon();
alert(rect.sides);//2

上面这段重写的代码中,一个Rectangle实例也同时是一个Polygon实例,所以Polygon.call()会照原意执行,最终为Rectangle添加sides属性。
多个程序员在同一个页面上写JavaScript代码的环境中,作用域安全构造函数就很有用了。那样的话,对全局对象意外的更改可能会导致一些常常难以追踪的错误。除非你单纯基于构造函数窃取来实现继承,推荐作用域安全的构造函数作为最佳使用。

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值