为了良好的代码高亮阅读体验,建议您查看github原文。
前言
prototype是js面向对象的一个重要机制,于是总结了以下几个问题,理解起来会比较有针对性。
1. prototype
和 __proto__
的关系是什么?
prototype
是只有函数才会有的属性;而__proto__
是所有对象都有的属性。
几乎所有的函数都有一个prototype
属性,prototype
上挂载的所有属性和方法都可以被这个函数的实例继承。
对于
function Foo(){}
const foo=new Foo()
// 注意这里:无论在实例化前后在prototype上添加属性实例都会继承
Foo.prototype.isTestable=true
console.log(foo.isTestable) // true
有
Foo.prototype===foo.__proto__ //true
foo.constructor===Foo // true
Foo.constructor===Function // true
Foo===foo.__proto__.constructor // true
是不是看起来很乱?祭上我多年收藏的此图!!OVO
2. 自有属性和原型属性又是什么?
上面的例子中,在调用foo.isTestable
时,先会在实例上查询是否有isTestable
这个属性;如果没有找到,再往它的__proto__
上查询这个属性;直至查到底层没有则返回undefined
。
function Foo(){}
const foo=new Foo()
Foo.prototype.isTestable=true
console.log(foo.isTestable) // true
那么如何区分某个对象的属性时其自身的还是继承的呢?我们就需要hasOwnProperty()
这个方法来确认。
其中isTestable
这个属性对于foo
来说就是原型上的属性,所以它返回false
foo.hasOwnProperty('isTestable') // false
如果我们在foo
上直接添加isTestable
这个属性,那么他与__proto__
上的isTestable
是否冲突?
function Foo(){}
const foo=new Foo()
foo.isTestable=false
Foo.prototype.isTestable=true
console.log(foo.hasOwnProperty('isTestable')) // true
console.log(foo.isTestable) // false
console.log(foo.__proto__.isTestable) // true
// 删除实例上的isTestable属性
delete foo.isTestable
console.log(foo.hasOwnProperty('isTestable')) // false
console.log(foo.isTestable) // true 这时又从__proto__上找到isTestable了
// 来,我们把__proto__上面的也删掉
delete foo.__proto__.isTestable
console.log(foo.isTestable) // false, isTestable终于没了
当然hasOwnProperty()
只能知道某个属性是否在实例上,如果我们想要知道某个属性是否在__proto__
上,就需要自己写一个函数:
const hasPrototypeProperty=(obj,key)=>(key in obj)&&(!obj.hasOwnProperty(key))
3. 每个实例之间的关系是什么?
对于以下由一个构造函数创建的实例
function Foo(){}
const foo1=new Foo()
const foo2=new Foo()
有下列关系
foo1==foo2 // false,这是因为在这里都指向不同的内存,{}==={} // false
JSON.stringify(foo1)===JSON.stringify(foo2) // true
foo1.constructor===foo2.constructor // true 这里的构造函数都指向Foo,所以为true
foo1.__proto__===foo2.__proto__ // 当然这个也为true
foo1.__proto__===Foo.prototype // true
Object.getPrototypeOf(foo1)===Object.getPrototypeOf(foo2) // true
4. 在构造函数中添加属性后实例怎么继承
对于在构造函数内部添加的属性,它的实例会继承它的属性;
function Foo(){this.name='foo'}
Foo.age=10
const foo1=new Foo()
然而直接挂载在Foo上的属性不会在实例上被继承;
foo1.name // 'foo'
foo1.age // undefined
如果实例上和原型上都挂载了同样的属性,会优先从实例上获取:
function Foo(){this.name='foo'}
const foo1=new Foo()
foo1.name='ahaha'
foo1.name //'ahaha'
参考文档
- 详解prototype与
__proto__
区别 - 一张图理解prototype、
__proto__
和constructor的三角关系
我的公众号:每周一三五七晚上八点定时更新,关于技术,关于生活的点滴~