手把手教你如何实现继承
本文将从最简单的例子开始,从零讲解在 JavaScript 中如何实现继承。
小例子
现在有个需求,需要实现 Cat 继承 Animal ,构造函数如下:
function Animal(name){this.name = name
}function Cat(name){this.name = name
}
复制代码
注:如对继承相关的 prototype、constructor、__proto__、new 等内容不太熟悉,可以先查看这篇文章:理性分析 JavaScript 中的原型
继承
在实现这个需求之前,我们先谈谈继承的意义。继承本质上为了提高代码的复用性。
对于 JavaScript 来说,继承有两个要点:
- 复用父构造函数中的代码
- 复用父原型中的代码
下面的内容将围绕这两个要点展开。
第一版代码
复用父构造函数中的代码,我们可以考虑调用父构造函数并将 this 绑定到子构造函数。
复用父原型中的代码,我们只需改变原型链即可。将子构造函数的原型对象的 __proto__ 属性指向父构造函数的原型对象。
第一版代码如下:
function Animal(name){this.name = name
}function Cat(name){Animal.call(this,name)
}Cat.prototype.__proto__ = Animal.prototype
复制代码
检验一下是否继承成功:我们在 Animal 的原型对象上添加 eat 函数。使用 Cat 构造函数生成一个名为 'Tom' 的实例对象 cat 。代码如下:
function Animal(name){this.name = name
}function Cat(name){Animal.call(this,name)
}Cat.prototype.__proto__ = Animal.prototype// 添加 eat 函数
Animal.prototype.eat = function(){console.log('eat')
}var cat = new Cat('Tom')
// 查看 name 属性是否成功挂载到 cat 对象上
console.log(cat.name) // Tom
// 查看是否能访问到 eat 函数
cat.eat() // eat
// 查看 Animal.prototype 是否位于原型链上
console.log(cat instanceof Animal) // true
// 查看 Cat.prototype 是否位于原型链上
console.log(cat instanceof Cat) //true
复制代码
经检验,成功复用父构造函数中的代码,并复用父原型对象中的代码,原型链正常。
图示
弊端
__proto__ 属性虽然可以很方便地改变原型链,但是 __proto__ 直到 ES6 才添加到规范中,存在兼容性问题,并且直接使用 __proto__ 来改变原型链非常消耗性能。所以 __proto__ 属性来实现继承并不可取。
第二版代码
针对 __proto__ 属性的弊端,我们考虑使用 new 操作符来替代直接使用 __proto__ 属性来改变原型链。
我们知道实例对象中的 __proto__ 属性指向构造函数的 prototype 属性的。这样我们 Animal 的实例对象赋值给 Cat.prototype 。不就也实现了Cat.prototype.__proto__ = Animal.prototype
语句的功能了吗?
代码如下:
function Animal(name){this.name = name
}function Cat(name){Animal.call(this,name)
}Cat.prototype = new Animal()
Cat.prototype.constructor = Cat
复制代码
使用这套方案有个问题,就是在将实例对象赋值给 Cat.prototype 的时候,将 Cat.prototype 原有的 constructor 属性覆盖了。实例对象的 constructor 属性向上查询得到的是构造函数 Animal 。所以我们需要矫正一下 Cat.prototype 的 constructor 属性,将其设置为构造函数 Cat 。
图示
优点
兼容性比较好,并且实现较为简单。
弊端
使用 new 操作符带来的弊端是,执行 new 操作符的时候,会执行一次构造函数将构造函数中的属性绑定到这个实例对象。这样就多执行了一次构造函数,将原本属于 Animal 实例对象的属性混到 prototype 中了。
第三版代码
考虑到第二版的弊端,我们使用一个空构造函数来作为中介函数,这样就不会将构造函数中的属性混到 prototype 中,并且减少了多执行一次构造函数带来的性能损耗。
代码如下:
function Animal(name){this.name = name
}function Cat(name){Animal.call(this,name)
}
function Func(){}
Func.prototype = Animal.prototypeCat.prototype = new Func()
Cat.prototype.constructor = Cat
复制代码
图示
ES6
使用 ES6 就方便多了。可以使用 extends 关键字实现继承, 复用父原型中的代码。使用 super 关键字来复用父构造函数中的代码。
代码如下:
class Animal {constructor(name){this.name = name}eat(){console.log('eat')}
}
class Cat extends Animal{constructor(name){super(name)}
}let cat = new Cat('Tom')
console.log(cat.name) // Tom
cat.eat() // eat
复制代码
相关知识点
- 理性分析 JavaScript 中的 this
- 理性分析 JavaScript 中的原型
参考书籍
- 《JavaScript高级程序设计(第3版)》
- 《Java核心技术 卷Ⅰ(第9版)》
手把手教你如何实现继承相关推荐
- 简单有趣的 NLP 教程:手把手教你用 PyTorch 辨别自然语言(附代码)
简单有趣的 NLP 教程:手把手教你用 PyTorch 辨别自然语言(附代码) 雷锋网(公众号:雷锋网)按:本文作者甄冉冉,原载于作者个人博客,雷锋网已获授权. 最近在学pyTorch的实际应用 ...
- css布局方式_手把手教你CSS Flex布局「真香」
作者:EcbJS 转发链接:https://blog.csdn.net/EcbJS/article/details/106466757 前言 之前做项目,关于布局方面没怎么深入研究过,好多页面都是 ...
- 手把手教我班小姐姐学java之多态
文章目录 多态 动态绑定 声明类型 实际类型 工作机制 对象转换和Instanceof操作符 概念: 隐式转换(向上转型.自动类型转换) 显式转换( 向下转型.强制类型转换) instanceof关键 ...
- 怎么批量在数字里加入网页_手把手教你爬取天堂网1920*1080大图片(批量下载)——理论篇
/1 前言/ 平时我们要下载图片,要要一个一个点击下载是不是觉得很麻烦?那有没有更加简便的方法呢?答案是肯定的,这里我们以天堂网为例,批量下载天堂网的图片. /2 项目准备工作/ 首先 我们第一步我们 ...
- 手把手教你写一个spring IOC容器
本文分享自华为云社区<手把手教你写一个spring IOC容器>,原文作者:技术火炬手. spring框架的基础核心和起点毫无疑问就是IOC,IOC作为spring容器提供的核心技术,成功 ...
- python新手入门代码-新手必看:手把手教你入门 Python
原标题:新手必看:手把手教你入门 Python 本文为 AI 研习社编译的技术博客,原标题 : Learning Python: From Zero to Hero 翻译 |永恒如新的日常校对 | 酱 ...
- iOS逆向-手把手教你写支付宝蚂蚁森林收集能量助手
iOS逆向-手把手教你写支付宝蚂蚁森林收集能量助手 前言 发现iOS支付宝逆向的分析并不多,蚂蚁森林基于H5应用 套着UIWebView 基本也没这类JS和原生交互分析的帖子,就拿此练手吧 作技术分享 ...
- 手把手教你iPhone 3G手机软件开发
手把手教你iPhone 3G手机软件开发 "iPhone 是一款革命性的.不可思议的产品,比市场上其它任何移动电话整整领先了五年,"苹果公司首席执行官史蒂夫·乔布斯如是说,&quo ...
- 手把手教你通过SpringBoot实现邮箱注册码验证
文章目录 一.前言 二.创建SpringBoot工程项目 2.1 JWT认证 2.2 集成mybatis-plus实现用户的增删改查 2.3 编写Email工具类实现邮件的发送 2.4 验证码邮件发送 ...
最新文章
- winform程序打包EXE三种方式
- LINUX服务器开启防火墙并屏蔽恶意IP
- Java 到底是值传递还是引用传递
- 01、DFT-全面了解如何测试一颗芯片
- mybatis-启动源码分析
- 三大主流软件负载均衡器对比(LVS VS Nginx VS Haproxy)
- 我眼中的分布式系统可观测性
- 华为设备IS-IS路由控制原理与实验
- 【SpringBoot_ANNOTATIONS】组件注册 05 @Lazy 懒加载
- Vue引入Froala-Editor富文本编辑器
- 在 WindowMobile 上的模拟LED 显示屏插件(转)
- [LeetCode] Maxium Subarray
- 大O记法-BigO notation
- 银河帝国----基地边缘
- oracle rman delete backupset,RMAN Crosscheck后delete obsolete遇到RMAN-06091的解决
- 原生 js html 开发成桌面应用 以及打包
- Power BI——建模
- requirejs html缓存问题,requirejs 缓存问题
- Microsoft visual Studio2017 中番茄visual assist 破解
- 718保时捷spyder_【图】保时捷发布718 Spyder/Cayman GT4官图_汽车之家
热门文章
- 内存区划分;内存分配;堆、栈概念分析;动态内存管理数据结构及程序样例;核心态与用户态...
- C++函数中返回引用和返回值的区别
- JS字符串与二进制的相互转化
- bzoj 36733674 可持久化并查集加强版(可持久化线段树+启发式合并)
- POJ 2323 贪心
- sqlserver2008r2安装
- poj 2051 Argus(优先队列)
- jenkins 通过批处理自动构建 非标准项目
- iPhone/Mac Objective-C内存管理教程和原理剖析(二)口诀与范式转
- url.getinputsteam 获取不完整_年度营销方案合集,活动策划,规划推广,全内容完整套用告别加班...