JavaScript面向对象技术·原型与原型链

来源:互联网 发布:网络与言论自由 编辑:程序博客网 时间:2024/06/04 19:00

在JavaScript世界中,一切都可以看做对象。与对象关系最密切的有两个概念:原型和原型链。

原型

在JavaScript世界中,原型具体可以分为两种:

  1. 隐式原型
  2. 显示原型

隐式原型

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:

Created with Raphaël 2.1.0开始查询对象a的实例中不含有属性bA.prototype中不含有属性bObject.prototype中不含有属性b返回`undefined`结束查询返回查询到的结果yesnoyesnoyesno

这种基于_proto_属性的属性查询,就是我们所说的原型链。它像一条连起来的链条一样,能让我们访问到构造函数的原型对象,Object.prototype(Object函数的原型对象),再到null。

注意:实际上访问到Object.prototype就停止了,因为null我们并不能设置属性给它。


总结

显示原型

显示原型就是我们所说的原型对象
显示原型是用来实现基于原型的继承和属性的共享。

隐式原型

所有对象有拥有隐式原型。
隐式原型是一个指针,指向显示原型。
隐式原型是原型链的基础,同样用于实现基于原型的继承。

原型链

对象有隐式原型,指向其原型对象;
原型对象有隐式原型,指向原型对象的原型对象,层层上沿,构成原型链。
原型链的终点是null。


本文到此结束,希望能对大家有所帮助(~* ̄︶ ̄)~

原创粉丝点击