属性构造到了window对象
在JavaScript中构造函数其实是一个使用new操作符调用的函数,在使用呢我调用时, 构造函数内部用到的this对象会指向新创建的对象
function Person(name,age,job){ this.name=name; this.age=age; this.job=job; } var person=new Person('Byron',24,'Software Engineer');
在这个例子中构造函数使用this对象给三个属性赋值:name、age、job,当和new操作符连用的时候会创建一个新的Person对象,并给它分配这三个属性。这样没什么问题,问题出在一疏忽忘记使用new操作符来调用构造函数的情况下,由于this对象是在运行期绑定的所以直接调用Person(),this会映射到全局对象window上,导致错误对象属性的意外增加。
var person= Person('Byron',24,'Software Engineer'); alert(window.name); //Byron alert(window.age); //24 alert(window.job); //Software Engineer
这样原本针对Person对象的三个属性被添加window对象,因为构造函数没有通过new操作符调用,而是作为普通函数被调用的,由于this的晚绑定而被解析成window对象。window的那么属性是用来标识链接目标和框架的,这里对该属性的偶然覆盖可能会导致页面上的其它错误,这个问题的解决方法就是创建一个作用域安全的构造函数。
作用域安全构造函数
作用域安全的构造函数在进行属性赋值之前会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 person= Person('Byron',24,'Software Engineer'); alert(window.name); // "" alert(person.name); //Byron alert(person.age); //24 alert(person.job); //Software Engineer
这段代码中Person函数添加了一个检查,确保this对象是Person的实例,如果不是则使用new操作符调用,如果是怎在现在实例内添加属性,这样保证了无论是否显示使用new操作符,都可以正确构造对象。貌似完美了,但是仍旧有问题。
构造函数窃取模式的继承
构造函数窃取模式是常见的一种实现JavaScript继承的方法,做法是在”子类“的构造函数中调用父类的构造函数以实现继承父类属性,这个模式有很多缺陷,这里不做具体说明,和本文内容相关的是这种模式下上面方法所写的构造函数仍旧不安全。
function Polygon(sides){ if(this instanceof Polygon){ this.sides=sides; this.getArea=function(){ return 0; } }else{ return new Polygon(sides); } } function Rectangle(wifth,height){ Polygon.call(this,2); this.width=this.width; this.height=height; this.getArea=function(){ return this.width * this.height; }; } var rect=new Rectangle(5,10); alert(rect.sides); //undefined
在这段代码中,Ploygon的构造函数是安全的,Rectangle的不是,新创建一个Rectangle实例后,这个实例应该通过Polygon.call()来继承sides属性。但是由于Polygon构造函数是安全的,this对象并非Polygon对象实例,构造函数会创建并返回一个新的Polygon实例,而不会把sides属性赋值到this对象上,所以Rectangle对象实例中没有sides属性。我们可以略施小计来解决这个问题
function Polygon(sides){ if(this instanceof Polygon){ this.sides=sides; this.getArea=function(){ return 0; } }else{ return new Polygon(sides); } } function Rectangle(wifth,height){ Polygon.call(this,2); this.width=this.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的prototype属性,使它的实例也变成Polygon的实例,这样既更像是继承一些,也解决了上面问题。
作用
看起来作用域安全的构造函数很没有市场的样子,很多人会说只要写代码的时候小心些就可以了,但是JavaScript的特点决定了其不能够在编译期发现错误,而在多个程序员开发一个页面的时候,作用域安全的构造函数就很有用了,我们不能保证每个人写的代码都那么小心,如果一个人代码出错,影响到全局属性,那么这种错误难以追踪调试,这时候使用作用域安全的构造函数就可以避免此类问题。