《JavaScript设计模式与开发实践》知识点笔记
1、javaScript没有提供传统面向对象语言中的类式继承,而是通过原型委托的方式来实现对象与对象之间的继承。
2、静态类型语言在编译时便已确定变量的类型,而动态类型语言的变量类型要到程序运行的时候,待变量被赋予某个值后,才会具有某种类型。
3、JavaScript是一门典型的动态类型语言
4、利用鸭子类型的思想,我能在动态类型语言中实现一个原则:“面向接口编程,而不是面向实现编程”
5、多态:同一操作作用于不同的对象上面,可以产生不同的解释和不同的执行结果。
6、多态背后的思想是将“做什么”和“谁去做以及怎样去做”分离开来,也就是将“不变的事物”与“可能改变的事物”分离开来。
7、多态的最根本的好处在于,你不必再向对象询问“你是什么类型”而后根据得到的答案调用对象的某个行为——你只管调用该行为就是了,其他的一切多态机制都会为你安排妥当。
8、从设计模式的角度出发, 封装在更重要的层面体现为封装变化
9、创建型模式,结构型模式,行为型模式
10、JavaScript选择基于原型的面向对象系统
11、原型模式不关心对象的具体类型,而是找到一个对象,然后通过克隆来创建一个一模一样的对象
12、原型模式的实现关键,是语言本身是否提供了clone方法,ECMAScript5提供了Object.create方法,可以用来克隆对象
13、所有的JavaScript对象都是从某个对象上克隆而来的
14、原型编程中的一个重要特性:当对象无法响应某个请求时,会把该请求委托给自己的原型
15、原型编程泛型至少包括以下基本规则:
1.所有的数据都是对象
2.要得到一个对象,不是通过实例化类,而是找到一个对象做为原型并克隆它
3.对象会记住它的原型
4.如果对象无法响应某个请求,它会把这个请求委托给它自己的原型
16、JavaScript中的根对象是Object.prototype对象,该对象是一个空对象
17、用new运算符来创建对象的过程,实际上也只是先克隆Object.prototype对象,再进行一些其他额外操作的过程
18、对于“对象把请求委托给它自己的原型”这句话,更好的说法是对象把请求委托给它的构造器的原型
19、__proto__就是对象跟“对象构造器的原型”联系起来的纽带,__proto__指向构造器的prototype
20、原型链终点,Object.prototype,也就是null
21、设计模式在很多时候其实都体现了语言的不足之处,如果要使用设计模式,不如去找一门更好的语言。
22、this具体指向哪个对象是在运行时基于函数的执行环境动态绑定的,而非函数被声明时的环境
23、借用构造方法
24、使用Array.prototype.push方法需要满足两个条件:
1.对象本身要可以存取属性
2.对象的length属性可读写
25、命令模式的意图是把请求封装为对象,从而分离请求的发起者和请求的接收者(执行者)之间的耦合关系。在命令被执行之前,可以预先往命令对象中植入命令的接收者。
26、高阶函数是指至少满足下列条件之一的函数:
1.函数可以作为参数被传递
2.函数可以作为返回值输出
27、回调函数的应用不仅只在异步请求中,当一个函数不适合执行一些请求时,我们也可以把这些请求封装成一个函数,并把它作为参数传递给另外一个函数,“委托”给另一个函数来执行。
28、AOP(面向切面编程)的主要作用是把一些跟核心业务逻辑模块无关的功能抽离出来,这些跟业务逻辑无关的功能通常包括日志统计,安全控制,异常处理等。
29、JavaScript实现AOP,都是指把一个函数“动态织入”到另一个函数中。
30、装饰者模式精华思想:装饰者对象和它所装饰的对象拥有一致的接口,所以它们对使用该对象的客户来说是透 明的,被装饰的对象也并不需要了解它曾经被装饰过,这种透明性使得我们可以递归地嵌套任意 多个装饰者对象 ,有点类似递归的思想
31、要实现一个标准的单例模式并不复杂,无非是用一个变量来标志当前是否已经为某个类创建过对象,如果是,则在下一次获取该类的实例时,直接返回之前创建的对象。
32、将不变的部分和变化的部分隔开是每个设计模式的主题
33、策略模式的目的就是将算法的使用与算法的实现分离开来。
34、一个基于策略模式的程序至少由两部分组成。第一个部分是一组策略类,策略类封装了具体的算法,并负责具体的计算过程。第二部分是环境类Context,Context接受客户的请求,随后把请求委托给一个策略类。要做到这点,说明Context中要维持对某个策略对象的引用。
35、策略模式的思想:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。其实也就是把多条if封装成多条路径,传入不同的参数,执行不同的路径,每条路径策略不同,但是大体目的都是一致的。
36、策略模式的实现并不复杂,关键是如何从策略模式的实现背后,找到封装变化、委托和多态性这些思想的价值
37、从定义上看,策略模式就是用来封装算法的。但如果把策略模式仅仅用来封装算法,未免有点大材小用。在实际开发中,我们通常会把算法的含义扩散开来,是策略模式也可以用来封装一系列的“业务规则”。只要这些业务规则指向的目标一致,并且可以被替换使用,我们就可以使用策略模式来封装它们。
37、代理模式是为一个对象提供一个代用品或占位符,以便控制对它的访问。
38、代理模式的关键是,当客户不方便直接访问一个对象或者不满足需要的时候,提供一个替身对象来控制对这个对象的访问,客户实际上访问的是替身对象,替身对象请对请求做出一些处理之后,再把请求转交给本体对象。
39、虚拟代理把一些开销很大的对象,延迟到真正需要它的时候才去创建,虚拟代理关键思路就是满足一定的条件了才真正执行原对象的方法。
40、代理就是在代理对象的一个方法中等到合适的时间(比如说图片预加载完),去执行目标对象的方法,这两个方法是同名的,所以对于用户来说这个过程是透明的。代理负责预加载图片,预加载操作完成之后,把请求重新交给本体MyImage。
41、给img节点设置src和图片预加载这两个功能,被隔离在两个对象里,他们可以各自变化而不影响对方。何况就算有一天我们不再需要预加载,那么只需要改成请求本体而不是请求代理对象即可。
42、缓存代理可以为一些开销大的运算结果提供暂时的存储,在下次运算时,如果传递进来的参数跟之前的一致,则可以直接返回前面存储的运算结果。
43、比如说分页请求,我们就可以使用缓存代理来缓存我们已经请求的数据,而不是每次都从后台抓取。
44、代理模式包括许多小分类,在JavaScript开发中最常用的是虚拟代理和缓存代理。虽然代理模式非常有用,但我们在编写业务代码的时候,往往不需要去预先猜测是否需要使用代理模式,当真正发现不方便直接访问某个对象的时候,在编写代理也不迟。因为真正的对象逻辑不会因为代理而改变,代理的目的只是在合适的时间点去执行原对象。
45、
发布-订阅者模式可以取代对象之间硬编码的通知机制,一个对象不用再显式地调用另外一个对象的某个接口。发布-订阅模式让两个对象松耦合地联系在一起,虽然不太清楚彼此的细节,但这不影响他们之间的通信
46、
实现发布-订阅者思路:
1.首先要指定好谁充当发布者(比如售楼处)
2.然后给发布者添加一个缓存列表,用于存放回调函数以便通知订阅者(售楼处的花名册)
3.最后发布消息的时候,发布者会遍历这个缓存列表,一次触发里面存放的订阅者回调函数(遍历花名册,挨个发短信)
47、
发布-订阅模式的优点非常明显,一位时间上的解耦,二为对象之间的解耦。发布-订阅者模式还可以用来帮助实现一些别的设计模式,例如中介者模式。
48、
命令模式应用场景:
有时候需要向某些对象发送请求,但是并不知道请求的接受者是谁,也不知道被请求的操作是什么,此时希望用一种松耦合的方式来设计软件,使得请求发送者和请求接收者能够消除彼此之间的耦合关系。
49、
设计模式的主题总是把不变的事物和变化的事物分离开来,命令模式也不例外。按下按钮之后会发生一些事情是不变的,而具体会发生什么事情是可变的。
50、
命令模式的由来,其实是回调函数的一个面向对象的替代品,回调是直接一个函数,比如说添加。而命令模式是一个对象,里面可以有多种方法,比如说添加和撤销,所以我们要拥有添加和撤销的功能,最好还是封装成command对象来传递,而不是直接传递函数。
51、
命令模式在js中说白了就是,我要执行一个方法,但是我不知道它的名字和具体代码,那么我就执行命令对象通过闭包返回的execute方法返,这个方法返回的就是我们想要的函数。具体例子看129页
52、
撤销是命令模式里一个非常有用的功能,试想一下开发一个围棋程序的时候,我们把每一步棋子的变化都封装成命令,则可以轻易地实现悔棋功能。同样,撤销命令还可以用于实现文本编辑器Ctrl+z功能。
53、
组合模式就是用小的子对象来构建更大的对象,而这些小的子对象本身也许是由更小的“孙对象”构成的
54、
当我们往万能遥控器里面添加一个命令的时候,并不关心这个命令是宏命令还是普通子命令。这点对于我们不重要,我们只需要确定它是一个命令,并且这个命令拥有可执行的execute方法,那么这个命令就可以被添加进万能遥控器。
55、
组合模式命令就在于每层对象都有execute方法,根对象执行execute,就会遍历所有子孙对象,体现出了多态。
56、
每当对最上层的对象进行一次请求时,实际上是在对整个树进行深度优先的搜索
57、
组合模式最大的有点就是在于可以一致的对待组合对象和基本对象
58、
客户希望统一对待树中的所有对象,组合模式使客户可以忽略组合对象和叶对象的区别,客户在面对这棵树的时候,不用关心当前正在处理的对象是组合对象还是叶对象,也就是不用写一堆if、else语句来分别处理他们,组合对象和叶对象会各自做自己正确的事情,这是组合模式的重要能力。
59、
组合模式缺点:系统中的每个对象看起来都与其他对象差不多,他们的区别只有在运行的时候才会凸显出来,这会使代码难以理解。此外,如果通过组合模式创建了太多的对象,那么这些对象可能会让系统担负不起。
60、
基于继承的设计模式——模板方法模式
61、
模板方法模式由两部分结构组成,第一部分是抽象父类,第二部分是具体的实现子类。通常在抽象父类中封装了子类的算法框架,包括实现一些公共方法以及封装子类中所有方法的执行顺序,子类通过继承这个抽象类,也继承了整个算法结构,并且可以选择重写父类的方法。
62、
p154,Beverage.prototype.init被称为模板方法的原因是,该方法中封装了子类的算法框架,它作为一个算法的模板,指导子类以何种顺序去执行哪些方法。
63、
JavaScript不支持抽象类,我们以下面两种变通的方法来处理:
1、用鸭子类型来模拟接口检查,以确保子类中确实重写了父类的方法。
2、让Beverage.prototype.brew等方法抛出异常,如果因为粗心忘记编写Coffee.prototype.brew方法,那么至少我们会在程序运行时得到一个错误。
64、
放置钩子是隔离变化的一种常见手段。我们在父类中容易变化的地方放置钩子,钩子可以有一个默认的实现,究竟要不要“挂钩”,这由子类自行决定。钩子方法返回结果决定了模板方法后面部分的执行步骤,也就是程序接下来的走向,这样一来,程序就拥有了变化的可能。
65、
用模板方法模式编写一个程序时,就意味着子类放弃了对自己的控制权,而是改为父类通知子类,哪些方法应该在什么时候被调用。做为子类,只负责提供一些设计上的细节。
66、
在JavaScript中,我们很多时候都不需要依样画瓢地去实现一个模板方法模式,高阶函数是更好的选择。
67、
享元模式的核心是运用共享技术来有效支持大量细粒度的对象。
68、
享元模式要求将对象的属性划分为内部状态与外部状态(状态在这里通常指属性)。享元模式的目标是尽量减少共享对象的数量,关于如何划分内部状态和外部状态,下面的几条经验提供了一些指引。
1、内部状态存储于对象内部。
2、内部状态可以被一些对象共享
3、内部状态独立于具体的场景,通常不会改变
4、外部状态取决于具体的场景,并根据场景而变化,外部状态不能被共享
69、
剥离了外部状态的对象称为共享对象,外部状态在必要时被传入共享对象来组装成一个完整的对象。
70、
享元模式是一种用时间换空间的优化模式
71、
享元模式的关键是如何区别内部状态和外部状态
72、
享元模式适用场景:
1、一个程序中使用了大量的相似对象
2、由于使用了大量对象,造成很大的内存开销
3、对象的大多数状态都可以变为外部状态
4、剥离出对象的外部状态之后,可以用相对较少的共享对象取代大量对象
73、
当对象没有内部状态的时候,生产共享对象的工厂实际上变成了一个单例工厂。
74、
对象池跟祥元模式有一些相似之处,但没有分离内部状态和外部状态这个过程。
75、
享元模式是为解决性能问题而生的模式,这跟大部分模式的诞生原因都不一样
76、
职责链模式的定义是:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止
77、
职责链模式最大优点:请求发送者只需要知道链中的第一个节点,从而弱化了发送者和一组接收者之间的强联系。
78、
用AOP来试下职责链既简单又巧妙,但这种把函数叠在一起的方式,同时也叠加了函数的作用域,如果链条太长的话,也会对性能有较大的影响。
79、
中介者模式的作用就是解除对象与对象之间的紧耦合关系
80、
如果对象之间的复杂耦合确实导致调用和维护出现了困难,而且这些耦合度随项目的变化呈指数增长曲线,那我们就可以考虑用中介者模式来重构代码
81、
状态模式的关键是区分事物内部状态,事物内部状态的改变往往会带来事物的行为改变。
82、
状态模式的好处很明显,它可以使每一种状态和它对应的行为之间的关系局部化,这些行为被分散和封装在各自对应的状态类中,便于阅读和管理代码
83、
状态模式定义:
允许一个对象在其内部状态改变时改变他的行为,对象看起来似乎修改了它的类
84、
策略模式和状态模式的相同点是,它们都有一个上下文,一些策略或者状态类,上下文把请求委托给这些类来执行
85、
策略模式和状态模式的不同点是,策略模式中的各个策略类之间是平等又平行的,它们之间没有任何联系, 所以客户必须熟知这些策略类的作用,以便客户可以随时主动切换算法;而在状态模式中,状态 和状态对应的行为是早已被封装好的,状态之间的切换也早被规定完成,“改变行为”这件事情 发生在状态模式内部。对客户来说,并不需要了解这些细节。这正是状态模式的作用所在。
86、
适配器模式的作用是解决两个软件实体间的接口不兼容的问题,使用适配器模式之后,原本由于接口不兼容而不能工作的两个软件实体可以一起工作
87、
如果现有的接口已经能够正常工作,那我们就永远不会用上适配器模式,适配器模式是一种“亡羊补牢”的模式,没有人会在程序设计之初就使用它
88、
常见的设计原则:单一职责原则,里氏替换原则,依赖倒置原则,接口隔离原则,合成复用原则,最少知识原则
89、
开放-封闭原则:软件实体等应该是可以拓展的,但是不可修改
90、
用对象的多态性消除条件分支
放置挂钩也是分离变化的一种方式
91、
在js中,策略模式和命令模式等都可以用回调函数轻松实现
92、
发布——订阅者模式用来降低多个对象之间的依赖关系,他可以取代对象之间硬编码的通知机制,一个对象不用再显式地调用另外一个对象的接口。