这一章相对来说是部重头戏,看完之后才发现用了这么久的js,却很少有用真正OO的思想在写js代码……
js面向对象的核心,相对C++和java,实现方式也比较纠结。一般js里创建对象有三种常用方法:
在以上三种方法中,直接量创建对象继承的是Object.prototype,而new的机制则比较有意思。在var foo = new Foo()
这一句中,发生了以下几个行为:
Object.create()方法的机制也类似。
知道继承的原理后,js中的原型链概念就好理解了。不同于C++中的多重继承,js只存在链式继承,也就是一个对象至多拥有一个父类。
画个图表示一下:
在通过var foo = new Foo();
构建新对象后,Foo.prototype的值被赋给foo对象内自有属性__proto__,同时js内一切对象都继承自Object.prototype,因此这条原型链就是 foo ----> Foo.prototype ----> Object.prototype
同时,比较奇怪的是,foo的构造函数Foo()并没出现在这条原型链上。因为在定义function Foo()
时,表示它是Function的一个实例,因此它继承自Function.prototype。这条原型链就是Foo ----> Function.prototype ----> Object.prototype
这两条可以通过instanceof运算符来验证。实践表明:Foo instanceof Function为true,而foo instanceof Function为false。
js中的对象可以通过getter和setter方法来设置“存取器属性”,类似于C++和java里的私有属性。写法如下:
var round = {
x: 1,
y: 1,
get r() {
return Math.sqrt(this.x*this.x + this.y*this.y);
},
set r(val) {
var oldVal = Math.sqrt(this.x*this.x + this.y*this.y);
var ratio = val / oldVal;
this.x *= ratio;
this.y *= ratio;
}
}
以上是一个表示圆的对象。其中x和y是普通的数据属性,可以通过赋值语句直接操作其值,而r则为存取器属性。可以通过设置有无getter或setter来控制该属性的可读/可写性。之后操作该属性时写法和普通赋值相同:
console.log(round.r); //=>1.4142135623730951
round.r = 2;
console.log(round.r); //=>1.9999999999999998
属性具有4个特性:
js提供了Object.defineProperty和Object.getPropertyDescriptor方法来修改和查询对象属性的特性。如下代码为round添加了一个只读属性“z”:
Object.defineProperty(round, "z", {
value: 0,
writable: false,
enumerable: true,
configurable: true
});
另外js还提供了hasOwnProperty方法来检测某个属性是自有属性还是继承属性。
内容还是蛮多的,待我在后面的章节中再消化一下……