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

一本非常值得推荐的书,看了前几章觉得收获还挺大的,于是整理成了笔记。本篇博客主要是一到三章的内容,也是书籍所分的第一部分,基础知识,后续内容会尽快更新的。

主要内容:动态类型语言、鸭子类型、this、call、apply、闭包、高阶函数。

第一章、面向对象的JavaScript

  1. 动态类型语言和鸭子类型
    • 编程语言按照数据类型大体可以分为两类,静态类型语言和动态类型语言
      • 静态类型语言在编译时就能发现类型不匹配的错误,规定了数据类型,编译器还可以对程序进行一些优化,提高程序执行的速度,缺点是会增加更多的代码
      • 动态类型语言的优点是编写的代码数量更少,专注于逻辑表达,缺点是无法保证变量的类型
    • 鸭子类型:关注对象的类型,而不关注对象的本身
    • 根据鸭子类型的思想,我们不必借助于超类,就可以轻松的实现在动态类型语言中实现的一个原则:‘面向接口编程,而不是面向实现编程’
  2. 多态
    • 含义:同一操作作用于不同的对象上面,可以产生不同的结果
    • 多态背后的思想是将‘做什么’和‘说去做以及怎样做’分离开来,也就是将不变的事物和可能改变的事物分离开来
    • JavaScript对象的多态性是与生俱来的
    • 面向对象设计的优点是将行为分布在各个对象中,并让这些独享负责自己的行为
  3. 封装
    • 封装的目的是将信息隐藏
    • 封装数据只能模拟出public private两种封装性,函数创建作用域,let,通过symbol创建私有属性
    • 封装的实现:封装使得对象内部的变化对其他对象而言是透明的,也就是不可见的,对象对自己的行为负责。其他对象或者用户都不关心它的内部实现,使得对象之间的耦合变松散
    • 封装是静态语言中一种重要的封装方式,一般把对象真正类型隐藏在抽象类和接口之后,相比对象类型,客户更关心对象的行为,促使工厂方法模式,组合模式的产生,javaScript中没有对抽象类和接口进行支持,没有能力进行封装。
  4. 原型模式和基于原型继承的JavaScript对象系统
    • 使用克隆的原型模式
    • 克隆是创建对象的手段
    • 原型编程范型的规则
      • 所有数据都要是对象
      • 要得到一个对象不是通过实例化类,而是找到一个对象作为原型克隆他
      • 对象会记住它的原型
      • 如果对象无法响应某个请求,他会把这个请求委托给自己的原型
    • JavaScript的原型继承
      1. 所有数据都是对象:除了undefined,一切都应是对象。javascript引入两套类型机制,基本类型和对象类型,number,boolean,string数据类型可以通过包装类的方式变成对象数据类型
        • javaScript的根对象是Object.prototype对象,Object.type对象是一个空对象,javascript创建每个对象,实际都是从它克隆来的
      2. 要得到一个对象,不是通过实例化,而是找到一个对象作为原型并克隆它
        • javaScript中没有类的概念,javaScript函数可以作为普通函数被调用也可以作为构造器被调用,用new运算符来调用时,此时的函数是构造器
        • new运算符创建对象的过程是先克隆Object.prototype对象,再进行其他额外操作(为了性能,并不是每次都新克隆对象),过程:先克隆一个对象,取得外部传入的构造器,将对象指向正确的原型,借用外部传入的构造器给obj设置属性,确保是对象并将对象返回
      3. 对象会记住它的原型
        • 仔细的说是记住对象的构造器的原型,把请求委托给构造器的原型
        • __proto__指向该对象的构造器的原型
      4. 如果对象无法响应某个请求,他会把这个请求委托给它的构造器的原型
      //实现一个类继承自另一个类的效果
      var A=function(){}
      A.prototype={name:'sven'}
      
      Var B=function(){}
      B.prototype=new A()
      
      var B=new B()
      console.log(B.name)
      
      1. 原型继承的未来

        设计模式是对语言不足的补充,Object.create()是原型模式的天然实现,使用它完成原型继承更能体现原型继承的精髓,但是Object.create()创建对象的效率并不高,比构造函数要慢,通过设置构造器的prototype来实现原型继承的时候,除了跟对象外,任何对象都会有一个原型,而通过Object.create(null)可以创建出没有原型的对象

  5. 原型模式是一个设计模式,也是一种编程范型,构成了javaScript语言的根本,原型模式十分重要,通过原型模式来实现面向对象系统简单而强大

