被面试官问烂了的JS原型,今天一次讲清楚
掌握 JavaScript 原型系统是成为 JS 高手的必经之路
前言
在 JavaScript 的世界中,原型(Prototype)是一个核心概念,但也是许多开发者的困惑之源。今天,我们将彻底揭开它的神秘面纱,让你对原型机制有清晰的理解!
一、原型 prototype(显示原型)
prototype就是函数的共享储物柜,所有实例都能从这里拿东西用,但谁都不能直接改柜子里的东西。
1. 函数的天生属性
每个 JavaScript 函数(除了箭头函数)在创建时都会自动获得一个 prototype 属性,这就是我们所说的显示原型。
2. 原型属性的访问机制
挂载在原型上的属性和方法可以被所有实例直接访问,就像它们是实例自身的属性一样。
3. 原型属性的不可直接修改性
实例对象无法直接修改构造函数原型上的属性值,这体现了原型的共享特性。
Person.prototype.say = '我太帅了'
function Person() {
this.name = '饶总'
}
const p = new Person()
p.say = 'hello'
const p2 = new Person()
console.log(p2.say);
二、对象原型 __proto__(隐式原型)
每一个对象都拥有一个 proto 属性,该属性值也是一个对象。 v8在访问对象中的一个属性时,会先访问该对象中的显示属性,如果找不到,就回去对象的隐式原型中查找。。 此外还有重要一点:实例对象的隐式原型 === 构造函数的显示原型 。看看下面代码
Grand.prototype.house = function() {
console.log('四合院');
}
function Grand() {
this.card = 10000
}
Parent.prototype = new Grand() // {card: 10000}.__proto__ = Grand.prototype.__proto__ = Object.prototype.__proto__ = null
function Parent() {
this.lastName = '张'
}
Child.prototype = new Parent() // {lastName: '张'}.__proto__ = Parent.prototype
function Child() {
this.age = 18
}
const c = new Child() // {age: 18}.__proto__ = Child.prototype
console.log(c.card);
当我们想从child上查找card上的余额时,发现child对象显示原型上没有card属性,于是去chlid对象的隐式原型也就是Parent的显示原型上查找,还是没有找到card,然后就再接着在Parent的隐式原型(就是Grand显示原型)上查找。
这一套的查找方式有个专属名词就叫做原型链。总的来说就是v8 在访问对象中的属性时,会先访问该对象中的显示属性,如果找不到,就去对象的隐式原型上找,如果还找不到,就去__proto__.proto 上找,层层往上,直到找到null为止。
三、new 操作符的幕后工作
new在这里面做了什么
- 创建一个空对象
- 让构造函数中的 this 指向这个空对象
- 执行构造函数中的代码 (等同于往空对象中添加属性值)
- 将这个空对象的隐式原型(proto) 赋值成 构造函数的显示原型(prototype)
- 返回该对象
Car.prototype.run = function() {
console.log('running');
}
function Car() { // new Function()
// const obj = {}
// Car.call(obj) // call 方法将 Car 函数中的 this = obj
this.name = 'su7' // 3
// obj.__proto__ = Car.prototype // 4
// return obj
}
const car = new Car() // {name: 'su7'}.__proto__ == Car.prototype
car.run()
这段代码中,car.run() 能够成功执行是因为 new 操作符在背后建立了一条原型链连接。当使用 new Car() 时,v8 引擎会创建一个新对象,将这个对象的 __proto__ 指向 Car.prototype,然后执行构造函数中的代码为对象添加属性,最终返回这个新对象。这样,car 实例就能通过原型链访问到 Car.prototype 上定义的 run 方法。
四、总结与最佳实践
核心要点回顾
- prototype:函数的显示原型,用于共享方法和属性
__proto__:对象的隐式原型,形成原型链的基础- new 操作符:创建实例并建立原型关系
- 原型链:属性查找的机制,实现继承的基础