JavaScript 原型详解
2014-07-26 22:38
274 查看
本篇全面分析下JavaScript的原型系统。
最标准的使用原型的代码如下图。这张图包括了对象和原型之间的引用关系。红色的链条就是原型链,按照规则foo会从其原型对象Foo.prototype里获得add属性,add是个函数对象。foo的属性toString和valueOf都继承自Foo.prototype,后者也没有实现这两个方法而是继承自Object.prototype。
1 原型
首先要明确原型是一个对象,目的是为了给新创建的对象快速设置属性。书中一般这么描述原型:"对象都从原型继承属性"。这句话听上去很好理解。但是其真实意思是:"每一个对象都通过引用(非拷贝)方式直接使用了原型的属性",这就是说原型里的属性如果发生了改变,那在对象里继承来的那个属性也会跟随一起改变!因为对象引用了原型的属性,而不是拷贝来的。下面的代码,我们给所有对象的原型Object.prototype设置了属性a,那么所有的对象就有了这个属性。当修改Object.prototype时,x.a依然会跟随改变!
有时我们会说对象foo的原型,但是foo本来是没有prototype这个属性的(__proto__后面会讲到),如果要引用到其原型对象必须要使用Foo.prototype。另外根据图上的剪头走向很容易得出这个等式
2 继承属性的写入
如果向原型继承来的那个属性里写入值,会导致新建一个属性。原型里的属性是不变的。这是JavaScript的一个重要特性。对于从原型继承来的属性,读和写并不是对称的。当对象的新属性创建后,如果再次读取这个属性时,会按照变量查找规则,优先查找本对象的属性,于是刚创建的那个属性被返回了。下面的代码示例里x.a有了新的值2,但是Object.prototype还是1
3 添加还是覆盖
在标准版代码中,我们通过为Foo.prototype添加了一个add属性达到对象共用函数的目的,常见的另一种写法是抛弃原来的原型而重新建立一个用直接量表示的对象,这个对象里包含要被继承的属性,像下面这样
一个对象的constructor属性还是比较重要的,因为在JavaScript里有时依赖其constructor判定类型,所以有必要重新修正下constructor:
4 __proto__属性
ECMAScript 5里并没有__proto__,它是由Firefox最先引入的。由于各主流浏览器厂商甚至Nodejs都支持此属性(IE6~IE10除外,IE11才支持),而且它会变成ECMAScript 6规范的一部分,虽然犀牛书里不推荐使用__proto__,但我觉得那是由于写书时这个属性被支持的还不够广泛,相信作者以后会在新版本里重点介绍的。本篇文章把__proto__也加入进来了。本来JavaScript的原型系统已经让人有点挠头了,但是系统还算清晰,因为普通自定义对象(内置对象除外)是没有直接属性能访问到自己原型对象的,加入__proto__虽然增加了访问原型的方便性,但是也导致了复杂性的提升。看到下面的结果你一定也在挠头吧:-)
最标准的使用原型的代码如下图。这张图包括了对象和原型之间的引用关系。红色的链条就是原型链,按照规则foo会从其原型对象Foo.prototype里获得add属性,add是个函数对象。foo的属性toString和valueOf都继承自Foo.prototype,后者也没有实现这两个方法而是继承自Object.prototype。
1 原型
首先要明确原型是一个对象,目的是为了给新创建的对象快速设置属性。书中一般这么描述原型:"对象都从原型继承属性"。这句话听上去很好理解。但是其真实意思是:"每一个对象都通过引用(非拷贝)方式直接使用了原型的属性",这就是说原型里的属性如果发生了改变,那在对象里继承来的那个属性也会跟随一起改变!因为对象引用了原型的属性,而不是拷贝来的。下面的代码,我们给所有对象的原型Object.prototype设置了属性a,那么所有的对象就有了这个属性。当修改Object.prototype时,x.a依然会跟随改变!
Object.prototype.a = 1; var x = {}; x.a; // ==> 1 Object.prototype.a = 2; x.a; // ==> 2
有时我们会说对象foo的原型,但是foo本来是没有prototype这个属性的(__proto__后面会讲到),如果要引用到其原型对象必须要使用Foo.prototype。另外根据图上的剪头走向很容易得出这个等式
foo.constructor === Foo.prototype.constructor === Foo // ==> true
2 继承属性的写入
如果向原型继承来的那个属性里写入值,会导致新建一个属性。原型里的属性是不变的。这是JavaScript的一个重要特性。对于从原型继承来的属性,读和写并不是对称的。当对象的新属性创建后,如果再次读取这个属性时,会按照变量查找规则,优先查找本对象的属性,于是刚创建的那个属性被返回了。下面的代码示例里x.a有了新的值2,但是Object.prototype还是1
Object.prototype.a = 1; var x = {}; x.a = 2; // ==> 1 console.log(Object.prototype.a); // ==> 1 console.log(x.a); // ==> 1
3 添加还是覆盖
在标准版代码中,我们通过为Foo.prototype添加了一个add属性达到对象共用函数的目的,常见的另一种写法是抛弃原来的原型而重新建立一个用直接量表示的对象,这个对象里包含要被继承的属性,像下面这样
Foo.prototype = { add: function (x, y) { return x + y; } }这样会产生一个不大不小的问题,那就是foo.constructor的值不正确,同时Foo.prototype.constructor的值也不正确。他们本来都应该等于Foo,但是上面的函数执行后,他们变成了Object。为什么呢?因为通过直接量创建的对象的原型都是Object.prototype,当然其constructor属性也就是Object了,原型的constructor属性会被foo继承,所以foo.constructor自然也就变成了Object.prorotype。
一个对象的constructor属性还是比较重要的,因为在JavaScript里有时依赖其constructor判定类型,所以有必要重新修正下constructor:
Foo.prototype = { add: function (x, y) { return x + y; } }看来还是添加属性的方法更简单切不易出错,推荐使用。
Foo.prototype.constructor = Foo;
4 __proto__属性
ECMAScript 5里并没有__proto__,它是由Firefox最先引入的。由于各主流浏览器厂商甚至Nodejs都支持此属性(IE6~IE10除外,IE11才支持),而且它会变成ECMAScript 6规范的一部分,虽然犀牛书里不推荐使用__proto__,但我觉得那是由于写书时这个属性被支持的还不够广泛,相信作者以后会在新版本里重点介绍的。本篇文章把__proto__也加入进来了。本来JavaScript的原型系统已经让人有点挠头了,但是系统还算清晰,因为普通自定义对象(内置对象除外)是没有直接属性能访问到自己原型对象的,加入__proto__虽然增加了访问原型的方便性,但是也导致了复杂性的提升。看到下面的结果你一定也在挠头吧:-)
Foo.__proto__ === Foo.prototype // ==> false foo.__proto__ === Foo.prototype // ==> true Object.__proto__ === Foo.__proto__ // ==>true
相关文章推荐
- JavaScript模块化编程(一):模块原型和理论概念详解
- 基于JavaScript实现继承机制之原型链(prototype chaining)的详解
- javascript原型详解
- JavaScript原型及原型链终极详解
- JavaScript原型及原型链详解
- JavaScript 原型链详解
- javascript原型继承工作原理和实例详解
- JavaScript中的原型prototype属性使用详解
- javascript原型模式用法实例详解
- javascript原型与原型链终极详解
- JavaScript原型机制详解
- JavaScript的原型继承详解
- javascript 原型链维护和继承详解
- JavaScript中的原型prototype属性使用详解
- JavaScript原型链详解
- 基于JavaScript实现继承机制之原型链(prototype chaining)的详解
- javascript 原型链维护和继承详解
- 深入理解JavaScript系列(42):设计模式之原型模式详解
- javascript中原型连的详解
- javascript原型继承工作原理和实例详解