Appearance
原型模式
本节目标:理解“原型模式”的核心思路,并能把它用于实际开发或面试表达。
说明:原 OneNote 中有图片引用,但图片未包含在导出目录中;本页保留了可用的文字与代码内容。 把所有实例的公共属性
(如constructor,所以实例都一致的值)和方法放在构造函数的原型prototype对象上。
原型对象
- 所有函数都有一个属性,键名为
prototype,键值为原型对象。 - 构造函数使用
new操作符创建出来的所有实例,将共享原型对象的属性和方法。 - 原型对象都一个属性,键名为
constructor,键值为原型对象所在函数。 - 可以为构造函数的原型对象添加其他属性和方法。
- 这些方法和属性自然也为所有构造函数new出来的实例所共享。
function Person() { }
Person.prototype.name = 'Nicholas';
Person.prototype.age = 29;
Person.prototype.job = 'Software Engineer';
Person.prototype.sayName = function () {
console.log(this.name);
};
var person1 = new Person();
person1.sayName();// Nicholas
var person2 = new Person();
person2.sayName();// Nicholas
person1.sayName == person2.sayName // true实例属性查找机制 一、实例有访问原型对象的隐式指针:
当调用构造函数创建一个新实例后,该实例内部将包含一个指针(内部属性),指向构造函数的原型对象。在
ECMA-262第五版中称为
[[Prototype]];虽然在脚本中没有标准的方式可以访问它,但
Firefox、Safari和Chrome在每个对象上都用属性__proto__来实现对原型对象的访问;通过实例获取原型的方法p.__proto__(非标准方式)p.constructor.prototypeObject.getPrototypeOf(p)- 可通过
isPrototypeOf()来判断原型和实例的联系。
- 可通过
Person.prototype.isPrototypeOf(person1);//true二、查找过程:,原型找到就返回,;
- 先搜自身,找到就返回:每当代码读取某个实例的某个属性时,就会搜索这个属性。搜索首先从实例本身开始。如果在实例中找到则返回该属性的值;
- 没找到就搜原型:如果没有找到,则继续搜索
__proto__指向的原型对象; - 找到就返回:如果在原型对象中找到了这个属性,则返回该属性的值。
- 没找到就继续搜索原型对象的原型:如果原型对象还是没有找到这个属性,由于原型对象也是某个构造函数创造出来的实例,它同样拥有一个指向它的原型对象的指针,所以原型对象会继续在它的原型对象上查找这个属性;
- 找到返回:如果找到了就返回,如果还没有找到,又会向上继续到更上一级的原型对象中去查找;
- 找到最上级还没有就指出属性未定义:查找所以会一直查找到
Object构造函数的原型对象为止,如果还没有找到,就会指出属性未定义。
实例对原型对象中同名属性的影响
- 只屏蔽不修改:不能通过对象实例重写原型中的同名属性的值。当为对象实例指定一个属性时,这个属性就会屏蔽对原型对象中同名属性的访问,但不会修改那个属性。即使将这个属性设置为
null,也只会在实例中设置这个属性,而不会恢复其指向原型的连接。 - 恢复访问:使用
delete操作符则可以完全删除实例属性,从而让我们能够重新访问原型中的属性。 - 利用原型对象访问原型上的同名属性。
hasOwnProperty()方法可以检测一个属性是存在于实例中,还是存在于原型中。这个方法只在给定属性存在于对象实例中时,才会返回true。
原生对象的原型 原生对象的公共属性和方法也定义在原型上; 可以对原生对象的原型进行属性的增加、修改或删除; 不推荐修改原生对象的原型,会导致命名冲突,还可能会意外地重写原生方法。
String.prototype.startsWith = function (text) {
return this.indexOf(text) === 0;
};原型模式创建对象的完美度
- 没有给函数函数传参,所以无法做到实例的差异化,即同实例同属性但不同值。
- 最大的问题是原型中所有属性是被很多实例共享的,这种共享对于函数非常合适;对于那些包含基本值的属性也说得过去,毕竟通过在实例上添加一个同名属性,可以隐藏原型中的对应属性。然而,对于包含引用类型值的属性来说,如果一个实例修改此引用类型的属性,其它实例上的这一属性也会被改,如下:
function Person() { }
};
var person1 = new Person();
var person2 = new Person();
person1.friends.push('Van');
person2.interests.play = 'Movie';
console.log(person1.interests);//'Shelby,Court,Van
console.log(person2.friends);//'Shelby,Court,Van
console.log(person1.friends === person2.friends);//truePerson.prototype = {
constructor: Person,
name: 'Nicholas',
age: 29,
interests: {
play: 'pingpong'
},
job: 'Software Engineer',
friends: ['Shelby', 'Court'],
sayName: function () {
console.log(this.name);
}由于friends数组存在于Person.prototype而非person1中,所以刚刚提到的修改也会通过person2.friends反映出来
