重学javaScript (十九)|谈谈原型吧

134 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第20天,点击查看活动详情

前言

原型应该分为对象中的原型和函数中的原型

对象中的原型

js的每个对象都会有一个隐藏的特殊的内置属性[[prototype]],我们将它称之为隐式原型,这个属性指向了一个特殊的对象。

这个时候,你可能有以下两个疑惑

  • 我们怎么访问这个隐式属性呢
  • 它有啥用

访问这个隐式属性

  • proto: 为了使我们访问到这个属性浏览器和node帮我们在对象上加上了这个属性,可以访问到[[prototype]]
  • Object.getPrototypeOf() : 可以通过这个方法访问
let obj = {}
console.log(obj.__proto__)
console.log(Object.getPrototypeOf(obj))

image.png

实际上我们在查找对象的属性的时候是这么个过程

  • 查询对象本身的属性
  • 查询对象隐式原型的属性
  • 然后按照作用域链依次查找
let obj = {}
console.log(obj.name)
// 给原型上添加name属性
obj.__proto__.name = 'kaka'
console.log(obj.name)

image.png

函数中的原型

函数也是对象,因此它也有隐式原型

function foo () {

}
console.log(foo.__proto__)

image.png

因为它是函数,所以它还会多出来个显示原型的属性 prototype

function foo () {

}
console.log(foo.prototype)

image.png

看下new操作符

  1. 在内存中创建一个空对象
  2. 将这个空对象的内置属性[[prototype]]赋值给构造函数的prototype属性
  3. 这个新对象会被绑定到函数调用的this上(this绑定在这个时候完成)
  4. 执行函数体
  5. 如果函数没有返回其他对象,函数会返回这个新对象

我们重点来模拟下第2步

function foo () {
    // 模拟
    let obj = {}
    // 将新对象的隐式原型,赋值给foo的prototype(显式原型)
    obj.__proto__ = foo.prototype

    return obj
}

从上边的代码可以看出来

function obj () {
  
}

let kaka =  new obj()
let roaldo = new obj()

console.log(kaka.__proto__ === roaldo.__proto__)

对象kaka和roaldo的__proto__是相等的

image.png

函数原型在内存中的表现

以上边的例子为例

先说结论

  • kaka和roaldo的原型指向函数obj的原型对象
  • 函数obj的原型对象还有个constructor指向函数obj本身

image.png

大家一定好奇,原型对象上的construtor是什么,我们可以使用对象的getOwnPropertyDescriptors来获取

image.png

可以看出

  • 直接使用obj.prototype在nodejs里是个空对象
  • 使用getOwnPropertyDescriptors来拿,可以看到constructor这个属性,由于enumerable是false,所以直接看不到
  • 可以看出来constructor的value是函数obj

我们再来做个测试

function obj () {
 
}

let kaka =  new obj()
let roaldo = new obj()


console.log(obj.prototype.constructor === obj)
console.log(kaka.__proto__.constructor === obj)
console.log(roaldo.__proto__.constructor === obj)

image.png