最近又很多同学在小猿圈学习群里讨论原型和原型链的问题,看大家讨论的热火朝天,但是并没有讨论出很准确的答案,今天小猿圈就给大家分享一下如何理解原型和原型链
JavaScript的特点
JavaScript是一门直译式脚本语言,是一种动态类型、基于原型的语言。 JavaScript的灵活性不亚于C++,你可以使用JavaScript尝试不同的程序设计范型。
比如类jQuery风格的函数式编程、基于过程的指令式编程、以及基于原型的面向对象编程。
不同于Java、C#等面向对象语言,JavaScript采用基于原型的继承方式。
为啥会有原型和原型链?
1994年,网景公司(Netscape)发布了Navigator浏览器0.9版,但是刚开始的Js没有继承机制,更别提像同时期兴盛的C++和Java这样拥有面向对象的概念。在实际的开发过程中,工程师们发现没有继承机制很难解决一些问题,必须有一种机制能将所有的对象关联起来。
Brendan Eich鉴于以上情况,但不想把Js设计得过为复杂,于是引入了new关键词和constructor构造函数来简化对象的设计,引入了prototype函数对象来包含所有实例对象的构造函数的属性和方法,引入了proto和原型链的概念解决继承的问题。
原型模式
[if !supportLists]· [endif]每个函数都有一个prototype(原型)属性
[if !supportLists]· [endif]这个属性都有一个指针,指向一个对象
[if !supportLists]· [endif]这个对象包含由特定类型所有实例共享的属性和方法
[if !supportLists]· [endif]使用原型的好处是 可以让所有对象实例共享它包含的方法和属性
通过in操作符和hasOwnProperty来判断给定属性是来自于原型还是实例
in- true 代表属性在对象中存在 来自实例或者来自原型
hasOwnProperty- true代表属性来自于实例 是实例属性
原型链
ECMAScript中只支持实现继承,而且是通过原型链的方式来实现的。所以原型链是JavaScript实现继承的一种重要方式。
用户定义类型的原型链
我们一般如何来检查JavaScript的变量数据类型?一般我们都是通过instanceof关键字,可以基于原型链来检测变量的类型。
我们可以先构造一个原型链,再用instanceof来检测类型:
[if !vml]
[endif]
[if !vml]
[endif]
由上面讲的instanceof的结果,可以判断这些类型的继承层级:
[if !vml]
[endif]
事实上instanceof是通过原型链来检测类型的,例如L
instanceof R: 如果R.prototype出现在了L的原型链上则返回true,否则返回false。
用JavaScript来描述instanceof的实现逻辑是这样的:
[if !vml]
[endif]
JavaScript原型链
先给大家看一个JavaScript的原型链结构图。
[if !vml]
[endif]
悄悄告诉你理解原型链的小技巧: 将__proto__箭头视作泛化(子类到父类)关系!
那么图中所有的虚线将构成一个继承层级,而实线表示属性引用。
图中给出了Object.prototype.__proto__ == null,但它还没有标准化,在Chrome、Safari和Node.js下它是不同的东西。
但可以看到JavaScript中所有对象的共同隐式原型为Object.prototype,它的上一级隐式原型是什么已经不重要了,因为它不会影响所有内置对象以及用户定义类型的原型链结构。
上图其实已经解释了不同内置对象instanceof的行为,我们来看Function和Object的特殊之处:
[if !supportLists]1. [endif]Object是由Function创建的:因为Object.__proto__ === Funciton.prototype;
[if !supportLists]2. [endif]同理,Function.prototype是由Object创建的;
[if !supportLists]3. [endif]Funciton是由Function自己创建的!
[if !supportLists]4. [endif]Object.prototype是凭空出来的!
现在我们可以解释特殊对象的instance行为了:
[if !vml]
[endif]
另外可以看到当你声明一个函数(比如Animal)时,Animal.prototype会自动被赋值为一个继承自Object的对象, 而且该对象的constructor等于Animal。即:
[if !vml]
[endif]
值得注意的是Animal如果被Cat继承,Cat实例(比如cat)的constructor仍然是Animal。
[if !vml]
[endif]
总结
1.每个函数对象都有一个 prototype 属性,这个属性就是函数的原型对象。
2.原型链是JavaScript实现继承的重要方式,原型链的形成是真正是靠__proto__ 而非prototype。
不知道同学们有没有明白呢?今天的分享就到这里了,希望大家要收藏或者记到小本本上哦,吐过还有不懂得可以来小猿圈找我哦,小猿圈-IT自学人的小圈子。