专栏首页小码农学习笔记一文学习 JavaScript 原型与原型链
原创

一文学习 JavaScript 原型与原型链

原型与原型链

前言

ES6 出来之后,原型在平时工作中用得就比较少了。但原型是 JavaScript 中的基础,很多流行框架诸如 Vue 和 React 中就多次用到 prototype 。平时在写代码时,也会不知不觉就应用上了原型的某个基础知识点。

本篇主要介绍以下两个知识点:

  • 原型
  • 原型链

原型

任何一个函数,都拥有一个 prototype 属性,它指向这个函数的原型对象,如:

function Foo () {}
console.log(Foo.prototype); // { constructor: f Foo(), __proto__: Object }

画图表示如下:

上图左边代表 Foo 函数,它有一个 prototype 属性,指向右侧这个原型对象,每声明一个函数,都会有这样的一个原型对象,原型对象有一个 constructor 属性,指向 Foo 函数本身,也有个 __proto__ 属性,这里我们暂且不讲。


我们来看 Foo 函数的实例化:

const foo = new Foo();

这里我们通过 new 操作符实例化了一个 foo 对象,来看此时的图解:

foo 默认会有个 __proto__ 属性,它也指向构造函数 Foo 的原型,这就是 __proto__ 的作用,即指向构造函数的原型


那让我们回到 Foo.prototype.__proto__,来看看他的指向吧:

上图的 Foo.prototype.__proto__ 指向 Object.prototype,也就是说:每个函数的原型,都是 Object 的实例。就好像每个函数的原型,是由 new Object() 产生一样。

以上就是关于原型的阐述,如果看到这里似懂非懂,建议反复看几遍,注意文字与图片对应,线条的指向,看懂了再接着往下看。

prototype__proto__

  • prototype:显式原型对象,每一个函数(除了 bind)在创建之后都会拥有一个名为 prototype 的内部属性,它指向函数的原型对象。用来实现基于原型的继承与属性的共享。
  • __proto__:隐式原型对象,是每个对象都具有的属性,这个属性的值指向该对象的构造函数的原型对象。

一个对象的隐式原型指向构造该对象的构造函数的显式原型对象

foo.__proto__ === Foo.prototype // true

::: warning

[[prototype]]__proto__ 意义相同,均表示对象的内部属性,其值指向对象原型。前者在一些书籍、规范中表示一个对象的原型属性,默认情况下是不可以再被外部访问的,估计是会被一些内部方法使用的,例如用 for...in 来遍历原型链上可以被枚举的属性的时候,就需要通过这个指针找到当前对象所继承的对象;后者则是在浏览器实现中支持的一个属性,用于指向对象原型。

:::

原型方法

在 ES5 之前没有标准的方法访问 [[prototype]] 这个内置属性,但是大多数浏览器都支持通过 __proto__ 访问。

如今,__proto__ 被认为是过时且不推荐使用的(deprecated),这里的不推荐使用是指 JavaScript 规范中规定,__proto__ 必须仅在浏览器环境下才能得到支持。

现代的方法有:

应该使用这些方法来代替 __proto__

例如:

let animal = {
  eats: true
};

// 创建一个以 animal 为原型的新对象
let rabbit = Object.create(animal);

console.log(rabbit.eats); // true

console.log(Object.getPrototypeOf(rabbit) === animal); // true

Object.setPrototypeOf(rabbit, {}); // 将 rabbit 的原型修改为 {}

Object.create 有一个可选的第二参数:属性描述器。我们可以在此处为新对象提供额外的属性,就像这样:

let animal = {
  eats: true
};

let rabbit = Object.create(animal, {
  jumps: {
    value: true
  }
});

console.log(rabbit.jumps); // true

我们可以使用 Object.create 来实现比复制 for..in 循环中的属性更强大的对象克隆方式:

let clone = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));

此调用可以对 obj 进行真正准确地拷贝,包括所有的属性:可枚举和不可枚举的,数据属性和 setters/getters —— 包括所有内容,并带有正确的 [[Prototype]]

原型的关系

所有原生构造函数的 __proto__ 都指向 Function.prototype

即:原生构造函数.__proto__ === Function.prototype

Object.__proto__   === Function.prototype;   // true
Function.__proto__ === Function.prototype;   // true
Number.__proto__   === Function.prototype;   // true
Boolean.__proto__  === Function.prototype;   // true
String.__proto__   === Function.prototype;   // true
Object.__proto__   === Function.prototype;   // true
Array.__proto__    === Function.prototype;   // true
RegExp.__proto__   === Function.prototype;   // true
Error.__proto__    === Function.prototype;   // true
Date.__proto__     === Function.prototype;   // true

进而有了:

String.__proto__ === Boolean.__proto__
RegExp.__proto__ === Error.__proto__
Date.__proto__ === Number.__proto__

同理,函数原型的隐式原型都是对象,所以构造函数是 Object

即:Function.prototype.__proto__ === Object.prototype

Object.__proto__.__proto__   === Object.prototype;   // true
Function.__proto__.__proto__ === Object.prototype;   // true
Number.__proto__.__proto__   === Object.prototype;   // true
Boolean.__proto__.__proto__  === Object.prototype;   // true
String.__proto__.__proto__   === Object.prototype;   // true
Object.__proto__.__proto__   === Object.prototype;   // true
Array.__proto__.__proto__    === Object.prototype;   // true
RegExp.__proto__.__proto__   === Object.prototype;   // true
Error.__proto__.__proto__    === Object.prototype;   // true
Date.__proto__.__proto__     === Object.prototype;   // true

