一直以来,js的原型和原型链这个概念性的问题一直讲不清,今天看了一篇较好的文章跟着走一遍之后发现有那么点明白了,下面是根据自己的理解再结合网上的文章做出的一些总结,有问题的地方随时纠正:smile:
四个原则
- 引用类型都具有对象特性, 即可自由扩展属性.
- 引用类型都有一个隐式原型
__proto__
属性, 属性值是一个普通的对象 - 引用类型,隐式原型
__proto__
的属性值指向它的构造函数的显示原型prototype
属性值 - 当你试图得到一个对象的某个属性时,如果这个对象本身没有这个属性, 那么它会去它的隐式原型
__proto__
(也就是它的构造函数的显示原型prototype
)中寻找
引用类型: Object,Array,Function,Date,RegExp. 这里我们姑且称
__proto__
为隐式原型
规则一
引用类型,都具有对象属性,即可自由扩展属性:
js
const obj = {};
const arr = [];
const fn = function() {}
obj.a = 1;
arr.a = 1;
fn.a = 1;
console.log(obj.a) // 1
console.log(arr.a) // 1
console.log(fn.a) // 1
规则二
引用类型都有一个隐式原型__proto__
属性, 这个属性值是一个普通的对象
js
const obj = {}
const arr = []
const fn = function() {}
console.log(obj.__proto__)
console.log(arr.__proto__)
console.log(fn.__proto__)
规则三
引用类型, 隐式原型__proto__
的属性值指向它的构造函数的显示原型prototype
的属性值
js
const obj = {}
const arr = []
const fn = function() {}
obj.__proto__ === Object.prototype // true
arr.__proto__ === Array.prototype // true
fn.__proto__ === Funtion.prototype // true
规则四
当你试图得到一个对象的某个属性时, 如果这个对象本身没有这个属性,那么它会去他的隐式原型__proto__
(也就是它的构造函数的显示原型prototype
)中寻找
js
const obj = {a: 1}
obj.toString
// ƒ toString() { [native code] }
首先, obj
对象并没有toString
属性, 之所以能够获取到toString
属性, 是遵循了第四条规则,从它的构造函数Object
的prototype
中去获取
一个特征
js
function Person(name) {
this.name = name;
// return this;
}
const nick = new Person('nick');
nick.toString
// ƒ toString() { [native code] }
按理说, nick
是 Person
构造函数生成的实例, 而 Person
的prototype
并没有toString
方法, 为什么nick
能获取toString
方法呢?
这里就引出了原型链
的概念了,nick
实例先从自身出发检讨自己, 发现并没有toSTring
方法.找不到就一直往上走,找Person
构造函数的prototype
属性,还是没找到.而prototype
也是一个对象嘛,那么对象的构造函数就是Object
,所以就找到了Object.prototype
下的toSting
方法.这种寻找的过程就形成了原型链的概念, 这就是我理解的原型链的一个过程.
Javascript中万物皆对象.从上述情况来看,好像是这么个道理
一张图片(原型链图解)
最后一个null
,设计上是为了避免死循环而是设置的, Object.prototype
的隐式原型(Object.prototype.__proto__
)指向null
一个方法
instanceof
运算符用于测试构造函数的prototype
属性是否出现在对象原型链中的任何位置.
instanceof
的简易手写板如下
js
// 变量R的原型 存在于 变量L 的原型链上
function instance_of (L, R) {
// 验证如果为基本数据类型就直接返回false
const baseType = ['string', 'number', 'boolean', 'undefined', 'symbol'];
if (baseType.includes(typeof(L))) {
return false;
}
// 取R的显示原型
let RP = R.prototype;
// 取L的隐式原型
L = L.__proto__;
while(true) {
// 找到最顶层
if (L === null) {
return false
}
// 严格相等
if (L === RP) {
return true;
}
// 没找到继续向上一下原型链查找
L = L.__proto__;
}
}
我们再来看下面这段代码:
js
function Foo(name) {
this.name = name
}
var f = new Foo('nick')
f instanceof Foo // true
f instanceof Object // true
上述代码判断流程大致如下:
f instanceof Foo
:f
的隐式原型__proto__
和Foo.prototype
是相等的,所以返回true
f instanceof Object
:f
的隐式原型__proto__
和Object.prototype
不等, 所以继续往上走,f
的隐式原型__proto__
指向Foo.prototype
, 所以继续使用Foo.prototype.__proto__
去对比Object.prototype
, 这会就相等了, 因为Foo.prototype
就是一个普通对象
再次验证万物皆对象
总结
通过四个特征,一个例子,一张图片,一个方法,应该对原型和原型链的关系有了大概的认证,我的认知就是: 原型链就是一个过程,原型就是原型链这个过程中的一个单位,贯穿整个原型链.