首先咱们须要明确两点:git
1️⃣__proto__
和constructor
是对象独有的github
2️⃣prototype
属性是函数独有的浏览器
所以,prototype 描述的是两个对象之间的某种关系(其中一个,为另外一个提供属性访问权限)。bash
下面经过一个例子来讲明:函数
function Demo() {}
console.log(Demo.prototype.constructor === Demo) // true
console.dir(Demo.prototype)
复制代码
能够看到就是Demo函数对象的prototype原型是右边这个对象,那么Demo.prototype原型上有个constructor属性,这个属性正好指向Demo函数自己。post
全部你能够理解成:性能
A的显示原型是B,则有:
A.prototype === B
B.constructor === A
复制代码
我以为这样子惟一的好处在于你能够找到我,我也能够找到你。好滑稽ui
__proto__
和prototype关系再次强调 :this
1️⃣__proto__
和constructor
是对象独有的。2️⃣prototype
属性是函数独有的spa
关于更多__proto__
更深刻的介绍,能够参看工业聚大佬的《深刻理解 JavaScript 原型》一文。
__proto__
, 及隐式原型(属性)**怎么理解呢?咱们经过内存结构图来看看吧
function Demo() {}
Demo.prototype.say = () => { //给原型添加say方法
console.log("hello world")
}
console.log(Demo.prototype.say)
let fn = new Demo();
fn.say(); // 怎么找到say方法的呢?
console.log(fn.__proto__ === Demo.prototype) // true
复制代码
咱们从图中能够看到,Demo函数的原型跟它构造函数(Demo)建立的实例fn.__proto__
指向同一个对象。
那么fn是怎么找到say方法的呢?
更加具体的说就是经过隐式原型__proro__
找到的,分析以下:
__proro__
属性对应的原型那么是否是能够更加准确的说明:实例是经过隐式原型__proto__
查找须要调用的属性的,那么咱们经过接下来的代码去验证一下。
代码:
function Demo() {}
Demo.prototype.say = () => { //给原型添加say方法
console.log("hello world")
}
Demo.prototype.name = 'old name'
let fn = new Demo();
fn.say(); // 怎么找到say方法的呢?
console.log(fn.name)
console.log("为修改前",fn.__proto__ === Demo.prototype) // true
console.log("-------接下来修改fn的__proto__")
fn.__proto__ = {
say: () => {
console.log("hello 隐式原型")
},
name : 'new name'
}
console.log("修改实例中的隐式原型",fn.__proto__ === Demo.prototype) // true
console.log(fn.name)
fn.say()
console.log("从新建立一个Demo构造函数实例")
let demo1 = new Demo();
console.log(Demo.prototype === demo1.__proto__)
demo1.say()
复制代码
首先的说明的是:
经过查阅相关的文档,ES6以前不能直接操做隐式原型,也不推荐你这么作。
经过修改fn的隐式原型,让它指向一个新的对象。那么fn.proto 不等于Demo.prototype. 这个例子也能证实一点,实例对象调用属性时,实例对象不具备该属性时,是经过隐式原型去找的该属性的,找不到的话,在它的隐式原型对象的隐式原型对象上找。
这也就是咱们常说的,在原型上添加属性或者方法,实例能够共享,缘由就在于咱们并不推荐去修改实例的__proto__
属性,这样子也就是会有一下结果:
function Demo() {
// 内部语句 this.prototype = {}
}
let fn = new Demo(); // 内部语句: fn.`__proto__` = Demo.prototype
// 实例化一个对象隐式原型会默认赋值: fn.__proto__ = Demo.prototype
// 定义函数时: 显式原型也会默认添加: Demo.prototype = new Object()
复制代码
这里咱们须要知道的是,__proto__
是对象所独有的,而且__proto__
是一个对象指向另外一个对象,也就是他的原型对象。咱们也能够理解为父类对象。它的做用就是当你在访问一个对象属性的时候,若是该对象内部不存在这个属性,那么就回去它的__proto__
属性所指向的对象(父类对象)上查找,若是父类对象依旧不存在这个属性,那么就回去其父类的__proto__
属性所指向的父类的父类上去查找。以此类推,知道找到 null
。而这个查找的过程,也就构成了咱们常说的原型链。
那什么是原型呢?你能够这样理解:每个JavaScript对象(null除外)在建立的时候就会与之关联另外一个对象,这个对象就是咱们所说的原型,每个对象都会从原型"继承"属性。
函数的prototype属性:在定义函数时自动添加prototype,默认是一个空Object对象
对象的__proto__
属性:建立一个对象实例时,默认值是构造函数的prototype属性值,也就是上面所说的
实例的构造函数属性(constructor)指向构造函数
通常而言,能够直接操做显式原型,不能直接操做隐式原型(ES6)
更多规范,移步MDN
Object
和Function
的鸡和蛋的问题**最后总结: ** 先有Object.prototype(原型链顶端),Function.prototype继承Object.prototype而产生,最后,Function和Object和其它构造函数继承Function.prototype而产生。
使用__proto__
是有争议的,也不鼓励使用它。由于它历来没有被包括在ECMAScript语言规范中,可是现代浏览器都实现了它。__proto__
属性已在ECMAScript 6语言规范中标准化,用于确保Web浏览器的兼容性,所以它将来将被支持。它已被不推荐使用, 如今更推荐使用Object.getPrototypeOf
/Reflect.getPrototypeOf
和Object.setPrototypeOf
/Reflect.setPrototypeOf
(尽管如此,设置对象的[[Prototype]]是一个缓慢的操做,若是性能是一个问题,应该避免)。
proto 属性也能够在对象文字定义中使用对象[[Prototype]]来建立,做为Object.create()
的一个替代。
a prototype may have a non-null implicit reference to its prototype, and so on; this is called the prototype chain.
如上,在 ECMAScript 2019 规范里,只经过短短的一句话,就介绍完了 prototype chain
原型链的概念,仅仅是在原型这个概念基础上所做的直接推论。
既然 prototype 只是刚好做为另外一个对象的隐式引用的普通对象。那么,它也是对象,也符合一个对象的基本特征。
每一个对象均可以有一个原型_proto_,这个原型还能够有它本身的原型,以此类推,
造成一个原型链。查找特定属性的时候,咱们先去这个对象里去找,
若是没有的话就去它的原型对象里面去,
若是仍是没有的话再去向原型对象的原型对象里去寻找......
这个操做被委托在整个原型链上,这个就是咱们说的原型链了。
复制代码
__proto__
是原型链查询中实际用到的,它老是指向 prototype
prototype 是函数所独有的**,**在定义构造函数时自动建立,它老是被 proto 所指。
全部对象都有__proto__属性,函数这个特殊对象除了具备__proto__属性,还有特有的原型属性prototype。prototype对象默认有两个属性,constructor属性和__proto__属性。prototype属性能够给函数和对象添加可共享(继承)的方法、属性,而__proto__是查找某函数或对象的原型链方式。constructor,这个属性包含了一个指针,指回原构造函数。