原型对象

原型对象(prototype),无论什么时候,只要创建新函数,就会根据一组特定规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。

在默认情况下,所有原型对象都会自动获得一个constructor属性,这个属性是一个指向prototype所在函数的指针。
构造函数原型对象.png
控制台结果1.png
如图所示,创建一个构造函数Person,并打印它的prototype属性,控制台输出一个包含constructor属性的对象,constructor属性指向Person函数。

原型对象对普通函数的意义不大,但当函数作为构造函数时,prototype可以起到很大帮助。

构造函数的prototype和实例对象

继续上面的例子,通过new操作符构造一个Person的实例对象person1,打印person1的constructor属性,控制台输出Person函数。明明我们在构造函数中没有给实例对象添加constructor这个属性,为什么控制台还会有内容输出呢?
person1.png
person1.constructor.png
constructor属性是存放在构造函数原型对象中的,说明实例对象可以访问到原型对象中的属性,也就是说实例对象可以继承构造函数放在prototype中的属性。

利用这个原理,我们可以在构造函数的原型对象中定义方法和实例对象共享的属性,这样做的好处是可以最大限度地节省内存。

如下图所示,分别创建两个实例对象person1、person2,它们各自有自己的name和age属性,同时又从构造函数继承了sayName这个方法,person1和person2共享着对sayName方法的引用
sayName.png
sayNameres.png

原型链

看完以上例子后,我们可能又有一个疑问,实例对象是怎么实现对构造函数原型对象的继承呢?这又涉及到原型链这个概念。

实例对象的__proto__属性

FireFox、Safari、Chrome浏览器在每个对象上都支持一个属性__proto__,通过这个属性可以访问到对象构造函数的原型对象。注意,这个连接存在于实例与构造函数的原型对象之间,而不是实例与构造函数之间。

打印person1的__proto__属性指向的对象,控制台输出Person的prototype对象
person1.prototype.png
person1.__proto__.png

总结构造函数、原型和实例的关系:每个构造函数都有一个原型对象prototype,原型对象都包含一个指向构造函数的指针constructor,而实例都包含一个指向原型对象的内部指针__proto__

再者,prototype也是一个对象,也有自己的__proto__属性,这个属性又指向另一个对象,由此形成一个原型链。

通过原型链实现继承

定义一个父类supClass,并在其原型对象定义一个方法sayName。定义子类subClass,并把父类原型对象赋给子类原型对象的__proto__属性。创建子类的实例对象obj,调用obj.sayName控制台会打印出obj.name。这样子类的所有实例对象就实现了对父类方法的继承。

原型链继承.png
在这个例子中,调用obj.sayName方法时,obj对象首先会在自身属性中检索sayName,自身属性中没有sayName时,通过obj.__proto__向原型链的上一层即构造函数的prototype中检索,此时还是找不到sayName,再此通过prototype.__proto__向上检索,终于在父类的原型对象中找到这个属性。

注意,构造函数的原型对象都是Object的实例,所以原型链的尽头是Object的原型对象,该对象的__proto__属性为Null。

在使用原型链实现继承时,要注意的是,定义引用类型的属性时,要在构造函数中定义而不是在原型对象中。

对象属性的枚举

1、for-in

for-in语句可以返回所有能通过对象访问的,可枚举(enumeratable = true)的属性,包括存在于实例和原型中的属性。
for-in.png
console forin.png

2、keys、values

Object.keys会把对象的所有可枚举的属性名封装成一个数组返回,不包括原型对象中的属性
Object.values把对象的所有可枚举的属性值封装成一个数组返回,不包括原型对象中的属性
keys.png
consolekeys.png

3、如何判断一个属性是在实例还是原型对象中

通过hasOwnProperty可以判断属性是在实例还是原型对象中,不包括不可枚举的属性
hasown.png


菜菜的电冰箱
15 声望0 粉丝

程序媛


下一篇 »
CSS-position