原型链
下图来自(juejin.cn/post/684490…)
原型链寻找属性顺序
- 寻找自身的属性挂载
- 通过
__proto__到达其构造函数的原型(Person.prototype)中寻找属性 - 通过
__proto__再到构造函数的原型的原型寻找属性 - 继续上一步直到null
注意:
- 如果未找到属性,属性值是undefined,原型链是终点的null
- 如果属性的值就是undefined,那就需要用hasOwnProperty('属性')来判断是否存在该属性(hasOwnProperty()只会对自身进行检查,并不会对原型链进行检查)
下一例子
function Person(){
}
p=new Person() // 执行[[prototype]]连接 ,即p.__proto__ = Person.prototype
Person.len =2
Person.prototype.number =345 // 为Person原型挂载属性
console.log(p.number) // 345
console.log(p.len) // undefined
console.log(Person.number) //undefined 寻找Person.__proto__,而不是Person.prototype
p.number 的寻找顺序
- 寻找自身的属性,没有找到
- 向原型寻找,即寻找
p.__proto__中的属性
只有prototype上的属性才会被继承
设置属性
给对象设置属性,会先执行上面的寻找属性
未找到该属性
如果没找到,就将该属性添加到这个对象
找到该属性
- 如果这个属性是在原型上(不是自身)并且是
普通数据访问属性(没有被标记已读),并且不是Setter,则会发生属性屏蔽,即将忽略原型上的该属性,将该属性和属性值挂载到自身的对象上,该属性就叫屏蔽属性
function Person(){
}
p=new Person() //
Person.prototype.number =345
console.log(p.number) // 345
p.number =2
console.log(Person.prototype.number) // 345
console.log(p.number) // 2
- 如果这个属性是在原型上但是是
只读属性,则无法对自身挂载新属性,也无法修改已有属性,默认忽略.在严格模式下就会报错
function Person(){
}
p=new Person() // 执行[[prototype]]连接 ,即p.__proto__ = Person.prototype
Object.defineProperty(Person.prototype,"number",{
value: 345,
writable: false // 设置只读
})
console.log(p.number) // 345
p.number =2;
console.log(p.number) // 345
- 如果这个属性在原型上但是是
Setter的,则就一定会调用这个Setter,不会添加新属性到对象上,也不会重新定义这个属性
function Person(){
}
p=new Person()
Object.defineProperty(Person.prototype,"number",{
get(){
return value
},
set(getValue){
value = getValue*2
}
})
p.number =2; //
console.log(p.number) // 4
console.log(Person.prototype.number) // 4
console.log(p.hasOwnProperty('number')) // false 本质上是给原型链上的number属性赋值4
console.log(p.__proto__.number===Person.prototype.number)// p.number是调用p.__proto__.number
instance
看下一经典例子
console.log(Object instanceof Object) // true
console.log(Function instanceof Function) // true
MDN:
instanceof运算符用于检测构造函数的prototype属性是否出现在某个实例对象的原型链
有点奇怪是吧, 结合上述例子Function的prototype属性出现在了自身的原型链上,怎么回事呢?下面逐一分析
(L)Object instanceof (R)Object
需要判断LObject 的 原型链中是否存在RObject的prototype(即Object.prototype),寻找只能对左值进行寻找
// 第一层寻找
console.log(Object.__proto__) // Function.prototype(Object的构造函数是Function)
console.log(Object.__proto__ === Function.prototype) // true
//第二层寻找
console.log(Object.__proto__.__proto__ === Object.prototy) // Object.prototype
console.log(Function.prototype.__proto__)
如上图,由于Object的构造函数是Funtion ,所以Object.__proto__为Function.__proto__,又因为Function.prototype.__proto__是Object.prototype,所以成立
(类型的构造函数都是Function? 所有没有显式new生成的函数的构造函数都为Function)
Function instanceof Function
同理,需要寻找左Function的原型链中是否存在Function.prototype
Function.__proto__等于Function.prototype
所以成立
fn instanceof fn
function fn(){
}
需要寻找左fn的原型链中是否存在fn.prototype
fn.__proto__为Function.prototype , fn.__proto__.__proto__等于Object.prototype ,fn.__proto__.__proto__.__proto__ 等于null
所以不成立
function fn(){
}
console.log(fn instanceof fn) //false
console.log(fn.__proto__=== Function.prototype) // true
console.log(fn.__proto__.__proto__ === Object.prototype) // true
console.log(fn.__proto__.__proto__.__proto__ === null) // true
对象关联
我们多用Object.create()来进行对象关联,接下来逐一分析
var foo={
showName :function(){
console.log('qianlon')
}
}
var anotherFoo =Object.create(foo) // 对象关联
anotherFoo.showName() // qianlon
console.log(anotherFoo.__proto__ === foo) // true
即Object.create本质是将右值(foo)添加到左值(anotherFoo)的原型链中,也就是上例中第7行anotherFoo.showName()本质上调用的是anotherFoo.__proto__.showName()
同样我们可以模仿实现Object.create()函数以做兼容性处理和底层了解
// Object.create() (ES5)
if(!Object.create){
Object.create=function(foo){
function fn={}
fn.prototype =foo
return new fn()
}
}