持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第20天,点击查看活动详情
前言
原型应该分为对象中的原型和函数中的原型
对象中的原型
js的每个对象都会有一个隐藏的特殊的内置属性[[prototype]],我们将它称之为隐式原型,这个属性指向了一个特殊的对象。
这个时候,你可能有以下两个疑惑
- 我们怎么访问这个隐式属性呢
- 它有啥用
访问这个隐式属性
- proto: 为了使我们访问到这个属性浏览器和node帮我们在对象上加上了这个属性,可以访问到[[prototype]]
- Object.getPrototypeOf() : 可以通过这个方法访问
let obj = {}
console.log(obj.__proto__)
console.log(Object.getPrototypeOf(obj))
实际上我们在查找对象的属性的时候是这么个过程
- 查询对象本身的属性
- 查询对象隐式原型的属性
- 然后按照作用域链依次查找
let obj = {}
console.log(obj.name)
// 给原型上添加name属性
obj.__proto__.name = 'kaka'
console.log(obj.name)
函数中的原型
函数也是对象,因此它也有隐式原型
function foo () {
}
console.log(foo.__proto__)
因为它是函数,所以它还会多出来个显示原型的属性 prototype
function foo () {
}
console.log(foo.prototype)
看下new操作符
- 在内存中创建一个空对象
- 将这个空对象的内置属性[[prototype]]赋值给构造函数的prototype属性
- 这个新对象会被绑定到函数调用的this上(this绑定在这个时候完成)
- 执行函数体
- 如果函数没有返回其他对象,函数会返回这个新对象
我们重点来模拟下第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__是相等的
函数原型在内存中的表现
以上边的例子为例
先说结论
- kaka和roaldo的原型指向函数obj的原型对象
- 函数obj的原型对象还有个constructor指向函数obj本身
大家一定好奇,原型对象上的construtor是什么,我们可以使用对象的getOwnPropertyDescriptors来获取
可以看出
- 直接使用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)