prototype与__proto__详解
prototype和__proto__在JS中一直是一个比较头痛的东西。JS的继承是基于原型的继承。
在JavaScript中,我们创建一个函数a(就是声明一个函数), 那么浏览器就会在内存中创建一个对象b,而且每个函数都默认会有一个属性 prototype 指向了这个对象( 即:prototype的属性的值是这个对象 )。这个对象b就是函数a的原型对象,简称函数的原型。这个原型对象b 默认会有一个属性constructor指向了这个函数a ( 意思就是说:constructor属性的值是函数a)。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13
| function a(){} a;
function a(){ prototype:{ constructor:function a(){} } }
|
当使用构造函数a创建对象b后,b对象会自动添加一个不可见的prototype属性,这个属性指向构造方法的原型对象,即a.prototype。但是个别浏览器(chrome、火狐支持(其它浏览器暂不清楚),IE不支持)提供了对这个属性的访问方式,即__proto__。
代码:
1 2 3 4 5 6 7 8 9 10 11
| var b = new a();
b = { __proto__:{ constructor:function a(){} } }
b.prototype b.__proto__
|
所以用a构造函数创建的对象原型都指向a.prototype,因此修改a.prototype,通过a函数创建的对象也会受到影响。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| a.prototype.sex = "boy";
function a(){ prototype:{ constructor:function a(){}, sex:"boy" } }
b = { __proto__:{ constructor:function a(){}, sex:"boy" } }
b.__proto__.sex
b.sex
b.sex = "girl"
var b = { sex:"girl", __proto__:{ constructor:function a(){}, sex:"boy" } }
b.sex
b.__proto__.sex
|
因为prototype可以修改,所以你也可以根据需要指定新的对象为a函数的原型对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| a.prototype = { name:"jack", age:"19" }
function a(){ prototype:{ name:"jack", age:"19" } }
a.prototype.constructor = a;
function a(){ prototype:{ constructor:a; name:"jack", age:"19" } }
a.prototype.constructor
|
hasOwnProperty方法
从上面我们已经知道了一个对象的属性可以来自自身也可以来自原型,那么怎么区分呢?hasOwnProperty方法就是用来区分某个属性是否来自自身。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| b.sex b.hasOwnProperty("sex") b.__proto__.name
a.prototype = { name:"jack", age:"19" }
var c = new a() c.__proto__.name
b.__proto__ = a.prototype b.__proto__.name
b.hasOwnProperty("name")
|
in 操作符
in操作符用来判断一个属性是否存在于这个对象中。查找顺序为现在对象自身中查找,自身找不到就到原型中查找,所以只要对象的原型中存在属性,in操作都返回true。
组合原型模型和构造函数模型创建对象
在构造函数中添加的属性和方法,每个对象都有自己独有的一份,大家不会共享。这个特性对属性比较合适,但是对方法又不太合适。因为对所有对象来说,他们的方法应该是一份就够了,没有必要每人一份,造成内存的浪费和性能的低下。
1 2 3 4 5 6 7 8 9 10 11
| function Person() { this.name = "李四"; this.age = 20; this.eat = function() { alert("吃完东西"); } } var p1 = new Person(); var p2 = new Person();
alert(p1.eat === p2.eat);
|
解决方法是将方法提取出来:
1 2 3 4 5 6 7 8 9 10 11 12
| function Person() { this.name = "李四"; this.age = 20; this.eat = eat; } function eat() { alert("吃完东西"); } var p1 = new Person(); var p2 = new Person();
alert(p1.eat === p2.eat);
|
但是上面的这种解决方法具有致命的缺陷:封装性太差。eat函数是暴露在全局中的。
原型模式适合封装方法,构造函数模式适合封装属性,综合两种模式的优点就有了组合模式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| function Person(name, age) { this.name = name; this.age = age; }
Person.prototype.eat = function (food) { alert(this.name + "爱吃" + food); } Person.prototype.play = function (playName) { alert(this.name + "爱玩" + playName); } var p1 = new Person("李四", 20); var p2 = new Person("张三", 30); p1.eat("苹果"); p2.eat("香蕉"); p1.play("志玲"); p2.play("凤姐");
|
动态原型模式创建对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| function Person(name, age) { this.name = name; this.age = age; if(typeof this.eat !== "function"){ Person.prototype.eat = function () { alert(this.name + " 在吃"); } } } var p1 = new Person("志玲", 40); p1.eat();
|
内容参考:http://blog.csdn.net/u012468376/article/details/53121081