1.什么是原型(prototype)?
在JavaScript中,每一个对象实例和对象都包含一个原型空间(prototype),包含在对象中的Prototype对象中。
// 使用构造函数创建一个对象
function Person(name, age) {
this.name = name
this.age = age
this.sayHi = function () {
console.log('hello')
}
}
// 设置原型属性
Person.prototype.obj = new Object()
let p = new Person('海绵宝宝', 18)
let p2 = new Person('派大星', 20)
console.log(p)
console.log(p2)
原型的出现是为了解决构造函数添加方法的缺点,每次都开辟新的内存空间造成内存浪费。如果我们定义构造函数的时候将方法写在构造函数中,那么每次创建对象都要创建一个新的方法返回给实例对象。我们为什么不将方法写在原型中呢?我们可以在构造函数创建完成后将方法插入到原型中,因为new操作符帮我们在每次创建一个实例对象的时候将实例对象的prototype指向构造函数的prototype,这样实例对象和构造函数就能共享同一个sayHi()方法,就不用在构造函数里面写方法,避免每次创建实例对象都重复创建相同的方法,每次都开辟新的内存空间造成内存浪费。
// 使用构造函数创建一个对象
// 方法写在构造函数内,每次创建实例对象都开辟内存空间,造成浪费
function Person(name, age) {
this.name = name
this.age = age
// this.sayHi = function () {
// console.log('hello')
// }
}
// 将方法写入到构造函数的prototype上,每次创建实例对象的prototype都指向构造函数的prototype,不用每次都开辟内存空间
function Person(name) {
this.name = name
}
// 添加方法
Person.prototype.sayHi = () => {
console.log('hello')
}
// 添加属性
Person.prototype.age = 18
let p1 = new Person('海绵宝宝')
let p2 = new Person('派大星')
p1.sayHello()
p2.sayHello()
console.log(p2.age)
console.log(p2.age)
2.什么是原型链?
每一个实例对象都有一个prototype,prototype里面有一个_proto_指向上一层的创建对象,实例对象的__proto__和所属的构造函数上的prototype是同一个对象空间。
实例对象上的prototype里面__proto__就指向这个构造函数的prototype,构造函数的prototype的_proto_指向上一层的Object对象。
因为object是js中的顶级构造函数,我们有一句话叫做万物皆对象,所以object.prototype就到头了,object.prototype上的__proto__就是null。
原型链帮助我们在实例对象中找不到方法或者属性的是从原型上找,自己的原型找不到就到上一层的原型找,直到找到或者到达object.prototype上的__proto__,也就是null,还是没有找到就返回 undefined(属性)或者 not a function(方法)。
换句话说,原型链:从实例对象开始沿着__proto__向上形成的连式结构就是这个实例对象的原型链。
function Person(name) {
this.name = name
}
// 添加方法
Person.prototype.sayHello = () => {
console.log('Hello')
}
// 添加属性
Person.prototype.age = 18
let p1 = new Person('海绵宝宝')
let p2 = new Person('派大星')
p1.sayHello()
p2.sayHello()
// 实例对象里面没有age属性,就到原型里面找
console.log(p2.age) // 18
console.log(p2.gender) // 没找到,返回undefined
console.log(p2.gender()) // not a function
3.constructor
在对象的原型上还有一个constructor属性,这个属性显示了当前对象由谁创建出来的。