欢迎光临散文网 会员登陆 & 注册

图解JavaScript:对象、继承与原型链

2022-04-26 10:07 作者:Flinx_方凌旭  | 我要投稿

根据理论绘制示意图

构造函数、原型

  1. 每个function都有一个原型functionprototype属性指向了这个function原型

  2. 构造函数function的一个种类,所以

  3. ● 每个构造函数都有一个原型构造函数prototype属性指向了这个构造函数原型

  4. 每个object都是由某个构造函数产生的,object__proto__属性指向产生这个object构造函数原型

注:原型是从属于function的,应当说“function原型”,而“object原型”是“object构造函数原型”的简略说法。

  1. functionobject的一个种类,所以

  2. ● 每个function都是由某个构造函数产生的,function__proto__属性指向了产生这个function构造函数原型

  3. 产生function构造函数Function,即Functionfunction构造函数,所以

  4. ● Function有一个原型Functionprototype属性指向了Function原型

    ● 每个function都是由Function产生的,function__proto__属性指向了Function原型

  5. Functionfunction构造函数,所以Function是一个function,所以Function是它本身的构造函数,所以

  6. ● Function__proto__属性指向了Function原型

类、实例

JavaScript没有专门的类定义。

假如有一个名为Person构造函数,由Person产生了一个名为person1object,我们可以认为Person是一个类的名字,person1Person类的一个实例

图中黄色框表示function,绿色框表示object

结合前文内容可知,person1Person类的一个实例,表现为person1__proto__属性指向Person原型

那么从图中可以发现,Person__proto__属性指向Function原型,所以说Person(一个function)是Function类的一个实例

这是因为产生function构造函数Function,所以所有的function都是Function实例

注:prototype构造函数的属性,__proto__实例的属性。然而,构造函数本身是Function实例,所以构造函数也有__proto__属性。

继承

这里直接给出结论:Teacher类继承Person类,表现为Teacher原型__proto__属性指向Person原型

图中绿色箭头表示实例化自,蓝色箭头表示继承。

观察图片左下角,我们还可以这样说,Teacher类继承Person类,表现为Teacher原型Person类的一个实例。这个说法在后面还会提到。

继续给出如下信息:

  1. Function类继承Object类,即

  2. ● Function原型__proto__属性指向Object原型

  3. Object构造函数,所以Object是一个function,所以FunctionObject构造函数,所以

  4. ● Object__proto__属性指向了Function原型

  5. 所有的都直接或间接地继承Object类,没有指定继承来源的(如Person类)都默认直接继承Object类,所以

  6. ● Person原型__proto__属性指向Object原型

  7. Object类是继承的终点,Object原型__proto__属性为null

object、function

  1. 上面所说的“名为Personfunction”,实际上是一个“名为Person的指向一个function变量

  2. 同理,“名为person1object”,实际上是一个“名为person1的指向一个object变量

  3. object本身没有名字,而function本身有名字,保存在functionname属性中

  4. functionlength属性中保存函数的形参个数

  5. 所有object都会从它的构造函数原型上继承一个 constructor 属性,保存对创建该object构造函数的引用,换句话说,

  6. ● 构造函数原型constructor 属性指向该构造函数本身

在浏览器中进行实验

Object

首先尝试在浏览器控制台打印Object,它应该是一个指向Object类构造函数内置变量

输出的“ƒ”表明这是一个function,“Object()”表明函数名为“Object”且形参列表为空。后面的大括号中本应输出函数体,由于Object()是内置函数,故显示为“[native code]”。

如何把Object类构造函数作为一个object而不是function打印出来?我们可以手动创建一个object,令它的一个成员变量(即“属性”)指向Object类构造函数

执行{Object},它是{Object: Object}的简写,表示使用对象字面量语法(大括号)创建一个object,并把内置变量Object(指向Object类构造函数)赋值给这个object的名为Object的属性。图示如下:

控制台输出如下:

输出的“{Object: ƒ}”表明这是一个object,且它有一个名为Object的属性,该属性指向一个function(或者说“该属性是一个function”)。

将其展开,可以发现它还有一个名为__proto__的属性(注意缩进),且__proto__属性指向一个Object类实例(即object)。而Object属性指向一个函数名为“Object”且形参列表为空的function,它就是Object类构造函数。这个Object属性可以被展开,即达到了将一个function作为object打印出来的目的。

观察Object类构造函数,发现它的确有lengthnameprototype__proto__属性:

  • length属性为数值1(注意并不是理论值“0”)

  • name属性为字符串“Object”

  • prototype属性指向一个object,它是Object这个构造函数原型

  • __proto__属性指向一个function,根据前文所述,它是Function这个构造函数原型(它并不是一个object,是例外情况),稍后进行验证

