原型对象
原型对象(prototype),无论什么时候,只要创建新函数,就会根据一组特定规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。
在默认情况下,所有原型对象都会自动获得一个constructor属性,这个属性是一个指向prototype所在函数的指针。
如图所示,创建一个构造函数Person,并打印它的prototype属性,控制台输出一个包含constructor属性的对象,constructor属性指向Person函数。
原型对象对普通函数的意义不大,但当函数作为构造函数时,prototype可以起到很大帮助。
构造函数的prototype和实例对象
继续上面的例子,通过new操作符构造一个Person的实例对象person1,打印person1的constructor属性,控制台输出Person函数。明明我们在构造函数中没有给实例对象添加constructor这个属性,为什么控制台还会有内容输出呢?
constructor属性是存放在构造函数原型对象中的,说明实例对象可以访问到原型对象中的属性,也就是说实例对象可以继承构造函数放在prototype中的属性。
利用这个原理,我们可以在构造函数的原型对象中定义方法和实例对象共享的属性,这样做的好处是可以最大限度地节省内存。
如下图所示,分别创建两个实例对象person1、person2,它们各自有自己的name和age属性,同时又从构造函数继承了sayName这个方法,person1和person2共享着对sayName方法的引用
原型链
看完以上例子后,我们可能又有一个疑问,实例对象是怎么实现对构造函数原型对象的继承呢?这又涉及到原型链这个概念。
实例对象的__proto__属性
FireFox、Safari、Chrome浏览器在每个对象上都支持一个属性__proto__,通过这个属性可以访问到对象构造函数的原型对象。注意,这个连接存在于实例与构造函数的原型对象之间,而不是实例与构造函数之间。
打印person1的__proto__属性指向的对象,控制台输出Person的prototype对象
总结构造函数、原型和实例的关系:每个构造函数都有一个原型对象prototype,原型对象都包含一个指向构造函数的指针constructor,而实例都包含一个指向原型对象的内部指针__proto__
再者,prototype也是一个对象,也有自己的__proto__属性,这个属性又指向另一个对象,由此形成一个原型链。
通过原型链实现继承
定义一个父类supClass,并在其原型对象定义一个方法sayName。定义子类subClass,并把父类原型对象赋给子类原型对象的__proto__属性。创建子类的实例对象obj,调用obj.sayName控制台会打印出obj.name。这样子类的所有实例对象就实现了对父类方法的继承。
在这个例子中,调用obj.sayName方法时,obj对象首先会在自身属性中检索sayName,自身属性中没有sayName时,通过obj.__proto__向原型链的上一层即构造函数的prototype中检索,此时还是找不到sayName,再此通过prototype.__proto__向上检索,终于在父类的原型对象中找到这个属性。
注意,构造函数的原型对象都是Object的实例,所以原型链的尽头是Object的原型对象,该对象的__proto__属性为Null。
在使用原型链实现继承时,要注意的是,定义引用类型的属性时,要在构造函数中定义而不是在原型对象中。
对象属性的枚举
1、for-in
for-in语句可以返回所有能通过对象访问的,可枚举(enumeratable = true)的属性,包括存在于实例和原型中的属性。
2、keys、values
Object.keys会把对象的所有可枚举的属性名封装成一个数组返回,不包括原型对象中的属性
Object.values把对象的所有可枚举的属性值封装成一个数组返回,不包括原型对象中的属性
3、如何判断一个属性是在实例还是原型对象中
通过hasOwnProperty可以判断属性是在实例还是原型对象中,不包括不可枚举的属性
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。