1.prototype属性(原型对象)
在ES6之前,对象不是基于类创建,而是使用构造函数。
每一个构造函数都有prototype属性,指向另一个对象,这个对象的所有属性和方法都会被构造函数所拥有。故对于实例化对象,可以利用构造函数的prototype属性实现方法的共享。
举个栗子:
function Person(name) {
this.name = name
}
Person.prototype.sing = () => {
console.log("singing...")
}
let p1 = new Person('小明')
let p2 = new Person('小红')
console.log(p1)
console.log(p2)
console.log(p1.sing === p2.sing)
执行结果:
可以看到实例化的两个对象都挂载了sing方法,且引用的sing方法是同一个。
对比一下,若是将sing方法写在构造函数中:
function Person(name) {
this.name = name
this.sing = function() {
console.log("singing...")
}
}
let p1 = new Person('小明')
let p2 = new Person('小红')
console.log(p1)
console.log(p2)
console.log(p1.sing === p2.sing)
执行结果:
可以看到实例化的两个对象都挂载了sing方法,但引用的sing方法不是同一个。因此,若是共享的方法,应挂载在构造函数的prototype属性上,减少内存的浪费。
2.__proto__属性(对象原型)
每一个对象都会有一个__proto__属性,指向其构造函数的prototype原型对象。
function Person() {}
let p = new Person()
console.log(p.__proto__ === Person.prototype) // true
因为prototype也是对象,所以也有__proto__属性,指向其构造函数的prototype原型对象。
function Person() {}
console.log(Person.prototype.__proto__ === Object.prototype) // true
3.constructor属性
原型对象和对象原型都有个默认属性constructor,默认指向构造函数。
原型对象prototype:
function Person() {}
console.log(Person.prototype.constructor === Person) // true
前面提到了每一个构造函数都有prototype属性,而constructor属性在prototype对象上面,故其可以被所有实例对象继承。
function Person() {}
let p = new Person()
console.log(p.constructor === Person) // true
console.log(p.constructor === Person.prototype.constructor) // true
对象原型__proto__:
function Person() {}
let p = new Person()
console.log(p.__proto__.constructor === Person) // true
4.原型链
因每一个对象都有自己的原型__proto__,指向其构造函数的prototype原型对象,而原型本身也是对象,于是就形成了一个链式结构,叫做原型链
原型链:对象的原型=>原型的原型=>原型的原型的原型…
以上面的例子为例:p=>Person.prototype=>Object.prototype=>null
整合上面的关系,用图来表示如下: