一、安全的类型检测
JavaScript 内置的类型检测机制并非完全可靠。事实上,发生错误否定及错误肯定的情况也不在少
数。比如说typeof操作符吧,由于它有一些无法预知的行为,经常会导致检测数据类型时得到不靠谱
的结果。Safari(直至第 4 版)在对正则表达式应用typeof操作符时会返回"function",因此很难确
定某个值到底是不是函数。
再比如,instanceof操作符在存在多个全局作用域(像一个页面包含多个 frame)的情况下,也
是问题多多。一个经典的例子(第 5 章也提到过)就是像下面这样将对象标识为数组。
var isArray = value instanceof Array;
以上代码要返回true,value必须是一个数组,而且还必须与Array构造函数在同个全局作用域
中。(别忘了,Array是window的属性。)如果value是在另个 frame 中定义的数组,那么以上代码
就会返回false。
在检测某个对象到底是原生对象还是开发人员自定义的对象的时候,也会有问题。出现这个问题的
原因是浏览器开始原生支持JSON对象了。因为很多人一直在使用 Douglas Crockford 的 JSON 库,而该
库定义了一个全局JSON对象。于是开发人员很难确定页面中的JSON对象到底是不是原生的。
原理:在任何值上调用Object原生的toString()方法,都会返回一个[object NativeConstructorName]格式的字符串
//写成公用函数
function type(val){
return Object.prototype.toString.call(val)
}
二、作用域安全的构造函数
作用域安全的构造函数在进行任何更改前,首先确认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 代码的环境中,作用域安全构造函数就很有用了。届时,
对全局对象意外的更改可能会导致一些常常难以追踪的错误。除非你单纯基于构造函数窃取来实现继
承,推荐作用域安全的构造函数作为最佳实践。
`·