在JavaScript世界中,一切都可以看做对象。与对象关系最密切的有两个概念:原型和原型链。
原型
在JavaScript世界中,原型具体可以分为两种:
- 隐式原型
- 显示原型
隐式原型
JavaScript中任意对象都有一个内置属性[[prototype]]
。一般情况下我们是没办法获取到这个内置属性的。不过在ES5中内置了该属性标准的Get方法:Object.getProtypeOf()
。
使用方法:
Object.getPrototypeOf(object)
object
:要返回其原型的对象;
返回值:指定对象的原型。
在ES5之前(现在也是),大多数浏览器(非标准方法)都支持并采用_proto_
属性来访问对象的原型。
使用方法:
object._proto_;
object
:要返回其原型的对象;
返回值:指定对象的原型。
显示原型
每个函数在创建之后都会拥有一个名为prototype
的属性。该属性指向函数的原型对象。
注意:
通过
Function.prototype.bind()
方法构造出来的函数例外。
两者的关系
上述两个概念可以简单理解为:
对象具有
[[prototype]]
属性,而函数具有prototype
属性。
那么现在引入第三个概念:
在JavaScript世界中,所有的函数都可以是构造函数,只要它是通过
new
关键字创建实例的,它就可以称之为该实例(对象)的构造函数。
有了上述几个概念,我们就可以解释下图了:
上图理解分为四个部分:
①构造函数通过new
关键字创建对象
②对象包含隐式原型[[prototype]]
,构造函数包含显示原型prototype
③隐式原型[[prototype]]
指向显示原型prototype
④显示原型prototype
包含constructor
属性,该属性指向构造函数。
Function构造函数
到这里为止,我相信大家应该都不会有什么疑惑。
那么我们深入一下:
文章开头,有这么一句话:
在JavaScript世界中,一切都可以看做对象。
构造函数也是对象的一种,那么它是如何被它的构造函数创建的呢?
相信大家已经猜到了答案,那就是JavaScript内置的Function构造函数。在JavaScript中,每个函数实际上都是一个Function函数的实例。
无论你是这样:
var f1=function f(){}
还是这样:
var f2=new Function();
他们都是Function的一个实例:
console.log(f1.__proto__.constructor === f2.__proto__.constructor)//true
//两者的构造函数都是Function函数
那么由此我们可以得出这样一个结论:
注意:方便演示,后面的所有图中显示原型prototype
都将写成XXX.prototype
,所有的隐式原型[[prototype]]
都将写成_proto_
。
Object构造函数
如果我们创建一个对象,而非一个函数呢?
在JavaScript中,所有的对象都来自Object
。所有对象从Object.prototype
继承方法和属性,尽管它们可能被覆盖。
那么我们可以得出以下结论:
注意:通过Object.create(null)
函数创建的对象将没有原型链。
函数与对象
函数本身是对象的一种,而对象由函数创建。
这似乎陷入到了先有鸡还是先有蛋 的死循环里了。
不过我们还是先按部就班,一步步来分析一下:
Object
对象是顶级对象,在它之上再无其他对象。
Object
函数对象将由Function构造函数创建。
Function
函数是顶级函数,在它之上再无其他函数。
那么Function
函数对象将没有构造函数。
原型是一个对象,它也有_pro_
属性。
上图一步步解释为:
①Function
函数是一个对象,它拥有_proto_
属性,该属性指向显示原型Function.prototype
。
②Function.prototype
也是一个对象,它包含_proto_
属性,该属性指向Object.prototype
。
③Object.prototype
也是一个对象,它也包含_proto_
属性,该属性指向null
。
将上述的两幅图合并,我们可以得到下图:
总结
Function函数创建了Object函数对象,Function和Object函数都是对象
Function和Object函数对象的_proto_
都指向Function.prototype
Function.prototype
的_proto_
指向Object.prototype
null
是原型链中最后的出口。
原型链
即便你没弄完全弄懂上述的内容,也没关系,我们可以举一个例子来解释。
首先创建一个构造函数(默认首字母大写的为构造函数):
function A(){};
创建它的实例:
var a =new A();
那么根据我们前文所说的内容,我们不难画出这样的图示:
JavaScript的属性查询是基于_proto_
属性的,现在我们来模拟一下。
查找一个对象a中不存在的属性b:
这种基于
_proto_
属性的属性查询,就是我们所说的原型链。它像一条连起来的链条一样,能让我们访问到构造函数的原型对象,Object.prototype
(Object函数的原型对象),再到null。
注意:实际上访问到Object.prototype
就停止了,因为null
我们并不能设置属性给它。
总结
显示原型
显示原型就是我们所说的原型对象。
显示原型是用来实现基于原型的继承和属性的共享。
隐式原型
所有对象有拥有隐式原型。
隐式原型是一个指针,指向显示原型。
隐式原型是原型链的基础,同样用于实现基于原型的继承。
原型链
对象有隐式原型,指向其原型对象;
原型对象有隐式原型,指向原型对象的原型对象,层层上沿,构成原型链。
原型链的终点是null。
本文到此结束,希望能对大家有所帮助(~* ̄︶ ̄)~