前言

你是否曾思考为什么我们能使用 JS 中的一些内置属性和方法,比如 .length.split().join()?我们并没有显式地声明它们,那么究竟它们从哪里来的呢?可不要说什么“那是 JS 中的魔法!”。其实这些都因为一个叫做 原型继承(prototypal inheritance) 的东西。它太棒啦,你平时也经常会用到,只不过可能没有注意!

构造函数

我们经常需要创建很多相同类型的对象。想象一下我们有个网站,上面都是狗狗~

对于每个狗狗来说,我们需要一个对象来表示它!为了不每次都新创建一个对象,就需要写一个构造函数(稍后再说 ES6 中的类哈~)。有了构造函数,就可以用 new 关键字来创建狗狗的 实例(instance) 了。

每只狗都有一个 名字(name)品种(breed)颜色(color) 和一个 吠(bark) 方法。

当我们创建了这个 Dog 构造函数,它并不是我们创建的唯一对象(要知道函数也是对象)。自动地,我们创建了另一个 prototype 对象。默认情况下,这个对象有一个 constructor 属性,指向的就是我们的 Dog 构造器。

这个在 Dog 构造器上的 prototype 属性是不可枚举的,意味着当你尝试访问对象属性时,该属性不会显示。但是它仍然在那里!

原型继承

好吧~那么为什么需要有该属性对象呢?首先,让我们来创建几只狗狗。简单起见,我们就叫它们 dog1 和 dog2dog1 叫 Daisy,是一只可爱的拉布拉多(Labrador)!dog2 叫 Jack,是一只勇敢的杰克罗素犬(Jack Russell)~

让我们将 dog1 打印到控制台,然后展开它的属性:

可以看到我们添加的属性有:namebreedcolor 和 bark,但是快看,还有一个 __proto__ 属性!它也是不可枚举的,所以通常我们在获取对象属性的时候也看不到它。让我们展开看看:

是不是看起来和 Dog.prototype 一样哈!你猜怎么着,这个 __proto__ 就是对 Dog.prototype 的引用。这就是 原型继承 的全部内容:构造函数创造的每个实例都能够访问构造函数的原型。

原型继承的好处

那么为什么这很酷?有时候我们拥有每个实例共享的属性。比如 bark 方法:它在每个实例中都是相同的,那为什么每次创建新实例都要新建一个 bark 方法,很明显这样会浪费内存。相反地,我们可以将 bark 方法添加到 Dog.prototype 上去!

这样每当我们访问实例的属性时,引擎首先检查该属性在实例上是否定义,如果没有找到,就会通过 __proto__ 属性,顺着原型链 继续查找。

不止是一层

这只是一个步骤,其实可以包含多个步骤!如果继续进行下去,你可能会注意到,当我展开 Dog.prototype 的 __proto__ 对象时,我没有包含一个属性。由于 Dog.prototype 自己也是一个对象,这意味着它实际上是 Object 构造函数的实例。这意味着 Dog.prototype 也有一个 __proto__ 属性,并且指向了 Object.prototype 。

比如说 .toString() 这个方法。它是定义在 dog1 上么?明显不是。那么在 Dog.prototype 上有么?也没有!其实它是定义在 Dog.prototype.__proto__ 上,即 Object.prototype 上。

ES6 中的类

前面我们使用的是构造函数的方式(function Dog() { ... }),实际上 ES6 中提供了构造函数和原型的更简单的语法:类(Classes)

 只是 构造函数 的 语法糖。一切都是以相同的方式工作!

我们使用 class 关键字来定义类。每个类都有一个 constructor 函数,基本上对应了 ES6 中构造函数的写法。而我们想要添加到原型 prototype 上的属性和方法,都可以在类中直接定义。

关于类的另一个好处就是,我们可以轻松地 扩展(extend) 其他的类。

类的继承

假如我们要添加另一种狗,吉娃娃(Chihuahuas)狗。为了便于理解,我们只添加一个 name 属性。但是吉娃娃也可以有自己特殊的叫声!和普通的叫声不同,吉娃娃的叫声可能比较小~

在子类中,我们可以通过 super 关键字访问到父类的构造方法。参数当然也参考父类的构造方法传入。

myPet 可以访问到 Chihuahua.prototype 和 Dog.prototype (当然也有 Object.prototype ,因为 Dog.prototype 是个对象)。

由于 Chihuahua.prototype 上有一个 smallBark 方法,Dog.prototype 上有一个 bark 方法,所以我们可以在 myPet 实例上同时访问到 smallBark 和 bark 方法。

原型的终点

现在,你可以想象,原型链不会永远持续下去。最终会有一个原型等于 null 的对象:它就是 Object.prototype。如果我们试图访问在本地或者原型链上都不存在的属性,最终会返回 undefined

Object.create

尽管上面已经解释了构造函数和类,其实还有一个为对象添加原型的方式是使用 Object.create 方法。通过这个方法,我们创建了一个新对象,并且指明了这个对象的原型是什么。

只需要将一个已经存在的对象传入 Object.create 方法中。创建出来的对象就是以我们传入的对象作为原型。看例子:

我们打印一下 me ,可以看到:

我们并没有为 me 对象添加其他的属性,但是访问它却有一个 __proto__ 属性,并且这个属性指向的是具有 name 和 age 的对象 person。而 person 这个对象的 __proto__ 属性指向的是 Object.prototype