原型链

原型链是 JavaScript 作者为了继承而设计的。由上边的分析,const foo = new Foo() 语句,其实是产生了一个链条的,如下:

我们在 new 出 foo 对象后,并没有给 foo 对象添加任何方法,但我们依然能从 foo 对象中调用 toString()hasOwnProperty() 等方法。这是为什么呢?

console.log(typeof foo.toString); // function
console.log(typeof foo.hasOwnProperty); // function

原因是:JavaScript 在设计之初,__proto__ 就是用来查找属性和方法的。

从上图的链条来看,我们在 foo 这个对象中,查找 toString 方法,没找到,就循着 foo.__proto__ 查找,foo.__proto__ 里也没有找到,就循着 foo.__proto__.__proto__ 找,这个时候找到了,则调用;如果还找不到,就再往上找,即 foo.__proto__._proto__._proto__,这个时候值为 null,查找结束。

这就是原型链,我们也可以说,Foo 继承了 Object,所以 foo 中能访问到 Object 的原型属性。


文章持续更新,本文 GitHub 前端修炼小册 已经收录,欢迎 Star。如对文章内容有不同见解,欢迎留言交流。

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • [我的理解]Javascript的原型与原型链

    一、原型与原型链的定义 原型:为其他对象提供共享属性的对象     注:当构造器创建一个对象,为了解决对象的属性引用,该对象会隐式引用构造器的"prototyp...

    sam dragon
  • 图解 JavaScript 原型与原型链

    原型在平时工作中用得比较少, 但原型是 JavaScript 中的基础, 是构建大型应用, 框架不可或缺的一环, 是你在写代码时, 不知不觉就应用上了的一个最基...

    winty
  • JavaScript原型和原型链( prototype 与 __proto__ )

    var a = new A; //a 类型:对象 //A 类型:函数 var Object = new Function(); //var 对象 = new...

    Leophen
  • JavaScript原型、原型链及原型链污染

    因为在CTF中时常也会考察原型链污染的问题,以前也一直让我捉襟见肘,一直没有系统的学习了解过JS原型的这些相关概念,因此写下本文,通过不断总结大佬的文章,写出自...

    字节脉搏实验室
  • 图解JavaScript对象原型与原型链

    原文链接:http://www.shuaihuajun.com/article/javascript-prototype-chain/

    陈帅华
  • Javascript 原型链

    本来想写一篇“如何用JS实现面向对象”,发现自己对prototype原型链还是有很多的不理解的地方。先看一张原型链关系图:

    mmzhou
  • javaScript原型链

    javaScript原型链 概念 JavaScript之继承(原型链) 数据结构 var Person = function(){}; Person...

    达达前端
  • Javascript 原型链

    每个实例对象(object)都有一个私有属性(称之为 __proto__ )指向它的构造函数的原型对象(prototype)。该原型对象也有一个自己的原型对象(...

    超级大帅比
  • 原型与原型链

    网上有太多关于原型的资料,不是一上来就各种概念,让人看到摸不着头脑,就是贴各种代码,少个通俗的解释,所以才有了这一篇文章。

    前端程序之路
  • 上帝视角一文理解JavaScript原型和原型链

    本文将从上帝角度讲解JS的世界,在这个过程中,大家就能完全理解JS的原型和原型链是什么,之后还会基于原型和原型链知识拓展一些相关知识。

    dellyoung
  • javascript 原型及原型链详解

    我们创建的每个函数都有一个 prototype (原型)属性,这个属性是一个指针,指向一个原型对象,而这个原型对象中拥有的属性和方法可以被所以实例共享。 fun...

    柴小智
  • JavaScript深入原型到原型链

    在这个例子中,Person就是一个构造函数,我们使用new创建了一个实例对象person。

    挨踢小子部落阁
  • JavaScript中原型与原型链的简单理解

    讲原型和原型链,如果是讲定义,那很是晦涩难懂,今天我们就通俗易懂的说说原型与原型链。还需要借助阮老师的“Javascript继承机制的设计思想”。

    青年码农
  • JS原型与原型链

    JavaScript有着七种基本类型String、Number、Boolean、Null、Undefined、Symbol、Object,前六种为基本数据类型,...

    WindrunnerMax
  • javascript原型链-review

    虽然现在es8都已经在预发布阶段了,但是无论发布到es几,其本身的运作原理都是一样的。

    littlelyon
  • JavaScript核心概念-原型、原型链

    每个函数都有一个prototype属性,那这个属性到底是指向哪里呢?是这个函数的原型吗?

    程序员不务正业
  • 理解原型与原型链

    原型与原型链是学习JavaScript这门语言不能不理解的两个重要概念,为什么?因为JavaScript是一门基于原型的语言。

    用户6167509
  • JavaScript中的原型链原理

      工作中经常解除到prototype的概念,一开始错误的认为prototype是对象的原型链,其实prototype只能算是JavaScript开放出来的原型...

    寒月十八
  • JavaScript 深入之从原型到原型链

    在这个例子中, Person 就是一个构造函数,我们使用 new 创建了一个实例对象 person 。 很简单吧,接下来进入正题:

    Jack Chen

扫码关注云+社区

领取腾讯云代金券