Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >《JavaScript 模式》读书笔记(6)— 代码复用模式1

《JavaScript 模式》读书笔记(6)— 代码复用模式1

作者头像
zaking
发布于 2020-04-21 03:20:53
发布于 2020-04-21 03:20:53
42800
代码可运行
举报
文章被收录于专栏:zaking'szaking's
运行总次数:0
代码可运行

  我们有开始进入新篇章了。这篇内容主要讲代码复用模式,实际上代码复用,就是继承啊,原型啊,构造函数啊等等这一类的内容。对于前端进阶来说,是很重要的基础知识。这一篇内容会对原型、 继承有很深入的讲解。我也会尽我所能的为大家讲清楚、分析透彻。

  代码复用是一个非常重要而且有趣的主题,简而言之,这是由于人们很自然的争取编写尽可能少的代码。尤其是那些具有质量优秀、通过测试、可维护、可扩展性、文档化的可复用代码。

  在谈及代码复用的时候,首先想到的是代码的继承性(inheritance),而本章中大部分也专门致力于代码复用这个主题。在这里可以看到多张方法都可以实现“基于类特性的(calssical)”和“非基于类特性的(nonclassical)”继承特性。但重要的是要记住其最终目标,我们要复用代码。继承性就是程序员用以实现代码复用这个目标的一种方法或手段,而且它也并不是唯一的方法。在本章中,可以看到如何利用其他对象组合成所需的对象,也可以看到如何使用mix-in技术(混入或者渗元技术),还可以看到如何在技术上没有永久继承的情况下仅借用和复用所需的功能。

  当开始接触代码复用任务时,请记住GoF(Gang of Four,指《Design Patterns》的四位作者)等人在其著作中提出的有关创建对象的建议原则“优先使用对象组合,而不是类继承”。

一、传统与现代继承模式的比较

经常会在有关JavaScript继承模式的主题讨论中听到术语“传统继承(classical inheritance)”,为此,让我们先阐明“传统(classical)”所代表的意义。该术语从词义上来说,并不是与用于古董、历史沉积、或者广为接受并认定为正确的处理事务的方法这些词义相同。实际上,该术语只是单词“Class(类)”的一种表现形式。

许多编程语言都具有类的概念,并以此作为对象的蓝图。在那些编程语言中,每个对象都是一个类的特定实例(比如,Java语言环境中),并且在不存在某个类的时候并不能创建该类的对象。在JavaScript中,由于没有类的概念,因此实例的概念也就没有多大的意义。JavaScript中的对象是简单的键-值(key-value)对,可以动态的创建和修改这些对象。

  但JavaScript具有构造函数,并且new操作符的语法与那些使用类的编程语言在语法上有许多相似之处。

  在Java中可以采用下列方式创建对象:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Person adam = new Person();

  而在JavaScript中则可以采用下列方式创建对象:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var adam = new Person();

  除了与Java中强类型限制的情况不同之外,在JavaScript中也必须声明adam是Person类型,其语法与Java看起来是一样的。JavaScript的构造函数在调用时Person看起来似乎是一个类,但重要的是要记住Person仍然只是一个函数。这种语法伤的相似性导致了很多程序员按照类的方式考虑JavaScript,并产生了一些假定在类的基础上的开发思路和继承模式。我们将这种实现方式称之为“类式(classical)”继承模式。在这里让我们将“现代(modern)”模式表述为:其他任何不需要以类的方式考虑的模式。

  在项目中,首先建议使用现代模式,除非你或你的团队真的不适应这样。本章先讨论类式继承模式,再讨论其他现代模式。

二、使用类式继承时的预期结果

实现类式继承(classical inheritance)的目标是通过构造函数Child()获取来自于另一个构造函数Parent()的属性,从而创建对象。

注意:虽然这里讨论的是类式继承模式,但是请让我们尽量避免使用“class(类)”这个单词。将其表述为“构造函数(constructor function 或 constructor)”时虽然字数更长一些,但是其表述更为精确且不会产生歧义。一般情况下,在开发团队交流时请努力消除单词“class”的使用,因为当涉及JavaScript时,“class”这个词对于不同的人可能意味着不同的含义。

  下面是定义两个构造函数Parent()和Child()的一个例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 父构造函数
function Parent(name) {
    this.name = name || 'Adam';
}

