相信你们确定见过下面这几个单词,可是有时候又是傻傻分不清楚,不知道这几个单词究竟是作啥的,又有啥区别.今天咱们就来好好的瞧一瞧,剖析剖析它们.它们分别是
prototyoe
,__proto__
,constructor
.javascript
在JavaScript中,每个对象(除了null)在建立的时候都会与之关联另外一个对象,这另外一个对象就是咱们所说的原型,每个对象都会从原型"继承"属性.这里的继承并非真继承.由于继承意味着复制操做,然而JavaScript默认并不会复制对象的属性.这里只是在两个对象之间创建一个关联.这样,一个对象就能经过委托访问另外一个对象的方法或属性. 全部函数都有prototype
属性,称之为原型属性(也叫显式原型),而普通对象是没有prototype
的. 虽然函数也是对象,可是咱们这里的普通对象显然是不包含函数的.java
console.log(({}).prototype) // undefined
console.log((function(){}).prototype) // {constructor: ƒ}
复制代码
其实,这个prototype
属性指向一个对象,这个对象就是调用该构造函数而建立的实例的原型,其中包含了一些属性
和方法
,而这些属性
和方法
可让全部的实例共享访问.bash
function Person(name){
this.name = name
}
Person.prototype.say = function(){
console.log('Hello World')
}
let p = new Person('zhangsan')
let p2 = new Person('lisi')
console.log(p.say === p2.say) // true
复制代码
Person
是个函数,经过构造调用的方式生成了p
和p2
两个对象.而Person
的原型对象上又定义了一个say
方法,从运行结果看,p
和p2
两个实例对象均可以调用say
方法输出Hello World
,而且p.say === p2.say
的结果为true
也证实了它们是共享访问. 下面咱们来看关系图: 函数
在JavaScript中,全部对象(除了null)都会有一个内部属性__proto__
(也叫隐式原型),该属性指向被构造调用函数的原型对象ui
function Person(){}
let p = new Person()
console.log(p.__proto__ === Person.prototype)
复制代码
由此,咱们又能够在关系图上面再添加点东西:从实例指向原型对象 this
上面也说到了,全部对象(除了null)都有__proto__
,那么Person.__proto__
又指向什么呢?咱们在上面的代码后面添加以下spa
console.log(Person.__proto__ === Function.prototype) // true
复制代码
这是由于函数也是一种对象,能够认为Person
函数是调用了内置构造函数Function
后生成的实例,因此上面的结果为true
咱们再来看下面代码prototype
let num = new Number(1)
console.log(num.__proto__ === Number.prototype) // true
let str = new String('Hello')
console.log(str.__proto__ === String.prototype) // true
let bool = new Boolean(true)
console.log(bool.__proto__ === Boolean.prototype) // true
let f = new Function()
console.log(f.__proto__ === Function.prototype) // true
console.log(Function.__proto__ === Function.prototype) // true 这个我目前还不太清楚是为何 QAQ
复制代码
能够得出一个结论:3d
let o = new F()
o.__proto__ === F.prototype
复制代码
或者:code
o.__proto__ === o.constructor.prototype // 这个缘由在后面的constructor中会提到
复制代码
注意: 使用__proto__
是有争议的,所以不鼓励使用它,如今更推荐使用Object.getPrototypeOf/Reflect.getPrototypeOf
.参考地址:MDN
默认状况下,每一个原型对象都会自动得到一个constructor
属性,指向其关联的构造函数.
function Person(){}
console.log(Person.prototype.constructor === Person)
let p = new Person()
// 使用 getPrototypeOf 方法还能获取对象的原型
console.log(Object.getPrototypeOf(p) === Person.prototype) // true
复制代码
咱们再来看下面代码
function Person(){}
let p = new Person()
console.log(p.constructor === Person) // true
复制代码
实例p
并无constructor
属性,但它从原型Person.prototype
中去读取,所以上面的输出结果为true
.此时再回过头去看o.__proto__ === o.constructor.prototype
是否是就明白为何了.
由此,咱们又能够在关系图上面再添加点东西:从原型对象指回被构造调用的函数
当访问一个对象的方法或属性时,若找不到则会查找与该对象关联的原型中的方法(属性),若仍是找不到,则继续向上去原型的原型中查找,直到找到为止,就这样一直到最顶层.能够理解__proto__
一层一层的指向就是原型链了.
function Person(){}
Person.prototype.say = function(){
console.log('Hello World')
}
let p = new Person()
console.log(p.__proto__ === Person.prototype) // true
console.log(Person.prototype.__proto__ === Object.prototype) // true 由于全部的对象都是直接或间接继承自Object
console.log(Object.prototype.__proto__) // null 由于Object做为最顶层对象,是原型链的最后一环.因此这里的null表示了Object.prototype没有原型
复制代码
咱们在上面的代码后面继续添加以下代码:
Object.prototype.say = function(){
console.log('Hi Universe')
}
Object.prototype.shout = function(){
console.log('Hello Universe')
}
p.say() // Hello World
p.shout() // Hello Universe
复制代码
实例对象p
自身没有say
和shout
方法,向原型查找,在Person原型对象中找到了say
,就再也不继续向上查找,而shout
方法不在Person原型对象中,所以继续向上查找.最终在Object原型对象中找到了shout
,发出了Hello Universe
的呐喊 此时咱们的关系图变成了以下:
function Person(){}
console.log(Person.__proto__ === Function.prototype) // true
console.log(Object.__proto__ === Function.prototype) // true
复制代码
Person
和Object
,它们一个是自定义函数,另一个是内置构造函数,都算是函数实例,所以也是由Function
构造生成. 最后,嘿嘿嘿,给你们出一道题目,看看你们知不知道为何
console.log(Function instanceof Object)
console.log(Object instanceof Function)
复制代码
总结: 有关原型和原型链的内容就暂时讲到这里了,相信你们对这几个东西应该都有所了解了.确实,这块内容中各类指来指去,关系图中也是箭头满天飞,还须要你们好好消化一下.