全文就到这里啦,希望对你学习原型继承有帮助~

本文是翻译的系列文章:

  • 动图学 JS 之:声明提升(Hoisting)

  • 动图学 JS 之:作用域链(Scope Chain)

  • 动图学 JS 之:事件循环(Event Loop)

  • 动图学 JS 之:JavaScript 引擎原理

  • 动图学 JS 之:原型继承 【本篇】

参考链接

  • JavaScript Visualized: Prototypal Inheritance


本文首发于公众号:码力全开(codingonfire)

本文随意转载哈,注明原文链接即可,公号文章转载联系我开白名单就好~

动图学 JavaScript 之:原型继承相关推荐

  1. html 原型图片,可视化的JavaScript:原型继承(动图演示)

    你是否曾思考为什么我们能使用 JS 中的一些内置属性和方法,比如 .length,.split(),.join()?我们并没有显式地声明它们,那么究竟它们从哪里来的呢? 可不要说什么"那是 ...

  2. 初学JavaScript:原型继承/盗用构造函数继承/组合继承/寄生式继承/原型式继承/寄生组合式继承

    文章目录 继承 简介 1.原型链继承 默认原型 判断原型与实例间是否为继承关系 原型继承中的方法 原型链的破坏 原型继承的问题 2.盗用构造函数继承 简介 盗用构造函数继承的问题 3.组合继承 简介 ...

  3. 理解JavaScript中的原型继承(2)

    两年前在我学习JavaScript的时候我就写过两篇关于原型继承的博客: 理解JavaScript中原型继承 JavaScript中的原型继承 这两篇博客讲的都是原型的使用,其中一篇还有我学习时的错误 ...

  4. 白话解释 Javascript 原型继承(prototype inheritance)

    来源: 个人博客 白话解释 Javascript 原型继承(prototype inheritance) 什么是继承? 学过"面向对象"的同学们是否还记得,老师整天挂在嘴边的面向对 ...

  5. 再论JavaScript原型继承和对象继承

    JavaScript的原型继承是老生常谈.由于原型即prototype本身也是对象,所以"原型"继承可认为是一种特殊的"对象式"继承."对象式&quo ...

  6. JavaScript大杂烩4 - 理解JavaScript对象的继承机制

    JavaScript是单根的完全面向对象的语言 JavaScript是单根的面向对象语言,它只有单一的根Object,所有的其他对象都是直接或者间接的从Object对象继承.而在JavaScript的 ...

  7. python继承方式是基于原型吗_[译] 为什么原型继承很重要

    五天之前我写了一个关于ES6标准中Class的文章.在里面我介绍了如何用现有的Javascript来模拟类并且介绍了ES6中类的用法,其实它只是一个语法糖.感谢Om Shakar以及Javascrip ...

  8. Javascript的原型链图

    90%的前端或者js程序员或者老师们对Javascript懂得不比这个多 给手机看的 但是这个图里的所有褐色单向箭头链就是Javascript的原型链(颜色标注对理解js原型链很关键) 这图中的各个_ ...

  9. 深入解析JavaScript 原型继承

    JavaScript 原型继承,学习js面向对象的朋友可以看看.十分的全面细致,具有一定的参考价值,对此有需要的朋友可以参考学习下.如有不足之处,欢迎批评指正. Object.prototype Ja ...

最新文章

  1. 学习vue的双向数据绑定的原理
  2. How To: Team Build 自定义版本号
  3. 谷歌浏览器的下载位置如何设置 Chrome浏览器下载路径设置方法简述
  4. 18 WM配置-策略-激活仓位类型搜索(Bin Type Search)
  5. 如何设置mysql让其他人能访问_怎么设置MySQL就能让别人访问本机的数据库了?...
  6. golang操作mongodb的驱动mongo-go-driver的事务支持和访问控制(mongodb4.0)
  7. 交朋友游戏C语言,幼儿园小班社会教案《我会交朋友》游戏活动
  8. 数据结构 实验五(银行叫号系统)
  9. BP神经网络python代码详细解答(来自原文)
  10. stm32蜂鸣器程序
  11. python安装numpy库教程_Python库之numpy库的安装教程
  12. Oracle 11g企业版下载
  13. 乐吾乐 Topology 全新优化1.0已经内测啦
  14. 【蓝桥杯】-- 竞赛规则及说明(Python程序设计)
  15. 精挑细选的原创公众号,你值得拥有
  16. Python之科赫曲线绘制
  17. OpenCV开发笔记(四十五):红胖子8分钟带你深入了解重映射(图文并茂+浅显易懂+程序源码)
  18. 自定义复选框checkbox样式
  19. su oracle和su - oracle的区别
  20. 计算机没考好的检讨书300百以上,考试反思检讨书300字(精选10篇)

热门文章

  1. 编译Nexus 7 源码的流程
  2. win10虚拟机搭建+Qt6安装
  3. HTML5开发IDE介绍
  4. springboot留言板添加登录功能
  5. 获取网页元素的绝对位置
  6. 算法分析课设(十一)博物馆守卫问题、世界名画陈列馆问题(分支界限法)
  7. 工业路由器误按RST复位键如何处理?RST键的作用
  8. (转)解读:孔子在中国与世界历史上的十种形象
  9. 关于favicon.ico
  10. win中MD5加密操作