二、作用域安全的构造函数
作用域安全的构造函数在进行任何更改前,首先确认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("Nicholas", 29, "Software Engineer");
alert(window.name); //""
alert(person1.name); //"Nicholas"
var person2 = new Person("Shelby", 34, "Ergonomist");
alert(person2.name); //"Shelby"
关于作用域安全的构造函数的贴心提示。实现这个模式后,你就锁定了可以调用构造函数的环境。
如果你使用构造函数窃取模式的继承且不使用原型链,那么这个继承很可能被破坏。这里有个例子:
function 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;
};
}
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属性。
如果构造函数窃取结合使用原型链或者寄生组合则可以解决这个问题。考虑以下例子:
function 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 Rectangle(5, 10);
alert(rect.sides); //2
上面这段重写的代码中,一个Rectangle实例也同时是一个Polygon实例,所以Polygon.call()
会照原意执行,最终为Rectangle实例添加了sides属性。
多个程序员在同一个页面上写 JavaScript 代码的环境中,作用域安全构造函数就很有用了。届时,
对全局对象意外的更改可能会导致一些常常难以追踪的错误。除非你单纯基于构造函数窃取来实现继
承,推荐作用域安全的构造函数作为最佳实践。 |