[Front-end dictionary] Essential knowledge-prototype and prototype chain

Preface

Inheritance is a knowledge point that our front-end must be familiar with. But there are still many front-ends who do not have an overall grasp of the implementation and application of inheritance . There are two reasons for this:

  1. The implementation method of ECMAScript  inheritance is different from other Object Oriented languages ​​that implement inheritance based on classes.


  2. Even if I don't know how to implement inheritance at work , I don't delay writing logic code at all.

Regardless of the reason, it is recommended that you understand the implementation and application of inheritance as soon as possible , otherwise you may be like your emoticon-shed tears of no technology.

Next, I will do my best to make it clear to inherit the concept, combined with the classic graphics-related assistance in explaining to do.

Before talking about the concept of ECMAScript inheritance, let me first talk about the concept of classes and prototypes .

Class and prototype

class

Before I talk about the concept of ECMAScript inheritance, let me talk about the concept of classes. (If you have been exposed to Java or C++, we know that Java (C++) inheritance is based on class inheritance).

Class: Object-oriented (Object Oriented) language realizes the basis of information encapsulation, called class type . Each class contains data descriptions and a set of functions for manipulating data or passing messages. Instances of classes are called objects .

Class: It describes a form of code organization structure, a method of modeling problem domains in the real world in software.

I will not expand the concept of classes here, and interested students can consult books by themselves. Next we will focus on the prototype and the prototype chain .

prototype

JavaScript has no concept of classes, so JavaScript is not based on class inheritance, but on prototype inheritance. ( Mainly learn from the Self language prototype ( prototype) inheritance mechanism ).

Note: The concept of the class keyword in ES6 and the class in OO language is different, I will talk about it below. ES6's class is also based on the inheritance of prototype implementation.

JavaScript abandons classes and uses prototypes as the basis for implementing inheritance because prototype- based inheritance is conceptually simpler than class- based inheritance. First of all, let us make it clear that the purpose of classes is to instantiate objects, and JavaScript can easily create objects directly through object literal syntax .

Each function has a prototypeproperty. All through function newobjects out of, this object has a __proto__point to this function prototype. When a function you want to use an object (or an array): if the object itself has this function, then used directly; if the object does not have this feature, then go __proto__find the.

1.  prototype [Explicit prototype]

prototype 是一个显式的原型属性,只有函数才拥有该属性。
每一个函数在创建之后都会拥有一个名为 prototype 的属性,这个属性指向函数的原型对象。( 通过 Function.prototype.bind 方法构造出来的函数是个例外,它没有 prototype 属性 )

prototype 是一个指针,指向的是一个对象。比如 Array.prototype 指向的就是 Array 这个函数的原型对象。

image.png

在控制台中打印 console.log(Array.prototype) 里面有很多方法。这些方法都以事先内置在 JavaScript 中,直接调用即可。上面我标红了两个特别的属性 constructor__proto__。这两个属性接下来我都会讲。

我们现在写一个 functionnoWork(){} 函数。image.png

当我写了一个 noWork 这个方法的时候,它自动创建了一个 prototype 指针属性(指向原型对象)。而这个被指向的原型对象自动获得了一个 constructor (构造函数)。细心的同学一定发现了: constructor 指向的是 noWork



  1. noWork.prototype.constructor === noWork     // true


  2. // 一个函数的原型对象的构造函数是这个函数本身

tips: 图中打印的 Array 的显式原型对象中的这些方法你都知道吗?要知道数组也是非常重要的一部分哦 ~  咳咳咳,这是考试重点。


2. __proto__[隐式原型]

prototype 理解起来不难, __proto__ 理解起来就会比 prototype 稍微复杂一点。不过当你理解的时候你会发现,这个过程真的很有趣。下面我们就讲讲 __proto__

其实这个属性指向了 `[[prototype]]`,但是 `[[prototype]]`  是内部属性,我们并不能访问到,所以使用 `__proto__` 来访问。

我先给个有点绕的定义:

__proto__ 指向了创建该对象的构造函数的显式原型。

我们现在还是使用 noWork 这个例子来说。我们发现 noWork 原型对象中还有另一个属性 __proto__

我们先打印这个属性:image.png我们发现这个 __proto__ 指向的是 Object.prototype

我听到有人在问为什么?

  1. 因为这个 __proto__.constructor 指向的是 Object

  2. 我们知道:一个函数的原型对象的构造函数是这个函数本身

  3. 所以这个 __proto__.constructor 指向的是 Object.prototype.constructor

  4. 进而 __proto__ 指向的是 Object.prototype

        

至于为什么是指向 Object? 因为所有的引用类型默认都是继承 Object 。

作用

  1. 显式原型:用来实现基于原型的继承与属性的共享。

  2. 隐式原型:构成原型链,同样用于实现基于原型的继承。 举个例子,当我们使用 noWork 这个对象中的 toString() 属性时,在 noWork 中找不到,就会沿着 __proto__ 依次查找。

3. new 操作符

当我们使用 new 操作符时,生成的实例对象拥有了 __proto__属性。即在 new 的过程中,新对象被添加了 __proto__ 并且链接到构造函数的原型上。

new 的过程

  1. 新生成了一个对象

  2. 链接到原型

  3. 绑定 this

  4. 返回新对象

Function.__proto__===Function.prototype

难道这代表着 Function 自己产生了自己? 要说明这个问题我们先从 Object 说起。

我们知道所有对象都可以通过原型链最终找到 Object.prototype ,虽然 Object.prototype 也是一个对象,但是这个对象却不是 Object 创造的,而是引擎自己创建了 Object.prototype 所以可以这样说:

所有实例都是对象,但是对象不一定都是实例。

接下来我们来看 Function.prototype 这个特殊的对象:

打印这个对象,会发现这个对象其实是一个函数。我们知道函数都是通过 newFunction() 生成的,难道 Function.prototype 也是通过 newFunction() 产生的吗?这个函数也是引擎自己创建的。

首先引擎创建了 Object.prototype ,然后创建了 Function.prototype,并且通过 __proto__ 将两者联系了起来。

这就是为什么 Function.prototype.bind() 没有 prototype 属性。因为 Function.prototype 是引擎创建出来的对象,引擎认为不需要给这个对象添加 prototype 属性。

对于为什么 Function.__proto__ 会等于 Function.prototype ? 我看到的一个解释是这样的:
其他所有的构造函数都可以通过原型链找到 Function.prototype ,并且 functionFunction() 本质也是一个函数,为了不产生混乱就将 functionFunction()__proto__ 联系到了 Function.prototype 上。

后话

如果你想进【大前端交流群】,关注公众号点击“交流加群”添加机器人自动拉你入群。关注我第一时间接收最新干货。

image.png


Guess you like

Origin blog.51cto.com/15077552/2596408