原型Prototype和原型链[[Prototype]]这两个概念起先让我晕头转向,不过看得多了也就慢慢梳理清楚了,突然想写一篇博客记一下,以后要忘了还可以回过头来翻一翻,复习复习…
Javascript当中可以说都是对象(但不都是object类型),没有类和实例这样的区分,它的继承主要是靠“原型链”来实现。其实会变成这样也是Javascript发展过程中的某种妥协,原先是没打算设计“继承”机制的,后来才加上去的……
构造函数和实例对象
首先我们有一个构造函数 Animal():
function Animal(name) {
this.name = name;
}
一般我们会这么构造:var human = new Animal("Human"); // 上帝造个人
这时候我们有了一个构造函数,它是 Animal(),还有一个实例对象 human。
谁有__proto__?谁有prototype?
首先,引入一下 __proto__
这个属性。这个没记错的话之前是浏览器提供的,后来纳入了 ES6 的规范。本质上它是[[Prototype]]的getter和setter方法,我们把它们俩简单的理解为“二位一体”就可以了。
其次,__proto__
本质上是一个指针,指向的是一个函数对象的 prototype
属性的值。
这里要特别说明的是 null
和 undefined
这两个基本类型是没有 __proto__
的,也就是没有原型链[[Prototype]]。
既然说到 prototype
,那就继续往下讲。因为JavaScript中通过函数来模拟类,所以这个属性只有函数才有。就是这么任性!
每个函数都有 prototype
,他也是一个指针,指向一个对象。而函数所指向的这个对象中包含 __proto__
和 constructor
。它和普通对象的区别在于有一个 constructor
表示构造函数,这玩意儿并没有啥用。
结合上面的例子,我们梳理一下前面的代码当中二者的情况:
- 构造函数 Animal() 有
prototype
属性,它是一个对象,包含__proto__
和constructor
- 实例对象 human 有
__proto__
和name
__proto__ 和 prototype 的内容
前面说了,原型链 __proto__
指向的是一个函数对象的 prototype
属性的值,我们把它看作一个指针就可以了。上面例子的情况就是:
- 在构造函数 Animal() 当中,
Animal.prototype.__proto__
指向的是Object.prototype
- 在实例对象 human 当中,
human.__proto__
指向的是Animal.prototype
- 构造函数 Animal() 有
prototype
,实例对象 human 则没有
JavaScript中有一个特殊的引用类型 object
,我们可以把 Object()
, String()
, Function()
, Array()
等等,都看作是 object
类型。创建实例时,每个实例都有以下的属性和方法:
- constructor:保存创建当前对象的函数,即构造函数
- hasOwnProperty(propName):可检查给定属性在当前实例中存在与否
- isPrototypeOf(object):检查传入对象是否是当前对象的原形
- propertyIsEnumerable(propName):检查给定参数可否用 for-in 枚举
- toLocaleString():返回字符串表示,与执行环境时区对应
- toString():返回对象的字符串表示
- valueOf():返回其值,与上者通常相同
上述列表就是 Object.prototype
的基本内容,也就是 Animal.prototype.__proto__
的内容。
下面说说 Animal.prototype
的内容,函数在构建的时候会自动生成一个内置属性表示构造函数,也就是 constructor
。它的值就是函数本身,也就是function Animal(name) {…} 。
(注意:constructor
在 prototype
属性当中)
什么是构造函数
构造函数是JavaScript中很奇葩的一个属性,只有函数当中才存在,但也不是都存在。
接着上面的例子,现在上帝不想单独造一个物种了,他想弄一大科目的物种,比如说猫科。
function Cat(name) {
this.nickname = name;
}
但是怎么把它和 Animal() 关联起来,实现继承关系呢?
于是乎,奇葩难懂的地方来了……我们先梳理一下这个函数当前的情况:
Cat.prototype
没有任何额外的属性Cat.prototype.constructor
是它本身,也就是 function Cat(name) {…}Cat.prototype.__proto__
指向的是Object.prototype
我们用 Cat.prototype = new Animal("Cat");
把它俩关联起来……
关联起来了……
下面看一下 Cat() 函数现在的情况:
Cat.prototype
中存在一个属性name
Cat.prototype.constructor
不存在(调用时会访问原型链的上一层)Cat.prototype.__proto__
指向的是Animal.prototype
最后构造一个猫科的实例对象:var lion = new Cat("lion");
,我们看看它都有啥:
lion
中存在属性nickname
和__proto__
lion.__proto__
指向的是Cat.prototype
混乱的总结
其实写到一半自己也有点要被绕晕的感觉。但这就是JavaScript实现原型链的方法,必须得清楚。
- __proto__本质上是[[Prototype]]的getter和setter,可以看作一体
- 函数默认有一个
prototype
属性,该属性包含constructor
和__proto__
- 对象默认有一个
__proto__
属性 __proto__
指向一个对象的prototype
,默认指向Object.prototype
- 函数的
prototype.constructor
默认指向自身 - 函数的
prototype
被替换为其他函数对象后,prototype
属性中丢失constructor
,只存在__proto__
来点图片总结……图片来源
咸鱼的一天开始了……
发表回复