第二章、this,call,apply

  1. this

    • this的指向:
      • 作为对象的方法调用
      • 作为普通函数调用
      • 构造器调用
      • Function.prototype.call或Function.prototype.apply调用
    1. 作为对象的方法调用:this指向该对象
      var obj={
          a;1,
          getA:function(){
              alert(this===obj)       //true
              alert(this.a)           //1
          }
      }
      
      obj.getA();
      
    2. 作为普通函数调用:this不作为对象的属性被调用时,普通函数方式时,此时this总指向全局对象,window
    window.name='globalName'
    var myObj={
        name:'even',
        getName:function(){
            return this.name
        }
    }
    
    var getName=myObj.getName;
    console.log(getName())      //globalName
    
    1. 构造器调用:javaScript中没有类,但是可以从函数构造器中创建对象,也提供了new,使构造函数更像一个类,构造函数和普通函数的区别就是调用方式,用new运算符调用函数时,该函数总会返回一个对象,构造器里的this就指向返回的这个函数,除非构造器显示地返回了一个object类型的对象

    4.Function.prototype.call或Function.prototype,apply调用

    丢失的this

  2. call和apply

    1. 区别:作用一模一样,区别仅在于传入参数形式不同,第一个函数指定了函数体this对象的指向,第二个参数为一个带下标的集合,可以是数组,也可以是类数组,javaScript参数在内部就是一个数组来表示,apply比call的使用率更高,call是包装在apply上面的一层语法糖
    2. 用途:
      1. 改变this指向
      2. Function.prototype.bind
      3. 借用其他对象的方

第三章、闭包和高阶函数

JavaScript是一门面向对象的编程语言,但是这门语言同时也拥有许多函数式语言的特性

函数式语言的鼻祖是LISP,JavaScript之初参考了Scheme

许多设计模式都是用闭包和高阶函数实现的

1.闭包

  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();
  1. 变量的生存周期

    • 对于全局变量,生存周期是永久的,除非主动销毁这个变量,对于由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不会被销毁。产生了一个闭包结构
    
  2. 闭包的更多作用

    • 封装变量
      • 闭包可以帮助把一些不需要暴露在全局的变量封装成"私有变量",避免这个变量在其他地方被修改造成错误
      • 提炼函数是代码重构的技巧,常常把能够独立出来的代码封装在独立的小函数里,如果小函数不需要复用,最好用闭包将其封闭起来
    • 延续局部变量的寿命
    var report=(function(){
        var imgs=[];
        return function(src){
            var img=new Image();
            imgs.push(img);
            img.src=src;
        }
    })();
    //解决请求丢失的问题
    
  3. 闭包和面向对象设计

    • 过程与数据的结合是形容面向对象中的“对象”经常使用的表达
    • 对象以方法的形式包含了过程,闭包是在过程中以环境的形式包含了数据,通常用面向对象可以实现的功能,闭包也可以实现
  4. 用闭包实现命令模式

    • 命令模式的意图是把请求封装为对象,从而分离请求的发起者和请求的接收者。
    //
    
    
  5. 闭包与内存管理

    • 闭包是一个非常大的特性,但有很多误解
    • 闭包容易造成循环引用,如果此时作用域链中保存着DOM节点,就有可能造成内存泄露,这并不是闭包和浏览器的问题,也不是JavaScript的问题,由于BOM和DOM中的对象是使用C++以COM实现,COM对象的垃圾回收机制采用的是引用计数,如果两个对象之间形成了循环引用,那么两个对象就都不能被回收,本质上不是闭包造成的。
    • 解决:把循环引用中的变量设为null,设置为null表示切断与它此前引用的值的联系

2.高阶函数

高阶函数:函数可以作为参数被传递,函数可以作为返回值输出

  1. 函数作为参数传递
    • 回调函数
    • Array.prototype.sort
  2. 函数作为返回值输出
    • 判断数据的类型
    • getSingle
  3. 高阶函数实现AOP(面向切面编程)
    • 面向切面编程的主要作用是把一些跟核心业务无关的功能抽离出来,例如日志统计,安全控制,异常处理等,抽离出来后通过动态织入的方式掺入业务逻辑中,好处是可以保持业务逻辑模块的纯净和高内聚性,其次是可以很方便的复用日志统计等模块儿
  4. 高阶函数的其他应用
    • currying
    • uncurrying
    • 函数节流
    • 分时函数
    • 惰性加载函数

 

注:本篇博客主要内容来自书籍原文

posted @ 2018-01-11 13:26  前端路上不断前进的小白  阅读(128)  评论(0)    收藏  举报