《JavaScript设计模式与开发实践》读书笔记 一

一本非常值得推荐的书,看了前几章觉得收获还挺大的,于是整理成了笔记。本篇博客主要是一到三章的内容,也是书籍所分的第一部分,基础知识,后续内容会尽快更新的。
主要内容:动态类型语言、鸭子类型、this、call、apply、闭包、高阶函数。
第一章、面向对象的JavaScript
- 动态类型语言和鸭子类型
- 编程语言按照数据类型大体可以分为两类,静态类型语言和动态类型语言
- 静态类型语言在编译时就能发现类型不匹配的错误,规定了数据类型,编译器还可以对程序进行一些优化,提高程序执行的速度,缺点是会增加更多的代码
- 动态类型语言的优点是编写的代码数量更少,专注于逻辑表达,缺点是无法保证变量的类型
- 鸭子类型:关注对象的类型,而不关注对象的本身
- 根据鸭子类型的思想,我们不必借助于超类,就可以轻松的实现在动态类型语言中实现的一个原则:‘面向接口编程,而不是面向实现编程’
- 编程语言按照数据类型大体可以分为两类,静态类型语言和动态类型语言
- 多态
- 含义:同一操作作用于不同的对象上面,可以产生不同的结果
- 多态背后的思想是将‘做什么’和‘说去做以及怎样做’分离开来,也就是将不变的事物和可能改变的事物分离开来
- JavaScript对象的多态性是与生俱来的
- 面向对象设计的优点是将行为分布在各个对象中,并让这些独享负责自己的行为
- 封装
- 封装的目的是将信息隐藏
- 封装数据只能模拟出public private两种封装性,函数创建作用域,let,通过symbol创建私有属性
- 封装的实现:封装使得对象内部的变化对其他对象而言是透明的,也就是不可见的,对象对自己的行为负责。其他对象或者用户都不关心它的内部实现,使得对象之间的耦合变松散
- 封装是静态语言中一种重要的封装方式,一般把对象真正类型隐藏在抽象类和接口之后,相比对象类型,客户更关心对象的行为,促使工厂方法模式,组合模式的产生,javaScript中没有对抽象类和接口进行支持,没有能力进行封装。
- 原型模式和基于原型继承的JavaScript对象系统
- 使用克隆的原型模式
- 克隆是创建对象的手段
- 原型编程范型的规则
- 所有数据都要是对象
- 要得到一个对象不是通过实例化类,而是找到一个对象作为原型克隆他
- 对象会记住它的原型
- 如果对象无法响应某个请求,他会把这个请求委托给自己的原型
- JavaScript的原型继承
- 所有数据都是对象:除了undefined,一切都应是对象。javascript引入两套类型机制,基本类型和对象类型,number,boolean,string数据类型可以通过包装类的方式变成对象数据类型
- javaScript的根对象是Object.prototype对象,Object.type对象是一个空对象,javascript创建每个对象,实际都是从它克隆来的
- 要得到一个对象,不是通过实例化,而是找到一个对象作为原型并克隆它
- javaScript中没有类的概念,javaScript函数可以作为普通函数被调用也可以作为构造器被调用,用new运算符来调用时,此时的函数是构造器
- new运算符创建对象的过程是先克隆Object.prototype对象,再进行其他额外操作(为了性能,并不是每次都新克隆对象),过程:先克隆一个对象,取得外部传入的构造器,将对象指向正确的原型,借用外部传入的构造器给obj设置属性,确保是对象并将对象返回
- 对象会记住它的原型
- 仔细的说是记住对象的构造器的原型,把请求委托给构造器的原型
- __proto__指向该对象的构造器的原型
- 如果对象无法响应某个请求,他会把这个请求委托给它的构造器的原型
//实现一个类继承自另一个类的效果 var A=function(){} A.prototype={name:'sven'} Var B=function(){} B.prototype=new A() var B=new B() console.log(B.name)-
原型继承的未来
设计模式是对语言不足的补充,
Object.create()是原型模式的天然实现,使用它完成原型继承更能体现原型继承的精髓,但是Object.create()创建对象的效率并不高,比构造函数要慢,通过设置构造器的prototype来实现原型继承的时候,除了跟对象外,任何对象都会有一个原型,而通过Object.create(null)可以创建出没有原型的对象
- 所有数据都是对象:除了undefined,一切都应是对象。javascript引入两套类型机制,基本类型和对象类型,number,boolean,string数据类型可以通过包装类的方式变成对象数据类型
- 原型模式是一个设计模式,也是一种编程范型,构成了javaScript语言的根本,原型模式十分重要,通过原型模式来实现面向对象系统简单而强大
第二章、this,call,apply
-
this
- this的指向:
- 作为对象的方法调用
- 作为普通函数调用
- 构造器调用
- Function.prototype.call或Function.prototype.apply调用
- 作为对象的方法调用:this指向该对象
var obj={ a;1, getA:function(){ alert(this===obj) //true alert(this.a) //1 } } obj.getA(); - 作为普通函数调用:this不作为对象的属性被调用时,普通函数方式时,此时this总指向全局对象,window
window.name='globalName' var myObj={ name:'even', getName:function(){ return this.name } } var getName=myObj.getName; console.log(getName()) //globalName- 构造器调用:javaScript中没有类,但是可以从函数构造器中创建对象,也提供了new,使构造函数更像一个类,构造函数和普通函数的区别就是调用方式,用new运算符调用函数时,该函数总会返回一个对象,构造器里的this就指向返回的这个函数,除非构造器显示地返回了一个object类型的对象
4.Function.prototype.call或Function.prototype,apply调用
丢失的this
- this的指向:
-
call和apply
- 区别:作用一模一样,区别仅在于传入参数形式不同,第一个函数指定了函数体this对象的指向,第二个参数为一个带下标的集合,可以是数组,也可以是类数组,javaScript参数在内部就是一个数组来表示,apply比call的使用率更高,call是包装在apply上面的一层语法糖
- 用途:
- 改变this指向
- Function.prototype.bind
- 借用其他对象的方
第三章、闭包和高阶函数
JavaScript是一门面向对象的编程语言,但是这门语言同时也拥有许多函数式语言的特性
函数式语言的鼻祖是LISP,JavaScript之初参考了Scheme
许多设计模式都是用闭包和高阶函数实现的
1.闭包
- 变量的作用域:变量的作用域指变量的有效范围,声明变量,如果变量前面没有带var,就是全局变量
var a=1;
var fun1=function(){
var b=2;
var fun2=function(){
var c=3;
alert(b); //2
alert(a); //1
};
fun2();
alert(c); // c is undefined
}
func1();
-
变量的生存周期
- 对于全局变量,生存周期是永久的,除非主动销毁这个变量,对于由var 声明的局部变量,会随着函数调用的结束而被销毁。】
var func=function(){ var a=1; return function(){ a++; alert(a); } } var f=func(); f() //2 f() //3 // 因为执行`var f= func()`时,f返回了一个匿名函数的引用,它可以访问到func()被调用时产生的环境,局部变量a一直处于这个环境,所以局部变量a不会被销毁。产生了一个闭包结构 -
闭包的更多作用
- 封装变量
- 闭包可以帮助把一些不需要暴露在全局的变量封装成"私有变量",避免这个变量在其他地方被修改造成错误
- 提炼函数是代码重构的技巧,常常把能够独立出来的代码封装在独立的小函数里,如果小函数不需要复用,最好用闭包将其封闭起来
- 延续局部变量的寿命
var report=(function(){ var imgs=[]; return function(src){ var img=new Image(); imgs.push(img); img.src=src; } })(); //解决请求丢失的问题 - 封装变量
-
闭包和面向对象设计
- 过程与数据的结合是形容面向对象中的“对象”经常使用的表达
- 对象以方法的形式包含了过程,闭包是在过程中以环境的形式包含了数据,通常用面向对象可以实现的功能,闭包也可以实现
-
用闭包实现命令模式
- 命令模式的意图是把请求封装为对象,从而分离请求的发起者和请求的接收者。
// -
闭包与内存管理
- 闭包是一个非常大的特性,但有很多误解
- 闭包容易造成循环引用,如果此时作用域链中保存着DOM节点,就有可能造成内存泄露,这并不是闭包和浏览器的问题,也不是JavaScript的问题,由于BOM和DOM中的对象是使用C++以COM实现,COM对象的垃圾回收机制采用的是引用计数,如果两个对象之间形成了循环引用,那么两个对象就都不能被回收,本质上不是闭包造成的。
- 解决:把循环引用中的变量设为null,设置为null表示切断与它此前引用的值的联系
2.高阶函数
高阶函数:函数可以作为参数被传递,函数可以作为返回值输出
- 函数作为参数传递
- 回调函数
- Array.prototype.sort
- 函数作为返回值输出
- 判断数据的类型
- getSingle
- 高阶函数实现AOP(面向切面编程)
- 面向切面编程的主要作用是把一些跟核心业务无关的功能抽离出来,例如日志统计,安全控制,异常处理等,抽离出来后通过动态织入的方式掺入业务逻辑中,好处是可以保持业务逻辑模块的纯净和高内聚性,其次是可以很方便的复用日志统计等模块儿
- 高阶函数的其他应用
- currying
- uncurrying
- 函数节流
- 分时函数
- 惰性加载函数
注:本篇博客主要内容来自书籍原文

浙公网安备 33010602011771号