我们手动创建的object必为Object类实例,所以它的__proto__属性应当与Object类构造函数prototype属性指向同一个object

我们将它们各自展开,发现它们完全一致:

验证如下:

接着验证Object原型constructor 属性指向Object本身:

注:访问一个对象的属性时,如果该对象内部不存在这个属性,那么就会去它的__proto__属性所指向的那个对象(可以理解为父对象)里找,如果父对象也不存在这个属性,则接着去父对象的__proto__属性所指向的那个对象(可以理解为爷爷对象)里找,如果还没找到,则继续往上找……直到原型链顶端null

最后,Object原型__proto__属性为null,而不是undefined(其实 __proto__ 是个定义在 Object.prototype 上的访问器属性,稍后会进行说明)。

选取几个方法作为代表,完善示意图:

Function

执行{Function},它是{Function: Function}的简写,表示使用对象字面量语法(大括号)创建一个object,并把内置变量Function(指向Function类构造函数)赋值给这个object的名为Function的属性。图示如下:

控制台输出如下:

输出的“{Function: ƒ}”表明这是一个object,且它有一个名为Function的属性,该属性指向一个function(或者说“该属性是一个function”)。

将其展开,可以发现Function属性指向一个函数名为“Function”且形参列表为空的function,它就是Function类构造函数

观察Function类构造函数,发现它的确有lengthnameprototype__proto__属性:

  • length属性为数值1(注意并不是理论值“0”)

  • name属性为字符串“Function”

  • prototype属性指向一个function,它是Function这个构造函数原型

  • __proto__属性指向一个function,它同样是Function这个构造函数原型

Function类构造函数prototype__proto__属性与Object类构造函数__proto__属性应当指向同一个function,验证如下:

观察Function原型,由于它是一个function,所以有lengthname__proto__属性,然而特殊之处是它没有prototype属性:

  • length属性为数值0

  • name属性为空字符串

  • __proto__属性指向一个object,根据前文所述,它是Object原型,验证如下:

Function原型虽然是一个function,但是没有prototype属性:

最后,验证Function原型constructor 属性指向Function本身:

那么Object.constructor指向谁?Function.constructor指向谁?

选取几个方法作为代表,完善示意图(注意Function原型改用黄色框,表示它是一个function):

使用Javascript中的原型

https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Objects/Object_prototypes#使用javascript中的原型

在控制台执行如下代码,创建doSomething函数:

我们可以添加一些属性到doSomething原型上面:

然后,我们可以使用new运算符来在现在这个原型的基础上,创建一个doSomething实例

就像上面看到的, doSomeInstancing__proto__ 属性就是doSomething.prototype. 但是这又有什么用呢? 好吧,当你访问 doSomeInstancing 的一个属性, 浏览器首先查找 doSomeInstancing 是否有这个属性. 如果 doSomeInstancing 没有这个属性, 然后浏览器就会在 doSomeInstancing__proto__ 中查找这个属性(也就是 doSomething.prototype). 如果 doSomeInstancing 的 __proto__ 有这个属性, 那么 doSomeInstancing 的 __proto__ 上的这个属性就会被使用. 否则, 如果 doSomeInstancing 的 __proto__ 没有这个属性, 浏览器就会去查找 doSomeInstancing 的 __proto____proto__ ,看它是否有这个属性. 默认情况下, 所有函数的原型属性的 __proto__ 就是 window.Object.prototype. 所以 doSomeInstancing 的 __proto____proto__ (也就是 doSomething.prototype 的 __proto__ (也就是 Object.prototype)) 会被查找是否有这个属性. 如果没有在它里面找到这个属性, 然后就会在 doSomeInstancing 的 __proto____proto____proto__ 里面查找. 然而这有一个问题: doSomeInstancing 的 __proto____proto____proto__ 不存在. 最后, 原型链上面的所有的 __proto__ 都被找完了, 浏览器所有已经声明了的 __proto__ 上都不存在这个属性,然后就得出结论,这个属性是 undefined.

执行如下代码:

输出结果为:

new 操作符的作用

当执行:

JavaScript 实际上执行的是:

可以认为在使用new 操作符创建一个实例的过程中,存在如下的中间状态:

Object.create()

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/create

Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__

注意d.__proto__undefined,而不是null

注意控制台把f作为Function类的一个实例打印出来。

[未完]

图解JavaScript:对象、继承与原型链的评论 (共 条)

分享到微博请遵守国家法律