们在实例中添加了一个属性,而该属性与实例原型中的一个属性同名,那么我们就在实例中创建该属性,该属性会屏蔽原型中的那个属性。
5.2 原型与in操作符
有两种方式使用 in 操作符:单独使用和在 for-in循环中使用。在单独使用时,in操作符会在通过对象能够访问给定属性时返回 true,无论该属性存在于实例中还是原型中。 另外由于in操作符只要通过对象能够访问到属性就返回 true ,hasOwnProperty()只在属性存在于实例中时才返回true,因此只要 in 操作符返回 true 而 hasOwnProperty()返回 false,就可以确定属性时原型中的属性。
注意:在使用 for-in 循环时,返回的是能够通过对象访问的、可枚举的属性,其中既包括存在于实例中的属性,也包括存在于原型中的属性。屏蔽了原型中不可枚举属性的实例属性也会在 for-in循环中返回。
5.3 更简单的原型语法
为了减少不必要的输入,以及从视觉上更好的封装原型的功能,常见的作法是用包含一个说有属性和方法的对象字面量来重写整个原型对象。
function P(){
}
P.prototype = {
name: 'tc',
age: 22,
job: 'web',
sayName: function(){
console.log(this.name)
}
}
5.4 原型的动态性
由于在原型中查找值的过程是一次搜索,因此我们对原型对象所做的任何修改都能够立即从实例上反映出来。尽管可以随时为原型添加属性和方法,并且修改能够立即在所有对象实例中反映出来,但如果是重写整个原型对象,那么情况就不同了。另外,调用构造函数时会为实例添加一个指向最初原型__proto__指针,而把原型修改为另一个对象就等于切断了构造函数和最初原型之间的联系。实例中的指针仅指向原型,而不是指向构造函数。
5.5 原生对象的原型
原型模式的重要性不仅体现在创建自定义类型方面,就连所有原生的引用类型,都是采用这种模式创建的。所有原生引用类型(Object、Array、String)都在其构造函数的原型上定义了方法。
typeof Array.prototype.sort // function
typeof String.prototype.substring // function
通过原生对象的原型,不仅可以取得所有默认方法的引用,也可以定义新的方法。可以像修改自定义对象的原型一样修改原生对象的原型,因此可以随时添加方法。
重要:尽管可以这样做,但是并不推荐在产品化的程序中修改原生对象的原型。如果因某个实现中缺少某个方法,就在原生对象的原型中添加这个方法,那么当在另一个支持该方法的实现中运行代码时,就可能的导致命名冲突,而且,这样做也可能会意外地重写原生方法
5.6 原型对象的问题
原型模式也有缺点,第一、它省略了为构造函数传递初始化参数这一环节,结果所有实例在默认情况下都将取得相同的属性值。虽然这个在某一程度上带来了不方便,但其最大的问题还是由其共享的本性所导致的。
在原型中,所有的属性时被很多实例共享的,这种共享对于函数非常合适。对于那些包含基本值的属性也行,但是对于引用类型值的属性来说,就有问题了。
6. 组合使用构造函数模式和原型模式
创建自定义类型的最常见方式,就是组合使用构造函数模式和原型模式。构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。结果每个实例都会有自己的一份实例属性的副本,但同时又共享着对方法的引用,最大限度地节省了内存。
7. 动态原型模式
把所有信息都封装了在构造函数中,而通过构造函数中初始化原型,又保持了同时使用构造函数和原型的有点。也就是说可以通过检查某一个应该存在的方法是否有效,来决定是否初始化原型。
function P(name, age, job) {
this.name = name
this.age = age
this.job = job
if(typeof this.sayName != 'function') {
P.prototype.sayName = fucntion() {
console.log(this.name)
}
}
}
let p = new P('tc', 23, 'web')
p.sayName() // tc
上面的代码 只有在 sayName() 方法不存在的情况下才会将它添加到原型中。这段代码只有在初次调用函数时才会执行。过后,原型已经初始化完成,不需要再修改。这样对原型所做的修改,能够立即在所有实例中得到反映。另外if语句检查的可以是初始化之后应该存在的任何属性或方法。
注意: 使用动态原型模式时,不能使用对象字面量重写原型,如果在已经创建了实例的情况下重写原型,那么就会切断现有实例与新原型之间的联系。
8. 寄生构造函数模式
创建一个函数,该函数的作用仅仅时封装创建对象的代码,然后再返回新创建的对象。
注意: 寄生构造函数模式返回的对象与构造函数或者与构造函数的原型属性之间没有关系,也就是说,构造函数返回的对象与在构造函数外部创建的对象没有什么不同。为此,不能依赖 instanceof 操作符来确定对象类型。
9. 稳妥构造函数模式
与寄生构造函数模式类似,使用稳妥构造函数模式创建的对象与构造函数之间也没有什么关系,因此 instanceof 操作符对这种对象也没有意义。
欢迎关注 公众号【小夭同学】
重学js系列
重学js之java script简介
重学 JS 之在 HTML 中使用 java script
重学js之java script基本概念(上)=> 数据类型
重学js之java script基本概念(中)=> 操作符
重学js之java script基本概念(下)=> 运算符
重学js之java script变量、作用域和内存问题
重学java script之引用类型
ES6入门系列
ES6入门之let、cont
ES6入门之变量的解构赋值
ES6入门之字符串的扩展
ES6入门之正则的扩展
ES6入门之数值的扩展
ES6入门之函数的扩展
ES6入门之数组的扩展
ES6入门之对象的扩展
ES6入门之Symbol
Git教程
前端Git基础教程