原型(二)
参考:
《JavaScript高级程序设计(第三版)》
MDN Web Docs MDN Web Docs (mozilla.org)
要点
- 创建对象的几种模式:工厂模式,构造函数模式,原型模式,组合使用构造函数模式和原型模式,动态原型模式,寄生构造函数模式,稳妥构造函数模式。这里讨论下组合使用构造函数模式和原型模式。
- 通过对象访问给定属性的方法有:单独使用in操作符,在for-in循环中使用。通过对象取得所有的实例属性,使用Object.getOwnPropertyNames()方法,若需要可枚举,使用Object.keys()方法。
- 单独使用原型模式创建对象有不足:所有的实例在默认情况下都将取得相同的属性值,对于包含引用类型值的属性问题较为突出,因为实例一般是要有属于自己的全部属性的。解决方法:组合使用构造函数模式和原型模式。
- 组合使用构造函数模式和原型模式的核心:构造函数模式用于定义实例属性,原型模式用于定义方法和共享的属性。
- 理解继承关系的关键是理解原型链。原型链一句话概况是:对象通过内部指针指向原型,原型的指针又可以指向上一级的原型,层层递进,下一级的原型对象将继承上一级原型对象和构造函数的实例中已有的属性和方法,默认的原型是Object.prototype。
通过对象访问给定属性的方法:
-
单独使用in操作符(见文章《JavaScript面向对象中的原型(一)》)
-
在for-in循环中使用:
for-in循环返回的是所有可以通过对象访问的、可枚举的(enumerated)属性,包括三种属性:
- 可枚举的实例属性
- 可枚举的原型属性
- 屏蔽了原本在原型中的不可枚举的原型属性的实例属性([ [Enumerable] ]特性标记为false的属性)
例子:
var obj = {
toString : function(){
return "My Object"
};
for(var p in obj){
if(p == "toString"){
alert("Found toString");
}
}
- Object.keys()方法:接收一个对象作为参数,返回一个包含所有可枚举的实例属性的字符串组。
- Object.getOwnPropertyNames()方法:接收一个对象作为参数,返回一个包含所有实例属性的字符串组。 注意,结果中可能包含不可枚举的construor属性。
原型对象的一些特点
- 可以以对象字面量形式创建新的原型对象。例子:
function Person{}
Person.prototype = {
name : "John",
age : 20,
job : "Engineer"
sayname : function(){
alert(this.name)
};
}
注意:原型对象以这种方式定义时会被重写,constructor属性不在指向Person了。可以刻意的包含一个constructor属性并将其值设为Person。
- 动态性
在原型中查找值是一次搜索,对原型对象所做的任何修改都能立即从实例中反映出来。(即使是先创立了实例后修改了原型)
注意:原型对象被重写会破坏动态性,即使是constructor属性指向Person。原因:重写后会创建一个新的原型对象,而实例的内部指针指向的是最初的原型对象。
- 原生对象也具有原型
所有的原生引用类型(Object,Array,String,等等)都在其构造函数的原型上定义了方法。例如Array.prototype中可以找到sort()方法。
注意:不推荐在产品化的程序中修改原生对象的原型,可能会导致命名冲突或者意外地重写了原生方法。
组合使用构造函数模式和原型模式
例子:
function Person(name, age){
this.name = name;
this.age = age;
this.friends = {"Shelby", "Court"};
}
Person.prototype = {
constructor : Person;
sayName : function(){
alert(this.name);
}
}
一个细节:先重写了原型,再创建了实例,此时实例中的指针指向了新的原型,而避免了动态性被破坏带来的麻烦。
继承与原型链
在OO语言中,继承有两种方式:接口继承和实现继承。接口继承只继承方法签名,而实现继承则继承实际的方法。ECMAScript只支持实现继承,而其实现继承主要是依靠原型链实现的。
当谈到继承时,JavaScript 只有一种结构:对象。每个实例对象(object)都有一个私有属性(称之为 proto )指向它的构造函数的原型对象(prototype)。该原型对象也有一个自己的原型对象(proto),层层向上直到一个对象的原型对象为
null
。根据定义,null
没有原型,并作为这个原型链中的最后一个环节。——MDN文档《继承与原型链》
一句话概况原型链:对象通过内部指针指向原型,原型的指针又可以指向上一级的原型,层层递进,下一级的原型对象将继承上一级原型对象和构造函数的实例中已有的属性和方法,默认的原型是Object.prototype。
关于原型链,一些细节以后再写。