大师网-带你快速走向大师之路 解决你在学习过程中的疑惑,带你快速进入大师之门。节省时间,提升效率

关于原型你需要知道的几个问题

为了良好的代码高亮阅读体验,建议您查看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

demo

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'

参考文档

  1. 详解prototype与 __proto__ 区别
  2. 一张图理解prototype、__proto__ 和constructor的三角关系

我的公众号:每周一三五七晚上八点定时更新,关于技术,关于生活的点滴~

我的公众号