// 向该原型添加功能
Parent.prototype.say = function () {
    return this.name;
};

// 空白的子构造函数
function Child() {}

// 继承的魔力在这里发生
inhert(Child,Parent);

  上面的方法中,存在父、子两个构造函数,say()方法被添加到父构造函数的原型(prototype)中,并且一个名为inherit()的函数调用负责处理它们之间的继承关系。其中,inherit()函数并非由编程语言提供的,为此,程序员必须自己来实现该函数。下面,让我们看看实现该函数的几种方法。

三、类式继承模式#1——默认模式

最常用的一种默认方法是使用Parent()构造函数创建一个对象,并将该对象赋值给Child()的原型。下面是可复用继承函数inherit()的第一种用法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function inhert(C,P) {
    C.prototype = new P();
}

  重要的是需要记住,原型的属性应该指向一个对象,而不是一个函数,因此它必须指向一个由父构造函数所创建的实例(一个对象),而不是指向构造函数本身。也就是说,要注意使用new操作符来创建新对象,因为需要new才能使用这种模式。

在以后的程序中,当使用new Child()语句创建一个对象时,它会通过原型从Parent()实例中获取它的功能,如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var kid = new Child();
kid.say();

追溯原型链

使用这种默认的继承模式时,同时继承了自身的属性(即,加入到this的实例相关属性,比如name),以及原型属性和方法【比如say()】。

  让我们回顾一下在这种继承模式下原型链的工作原理。出于讨论的目的,让我们将对象视做存在于内存中某处的块,该内存块可以包含数据以及指向其他块的引用。

  当使用new Parent()语句创建一个对象时,会创建一个这样的块,如下图所示中的#2块。

在#2块中保存了name属性的数据。如果您尝试访问say()方法,虽然块#2中并不包含say()方法,但是通过使用指向构造函数Parent()的prototype(原型)属性的隐式链接__proto__,便可以访问对象#1(Parent.prototype),而对象#1又确实知道关于say()的地址。所有这一切都在后台发生,并不用为这种复杂的原型链而烦恼,重要的是需要理解它的工作原理以及所需要访问或可能修改的数据位于何处。请注意,这里仅使用__proto__来解释原型链,即使在一些环境中提供了该属性,在程序开发语言中也并不能使用该属性。

现在,让我们来看一下在使用inherit()函数后,当使用var kid = new Child()创建新对象时会发生什么情况,如下图所示:

  从上图可以看出,child()构造函数是空的,并且没有任何属性添加到Child.prototype中。因此,使用new Child()语句所创建的对象除了包含隐式链接__proto__以外,它几乎是空的。在这种情况下,__proto__指向了在inherit()函数中使用new Parent()语句所创建的对象。

  现在,当执行kid.say()时会发生什么情况?对象#3中并没有这样的say()方法,因此它将通过原型链查询到#2.然而,#2中也没有该方法,因此它又顺着原型链查询到对象#1,而对象#1正好具有say()方法。然而,在say()中引用了this.name,该引用仍然还需要解析。因此,查询再次启动。在这种情况下,this指向对象#3,对象#3中没有name属性。为此,将查询对象#2,而对象#2中确实有name属性,其值为“Adam”。

  最后,让我们更进一步查看原型链的概念,比如说,我们有以下这样的代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var kid = new Child();
kid.name = 'Patrick'
kid.say();

  下图显示了上述这种情况下原型链的工作过程。

  设置kid.name语句并不会修改对象#2的name属性,但是它却直接在kid对象#3上创建了一个自身的属性。当执行kid.say()时,将依次在对象#3、对象#2中查询say()方法,并且最终在对象#1中找到该放啊,这与前面所描述的过程相似。但是,如果这次是查找this.name(这是与kid.name相同的),那么其过程是很快的,这是由于该属性立刻就能够在对象#3中找到,而无需通过原型链。

  如果使用delee kid.name语句删除新属性,那么对象#2的name属性将会“表现出来”,并在连续的查找过程中找到其name属性。

使用模式#1时的缺点

本模式的其中一个缺点在于:同时继承了两个对象的属性,即添加到this的属性以及原型属性。在绝大多数的时候,并不需要这些自身的属性(比如这里的name),因为它们很可能是指向一个特定的实例,而不是复用。

