理解原型设计模式以及 JavaScript中的原型规则(原文地址)
1、原型对象:我们创建的每一个函数(JavaScript中函数也是一个对象)都有一个原型属性 prototype,原型属性实质上是一个指针,它指向一个对象,这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法(通俗的说:就是这个特定类型的所有实例都可以共享原型对象包含的属性和方法)。
2、原型对象的两赋值方式:
①:
此时实例对象 person1 和 person2 的属性和方法都是用原型对象共享的,所以上面结果是:
②:
和创建一个引用对象一样,也可以采用字面量的形式给原型对象赋值。最终结果和上面结果相同,但是一个例外就是 constructor 属性不再指向Person了,因为字面量的方式重写了原型对象,此时 constructor 属性指向Object对象
但是我们可以指明 constructor 指向Person:
3、原型对象的内存分析:
引用《javascript高级程序第三版》中的内存分析图
Person构造函数、Person原型对象、Person现有的两个实例之间的关系:
每个函数都会有一个原型属性(prototype),它是一个指针,指向原型对象;默认情况下,原型对象包含一个constructor(构造函数)属性(原型最初只包含constructor属性),这个属性包含一个指向prototype属性的指针;如上图:Person.prototype.constructor指向Person,通过这个构造函数就可以继续为原型对象添加其他的属性和方法。
当调用构造函数创建一个新实例后,该实例内部会包含一个内部属性(指针),它指向构造函数的原型对象;这个连接存在于实例与构造函数的原型对象之间,而不是存在于实例和构造函数之间;也就是说这个内部属性和构造函数没有直接的关系。
4、原型对象中的值不能被对象实例重写:
测试结果:
可以看到原型对象中的name属性没有被改变,person1.name来自实例,person2.name来自原型。
在原型模式中当通过person1.name读取属性值时,首先会去实例上查找有没有名称为name的属性,有的话就不会再去原型对象上查找;如果实例上没有,就会去原型对象上搜索。
也就是说当我们给实例上添加了一个属性,这个属性就会阻止我们去原型上访问这个同名属性,但是不会修改那个属性。
内存分析,图中省略了与Person构造函数之间的关系:
通过delete操作符可以直接删除实例中的属性:delete person1.name
5、原型的动态性:
第4点说了不能通过对象实例来修改原型对象中的值,不是说原型对象中的值不能被修改,通过下面方式仍可进行修改:
测试结果:
由于在原型中查找的过程是一次搜索,因此在对原型对象上做的任何修改都会立即从实例上反映出来,即使是先创建实例后修改原型也是如此:
测试结果:
内存分析:
但是如果在先创建实例后修改原型的情况下,用字面量赋值的方式来重写原型对象,这就会切断现有原型与任何之前存在的对象实例之间的联系(不是先创建实例后修改原型的情况下仍然可以用这种方式重写原型对象)
测试结果:
内存分析:
(图引用《JavaScript高级程序设计第3版》,图中值未改,原理一样)
6、原生对象的模型:
所有原生引用类型(Object、String、Array等)都在其构造函数的原型上定义了方法,例如:Array.prototype.sort()、Array.prototype.substring()等,通过原生对象的模型不仅可以读取到所有默认方法的引用,还可以像修改自定义对象的原型一样修改原生对象的原型,所以也可以给原生对象添加方法,但是在实际运用中我们不要这样去修改原生对象的模型(命名冲突、重写原生方法等等问题都是我们在实际开发中所不希望遇到的)。
7、关于原型对象的几种操作:
1)isPrototypeOf检测实例对象是否包含指向某个原型对象的指针,包含返回true,否则返回false。
2)Object.getPrototypeOf(实例对象),返回的就是这个对象的原型,下面结果为true
Object.getPrototypeOf(person1).name,通过这种方式可以访问原型对象上name的值
3)hasOwnProperty(从Object中继承而来),检测一个属性是否存在于实例中,是就返回true,否则返回false
用 in 也可以检测属性值,只不过用这种方式检测的,无论属性在实例中还是在原型中都会返回true
4)Object.keys(原型对象),返回一个包含所有可枚举属性的字符串数组
测试结果:
8、原型对象的缺点:
原型对象的好处是原型对象上的所有属性和方法可以被很多实例共享,缺点是当原型中包含引用类型的值的属性时,一个实例对象对这个引用类型的的属性做了修改,在其他实例对象中也可以体现:
9、好的实践:
1)组合使用构造函数模式和原型模式
2)动态原型模式