看了10篇文章,我写下了这篇有关原型和原型链的笔记

相信你们确定见过下面这几个单词,可是有时候又是傻傻分不清楚,不知道这几个单词究竟是作啥的,又有啥区别.今天咱们就来好好的瞧一瞧,剖析剖析它们.它们分别是 prototyoe, __proto__, constructor.javascript

prototype

在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是个函数,经过构造调用的方式生成了pp2两个对象.而Person的原型对象上又定义了一个say方法,从运行结果看,pp2两个实例对象均可以调用say方法输出Hello World,而且p.say === p2.say的结果为true也证实了它们是共享访问. 下面咱们来看关系图: 函数

在这里插入图片描述

proto

在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

默认状况下,每一个原型对象都会自动得到一个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自身没有sayshout方法,向原型查找,在Person原型对象中找到了say,就再也不继续向上查找,而shout方法不在Person原型对象中,所以继续向上查找.最终在Object原型对象中找到了shout,发出了Hello Universe的呐喊 此时咱们的关系图变成了以下:

在这里插入图片描述

补充

function Person(){} 
console.log(Person.__proto__ === Function.prototype)  // true
console.log(Object.__proto__ === Function.prototype)  // true
复制代码

PersonObject,它们一个是自定义函数,另一个是内置构造函数,都算是函数实例,所以也是由Function构造生成. 最后,嘿嘿嘿,给你们出一道题目,看看你们知不知道为何

console.log(Function instanceof Object)
console.log(Object instanceof Function)
复制代码

总结: 有关原型和原型链的内容就暂时讲到这里了,相信你们对这几个东西应该都有所了解了.确实,这块内容中各类指来指去,关系图中也是箭头满天飞,还须要你们好好消化一下.

相关文章
相关标签/搜索