注意,对于构造函数的一般经验法则是:应该将可复用的成员添加到原型中。

  另一个关于使用通用inherit()函数的问题在于它并不支持将参数传递到子构造函数中,而子构造函数然后又将参数传递到父构造函数中,考虑以下这个例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var s = new Child('Seth');
s.say(); // 输出“Adam”

  以上的输出结果可能并不是您所期望的。虽然子构造函数可以将参数传递到父构造函数中,但是那样的话,在每次需要一个新的子对象时都必须重新执行这种继承机制,而且该机制的效率时很低的,其原因在于最终会反复的重新创建父对象。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-04-19 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
《JavaScript 模式》读书笔记(6)— 代码复用模式2
  上一篇讲了最简单的代码复用模式,也是最基础的,我们普遍知道的继承模式,但是这种继承模式却有不少缺点,我们下面再看看其它可以实现继承的模式。
zaking
2020/04/21
3610
《JavaScript 模式》读书笔记(6)— 代码复用模式2
《JavaScript 模式》读书笔记(6)— 代码复用模式2「建议收藏」
  上一篇讲了最简单的代码复用模式,也是最基础的,我们普遍知道的继承模式,但是这种继承模式却有不少缺点,我们下面再看看其它可以实现继承的模式。
全栈程序员站长
2022/07/21
2320
《JavaScript 模式》读书笔记(6)— 代码复用模式2「建议收藏」
《JavaScript 模式》读书笔记(6)— 代码复用模式3
我们之前聊了聊基本的继承的概念,也聊了很多在JavaScript中模拟类的方法。这篇文章,我们主要来学习一下现代继承的一些方法。
zaking
2020/05/01
5380
深入理解JavaScript系列(45):代码复用模式(避免篇)
任何编程都提出代码复用,否则话每次开发一个新程序或者写一个新功能都要全新编写的话,那就歇菜了,但是代码复用也是有好要坏,接下来的两篇文章我们将针对代码复用来进行讨论,第一篇文避免篇,指的是要尽量避免使用这些模式,因为或多或少有带来一些问题;第二排是推荐篇,指的是推荐大家使用的模式,一般不会有什么问题。
用户4962466
2019/12/19
3110
《现代Javascript高级教程》JavaScript中的原型与继承
JavaScript是一门支持面向对象编程的语言,它的函数是第一公民,同时也拥有类的概念。不同于传统的基于类的继承,JavaScript的类和继承是基于原型链模型的。在ES2015/ES6中引入了class关键字,但其本质仍然是基于原型链的语法糖。
linwu
2023/07/27
2560
《现代Javascript高级教程》JavaScript中的原型与继承
深入理解JavaScript系列(46):代码复用模式(推荐篇)
同时,ECMAScript5也提供了类似的一个方法叫做Object.create用于继承对象,用法如下:
用户4962466
2019/12/19
3380
这些JS设计模式的基础知识点你都会了吗?
笔者对于JavaScript中如何实现继承、多态的知识一直比较零散,遂尽可能地全面由表及里地梳理相关内容。
小东同学
2022/07/29
3680
这些JS设计模式的基础知识点你都会了吗?
JS完美收官之——继承发展史
代码复用一直是我们程序员所追求的远大目标,毕竟可以少写点代码,何乐而不为呢?当说到代码复用的时候,最先想到的是继承,JavaScript对象上有自己的属性,也有一些属性是从原型对象继承来的,下面我们来看看实现继承的几种方式:
程序员法医
2022/08/11
4100
JS完美收官之——继承发展史
OOP in Javascript
写了几篇Vue入门的内容了,今天写点其它的放松一下,简单讲讲javascript中的面相对象。 在面向对象的语言中,都有类的概念,当然es6中开始javascript中也有类的概念了,这里以es5为基础开始讲解,毕竟当前即使写的es6代码,一般还是会通过babel等转码器翻译到es5来执行的; 在js中虽然没有类的概念,但是我们却可以创建对象,一般创建对象有两种方式(这里指自定义对象): 1、使用构造函数 function Person(){ } var p=new Person(); 2、使用字面量
Jerremy
2018/06/13
4990
JavaScript 面向对象继承详解
由于js不像java那样是完全面向对象的语言,js是基于对象的,它没有类的概念。所以,要想实现继承,一般都是基于原型链的方式;
书童小二
2018/09/03
5030
JavaScript 面向对象继承详解
聊聊JavaScript如何实现继承
“继承”是面向对象编程里面经常提及到的概念,它的目的是实现代码复用。JavaScript并没有“类”的概念,那么,它如何实现继承呢? (ES6有关键字class和extend,继承的语法与Java等面向对象语言类似,但是,ES6 class,只是JavaScript原型继承的语法糖而已)
娜姐
2022/05/13
3160
JavaScript的几种继承方式
这篇文章称为笔记更为合适一些,内容来源于 《JavaScript高级程序设计 (第三版)》第六章 6.3 继承。
木子星兮
2020/07/16
5240
「思维导图学前端 」一文搞懂Javascript对象,原型,继承
去年开始我给自己画了一张知识体系的思维导图,用于规划自己的学习范围和方向。但是我犯了一个大错,我的思维导图只是一个全局的蓝图,而在学习某个知识点的时候没有系统化,知识太过于零散,另一方面也很容易遗忘,回头复习时没有一个提纲,整体的学习效率不高。意识到这一点,我最近开始用思维导图去学习和总结具体的知识点,效果还不错。试想一下,一张思维导图的某个端点是另一张思维导图,这样串起来的知识链条是多么“酸爽”!当然,YY一下就好了,我保证你没有足够的时间给所有知识点都画上思维导图,挑重点即可。
程序员白彬
2020/07/10
7750
「思维导图学前端 」一文搞懂Javascript对象,原型,继承
JavaScript进阶-原型链与继承
在JavaScript中,原型链和继承是理解对象间关系和实现代码复用的核心概念。这两个机制共同构成了JavaScript面向对象编程的基础。本文将深入浅出地探讨原型链与继承的工作原理、常见问题、易错点及其避免策略,并通过具体代码示例加以说明。
Jimaks
2024/06/19
1950
JavaScript进阶-原型链与继承
JavaScript中原型与原型链的简单理解
讲原型和原型链,如果是讲定义,那很是晦涩难懂,今天我们就通俗易懂的说说原型与原型链。还需要借助阮老师的“Javascript继承机制的设计思想”。
青年码农
2021/03/23
4130
javascript基础知识
对象是一个包含相关数据和方法的集合(通常由一些变量和函数组成,我们称之为对象里面的属性和方法)。在其他经典的面向对象的语言中我们并没有办法直接去创建对象,我们都需要先用class这样的关键词创建一个对象模板(被称为类),然后调用类的构造函数去初始化一个对象出来,在类中描述的属性和方法会复制一份到对象中去,然而对于javascript来说,情况并非如此(不像“经典”的面向对象的语言,从构建函数创建的新实例的特征并非全盘复制,而是通过一个叫做原形链的参考链链接过去的,所以这并非真正的实例,严格的讲, JavaScript 在对象间使用和其它语言的共享机制不同,这个后面再讲),我们可以直接创建一个对象而不需要对象模板,创建方法请参考第二节。
用户1879329
2023/02/27
2800
javascript基础知识
JavaScript入门笔记(6)标准对象面向对象编程
标准对象 正则对象 正则表达式是一种处理文本信息的神器,在JavaScript中可以方便的使用正则对象对文本进行处理。JavaScript中声明正则对象可以使用var name = /.../和var name = new RegExp("..."),第二种方法个人是不推荐的,要处理一系列的文本转义,正则表达式定以后可以使用.test()方法检验是否有匹配和.exec()方法分组 var re_test = /\w+@\w+\.\w+/ console.log(re_test) ///\w+@\w+\.\w
月见樽
2018/04/27
7810
【THE LAST TIME】一文吃透所有JS原型相关知识点
首先我想说,【THE LAST TIME】系列的的内容,向来都是包括但不限于标题的范围。
Nealyang
2019/11/04
1.1K0
JavaScript(高级)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
神葳
2021/01/22
9020
一篇文章告诉你JavaScript 如何实现继承
JavaScript 在编程语言界是个特殊种类,它和其他编程语言很不一样,JavaScript 可以在运行的时候动态地改变某个变量的类型。
前端皮皮
2021/11/12
2100
一篇文章告诉你JavaScript 如何实现继承
推荐阅读
相关推荐
《JavaScript 模式》读书笔记(6)— 代码复用模式2
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验