原型链
前言
最近在恶补 JS 基础,什么闭包、作用域链、词法环境、事件循环 balabala…人都补傻了…
大概这就是半路出家学前端的代价吧 QAQ。
今天先来把最近刚刚啃透的原型链知识梳理一下。
几个概念
这几个概念是理解原型链的基础,非!常!重!要!
万物皆为对象
在 JS 中,除了 null
、undefined
之外的所有数据类型都是对象。
没错, NaN
也是对象,NaN
本质上是 number
。
普通对象
就是对象(废话)。
普通对象都会拥有 __proto__
属性。
{}
是普通对象。var foo = new Foo()
,foo
是普通对象。"abc"
是普通对象。123
是普通对象。NaN
也是普通对象。
函数对象
就是函数。
函数对象都会拥有 prototype
属性,函数对象同时也是普通对象,所以也拥有 __proto__
属性。
function A() {}
是一个函数对象。String
、Number
、Object
、Function
等都是函数对象。
普通对象是函数对象的实例
下面这几个例子看一下,相信就能理解了
{}
是Object
的实例,我们平时看到的var obj = {}
其实等于var obj = new Object({})
foo
是function Foo() {}
的实例1
2function Foo() {}
var foo = new Foo();"abc"
是String
的实例123
是Number
的实例NaN
也是Number
的实例
prototype
prototype
即是对象的原型
__proto__
__proto__
串起原型形成了原型链,可以理解 __proto__
指向该对象继承的父对象
几个准则
总结出来的几个规律,能记住基本上吴迪!
准则一
普通对象的 __proto__
指向实例化它的函数对象的 prototype
。
所以:
__proto__
串起原型形成了原型链
例子
1 | function Foo() {} |
准则二
函数对象的原型上的 constructor
指向函数对象本身。
例子
1 | function Foo() {} |
准则三
函数对象都是 Function
类型的实例,包括 Object
,也包括 Function
本身。
例子
1 | function Foo() {} |
准则四
除了 Object
以外的函数对象的原型都继承于 Object
的原型
Object
的原型继承于 null
,即原型链的顶端为 null
例子
1 | function Foo() {} |
准则五
获取普通对象的属性时,若不能在普通对象本身上找到,则去原型链上获取。
即先从其 __proto__
上获取,若还是没有,则继续去其 __proto__.__proto__
上获取,直到原型链顶端或找到为止。
例子
foo
上并未定义 toString
方法,但是能拿到,是因为会在原型链上找。
1 | function Foo() {} |
test
未定义且原型链上也找不到该属性,所以会报错;
若在 Object
的原型上加上该属性的话,即可获取到,也证明了原型链的逻辑。
1 | foo.test; // Uncaught ReferenceError: foo is not defined |
准则六
所有某一对象的实例,共享同一个对象原型,对象原型是一个 Object
的实例;
已经创建的实例的 __proto__
引用不会改变;
若对象原型设置为了非引用类型,则新的实例的 __proto__
会直接指向 Object.prototype
。
例子
引用相同。
1 | function Foo() {} |
改变引用。
1 | Foo.prototype = { newTest: 1 }; |
设置原型为非引用类型。
1 | Foo.prototype = 'test'; |
总结
我个人就不总结了,直接上一张非常经典的图,这张图比任何总结都来的直观。
如果能利用上面的原理、准则过一遍,基本上就没问题了!