共计 1732 个字符,预计需要花费 5 分钟才能阅读完成。
原型对象
原型对象(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 能够判断属性是在实例还是原型对象中,不包含不可枚举的属性