美文网首页
《JavaScript模式》读书笔记

《JavaScript模式》读书笔记

作者: newDasiykoo | 来源:发表于2017-03-21 15:35 被阅读0次

    Javascript 模式

    模式:更好的实践经验,有用的抽象化表示和解决一类问题的模板

    • 设计模式
    • 编码模式
    • 反模式

    如何编写易维护的代码

    • 阅读性好
    • 具有一致性
    • 预见性好
    • 看起来如同一个人编写
    • 有文档

    一·全局变量问题

    哪些情况容易导致隐含全局变量出现?
    1. 暗示全局变量

    2. var声明的链式赋值

      var a = b = 1;
      var a = (b = 1);
      
    3. 隐含全局变量 vs 明确定义的全局变量

      var a = 1;
      b = 1;
      (function() {c = 1})();
      
      delete a //false
      delete b //true
      delete c //true
      typeof a //number
      typeof b //undefined
      typeof c //undefined
      
    单一var模式
    function singleVar() {
      var a = 1,
          b = 2,
          sum = a + b,
          myObject = {},
          i, j;
       //函数体  
    }
    
    • 提供单一地址以查找到函数所需的所有局部变量

    • 防止出现变量在定义前就被使用的逻辑错误(变量提升?只提升声明,不赋值)

    myname = "global";
    function fun() {
      alert(myname);
      var myname = "local";
      alert(myname);
    }
    
    myname = "global";
    function fun() {
      var myname;
      alert(myname);
      myname = "local";
      alert(myname);
    }
    

    提升的实质:代码处理的两个阶段

    1. 解析和进入上下文阶段(创建变量~函数声明~形式参数)
    2. 代码运行时的执行阶段(创建函数表达式和不合格标识符【未定义变量】)
    • 帮助牢记要声明变量,以尽可能少的使用全局变量

    • 更少的编码!

    二· for循环

    //次优循环
    for(var i = 0; i < myarray.length; i++) {
      
    }
    
    //对长度值只提取一次,用于整个循环
    for(var i = 0, max = myarray.length; i < max; i++) {
      //如果需要在循环中修改容器,需要修改容器长度
    }
    
    //递减循环 ???  P20
    var i, myarray = [1,2,3,4,5];
    for (i = myarray.length; i--;) {
        console.log(myarray[i])
    }
    
    var myarray = [1,2,3,4,5], i = myarray.length;
    while (i--) {
        //处理myarray[i]
        console.log(myarray[i])
    }
    

    三· for-in 循环

    问题:过滤原型链属性
    var man = {
      hands: 2,
      legs:2,
      heads: 1
    };
        Object.prototype.clone = function() {console.log('clone')}
    
    for (var i in man) {
      console.log("全枚举", i, "::", man[i])
    }
    
    for (var i in man) {
        if (man.hasOwnProperty(i)) {
            console.log("自身属性枚举", i, "::", man[i])
        }
    }
    
    for (var i in man) {
      if(Object.prototype.hasOwnProperty.call(man, i)) {
            console.log("自身属性枚举", i, "::", man[i])
      }
    }
    //最优
    var hasOwn = Object.prototype.hasOwnProperty;
    for (var i in man) if(hasOwn.call(man, i)){
            console.log("自身属性枚举", i, "::", man[i])
    }
    
    拓展

    Object.defineProperty(obj, prop, descriptor)

    四 · 不要增加内置的原型

    Object(), Array(),Function()

    • 没有定义不可枚举时,在循环中输出
    • 使代码变得不可预测
    //浏览器兼容 ie8  文档
    Array.filter() , Array.map(), Array.forEach(), Array.indexOf()
    

    五 · 避免使用隐式类型转换

    使用===!==进行比较,避免导致混淆不清

    六 · 避免使用eval()

    var property = "name";
    alert(eval("obj." + property));
    

    如果必须使用,优先使用new Function(), 防止污染作用域链

    (function(){
        var local = 1;
        eval("local = 3; console.log(local);")
    }())
    (function(){
        var local = 1;
        new Function("console.log(typeof local);")();
    }())
    
    

    七 · 编码约定

    一致的空格,缩进, 分号, 命名约定

    开放的大括号的位置
    if (true) {
        alert('it is true!')
    }
    if (true) 
    {
        alert('it is true!')
    }
    

    分号插入机制:当没有正确使用分号结束语句时,它会自动补上

    八 · 命名规则

    构造函数名:首字母大写, 大驼峰式function MyConstructor(){}

    函数名&属性名: 小驼峰式 function calculateArea(){},object.getName

    变量:小驼峰式, 下划线式 first_name

    常量:大写 const MAX_WIDTH = 800

    私有属性: 下划线前缀&后缀 __proto__,__ob__

    var person = {
        getName: function() {
          return this._getFirst() + '' + this._getLast()
        },
        _getFirst: function() {
          //...
        },
        _getLast: function() {
          //...
        }
    }
    
    

    九· 编写注释

    头部文件·作者·时间信息

    函数的作用和参数含义, 正则表达式

    十 · 代码检查JSLint

    • 无法运行的代码
    • 变量未定义前使用
    • 不安全的UTF字符
    • 使用void, with, eval
    • 正则中不合适的转义字符
    • 分号等格式问题

    gulp or 安装到编辑器

    字面量和构造函数

    一 · 对象字面量

    var car1 = {goes: "far"}  //没有作用域解析
    //使用new Object() 构造函数
    var car2 = new Object()   
    car2.goes = "far"
    //需要用调用Object()的位置开始一直向上查询作用域链,直到发现全局object构造函数
    
    //返回的值不一定是对象exp
    
    默认构造函数形式(强制使用new的模式)
    var Person = function(name) {
      this.name = name
      this.say = function() {
            console.log('hello ', this.name)
        }
    }
    
    //new操作符
    var Person = function(name) {
      //创建了一个空对象,并this变量引用该对象,同时继承函数原型
      //var this = {}      
      //var this = Object.create(Person.prototype)
      
      //属性和方法被加入this引用的对象中
      this.name = name
      this.say = function() {
        console.log('hello ', this.name)
      }
      
      //隐式返回this
      //return this
    }
    
    代码命名规范:大写开头
    自定义构造函数返回值(优先)
    //可以根据需要返回任意其他对象(必须是对象)
    var Objectmaker = function() {
        this.name = 'this is it'
        var that = {}
        that.name = 'And that\'s that'
        return that
    }
    

    优点:避免忘记new带来的问题

    缺点:丢失原型链

    自调用构造函数
    function Waffle() {
        if (!(this instanceof Waffle)) {
            return new Waffle()
        }
        this.state = "yummy"
    }
    

    具体的做法就是,在构造函数中检查this是否为构造函数的一个实例,如果不是,构造函数可以通过new关键字进行自调用。

    拓展 : Object.defineProperty——get,set MVVC利器 ES5, ie9+

    Object.defineProperty(obj, prop, descriptor)
    //返回对象obj
    
    1. 数据描述符 可写,不可写
    2. 存取描述符 getter, setter

    两者不能同时存在!

    同时具有的可选键值:

    • configurable

      当且仅当该属性的 configurable 为 true 时,该属性描述符才能够被改变,也能够被删除。默认为 false

    • enumerable

      当且仅当该属性的 enumerable 为 true 时,该属性才能够出现在对象的枚举属性中。默认为 false。

    数据描述符可选键值:

    • value

      该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。默认为 undefined

    • writable

      当且仅当该属性的 writable 为 true 时,该属性才能被赋值运算符改变。默认为 false

    存取描述符可选键值:

    • get

      一个给属性提供 getter 的方法,如果没有 getter 则为 undefined。该方法返回值被用作属性值。默认为 undefined

    • set

      一个给属性提供 setter 的方法,如果没有 setter 则为 undefined。该方法将接受唯一参数,并将该参数的新值分配给该属性。默认为 undefined

    let Good = function(name,price, amt) {
        if (!(this instanceof Good)) {
            return new Good(name,price, amt);
        }
        var newLine = "<br />";
    // Example of an object property added with defineProperty with a data property descriptor
        Object.defineProperty(this, 'price', {
          enumerable: true,
          configurable: true,
          set: function (x) {
            document.write(this.name + "\'s price set to " + x + newLine);
            price = x;
        },
        get: function () {
            document.write(this.name + "\'s amount get " + price + newLine);
            return price;
        },
        });
    
        Object.defineProperty(this, 'amt', {
          enumerable: true,
          configurable: true,
          set: function (x) {
            amt = x;
            document.write(this.name + "\'s amount set to " + x + newLine);
        },
        get: function () {
            document.write(this.name + "\'s amount get " + amt + newLine);
            return amt;
        },
        });
        this.name = name;
        this.amt = amt;
        this.price = price;
    };
    let pillow = new Good('pillow', 123.33, 1);
    let chair = Good('chair', 233, 1);
    
    

    二 · 数组字面量

    var arr = new Array('itsy', 'bitsy', 'spider')
    var arra = ['itsy', 'bitsy', 'spider']
    
    var arr_b = new Array(3)
    var arr_c = new Array(3.14)
    var q = new Array(201).join('?')
    
    //检查数组性质
    console.log(typeof arr);  //object
    console.log(arr.constructor === Array); //ie8
    console.log(arr instanceof Array); //ie8
    console.log(Array.isArray(arr));  //ES5 ie9+
    if (typeof Array.isArray === "undefined") {
        Array.isArray = function(arg) {
            return Object.prototype.toString.call(arg) === "[object Array]";
        }
    }
    

    三 · 基本值类型包装器

    var n = 100;   //基本类型值,可临时转换成对象,使用属性方法
    console.log(typeof n);
    
    var nobj = new Number(100);  //包装对象
    console.log(typeof nobj);
    
    var test = Number(100);  //基本类型值
    console.log(typeof test);
    
    var greet = "hello there";
    greet.split(' ')[0];
    greet.smile = true;
    
    var greet = new String("hello there");
    greet.split(' ')[0];
    greet.smile = true;
    

    四 · 错误对象

    throw语句创建或抛出错误对象

    var errorHandle = function() {
    }
    try {
      throw {
        name: 'MyErrorType',
        message: "oops",
        extra: "This was rather embarrassing",
        remedy: errorHandle  //指定应该处理该错误的函数
      };
    } catch (e) {
      //通知用户
      alert(e.message);
      throw e;
      e.remedy();  //处理错误
    }
    

    函数

    函数是一个由Function()构造函数创建的对象

    函数提供局部作用域

    一 · 定义函数的方法

    • 命名函数表达式(需要分号结尾)不需要特殊的语言结构,推荐

      var func = function add(a, b) {
        return a + b;
      };
      
    • 匿名函数表达式(需要分号结尾)不需要特殊的语言结构,推荐

      var func = function (a, b) {
        return a + b;
      };
      
      //匿名函数和命名函数表达式,函数声明的区别在于有无函数对象name属性
      var printFuncName = function(cb) {
        console.log(cb.name);
      }
      printFuncName(function() {})
      
    • 函数声明(不需要分号结尾, 不能以参数形式出现在函数调用中)

      function add(a, b) {
        return a + b;
      }
      

    二 · 函数提升

    == 函数声明及定义同时提升!==

    function foo() {
        alert('global foo');
    }
    
    function bar() {
        alert('global bar');
    }
    function hoistMe() {
        console.log(typeof foo);  
        console.log(typeof bar);
    
        foo();
        bar(); 
    
        function foo() {
            alert('hoist foo');
        }
    
        var bar = function() {
            alert('hoist bar');
        }
        
        // bar();
    }
    

    三 · API模式(为函数提供更好更整洁的接口)

    回调模式

    回调函数为全局函数或匿名函数时(非对象方法)

    var findNodes = function(callback) {
        var i = 10,
            nodes = [],
            found;
    
        if (typeof callback !== "function") {
            callback = false;
        }
    
        while (i) {
            i -= 1;
            if (callback) {
                callback(found);
            }
    
            nodes.push(found);
        }
    
        return nodes;
    };
    
    var hide = function(node) {
        node.style.display = "none";
    };
    
    findNodes(hide);
    findNodes(function() {
        node.style.display = "block";
    });
    

    回调与作用域(回调函数为对象方法)

    var myapp = {};
    myapp.color = "green";
    myapp.paint = function(node) {
      console.log('this', this);
      node.style.color = this.color;
    };
    var findNodes = function(cb_property, cb_obj) {
      var found = document.getElementById('cbPaint'),
          cb;
      if (typeof cb_property === "string") {
        cb = cb_obj[cb_property];
      }
      if (typeof cb === "function") {
        cb.call(cb_obj, );
        cb.apply(cb_obj, [found])
      }
    }
    findNodes("paint", myapp);
    
    配置对象
    addPerson('firstName', 'lastName', 22, 'address', 'gender');
    addPerson({
      firstName: 'firstName',
      lastName: 'lastName',
      addr: 'address',
      age: 'age',
      gender: 0
    })
    
    返回函数

    储存函数私有数据

    var setUpCounter = function() {
        var count = 1;
        return function() {
          console.log(count++);
        }
    }
    var count = setUpCounter()
    
    Curry化
    function add(x, y) {
      if (typeof y === "undefined") {
        return function (y) {
          return x + y;
        }
      }
      return x + y;
    }
    
    add(3)(4)
    var add2000 = add(2000);
    add2000(10);
    

    函数curry化

    function schonfinkelize(fn) {
      var slice = Array.prototype.slice,
          stored_args = slice.call(arguments, 1);
      return function() {
        var new_args = slice.call(arguments),
            args = stored_args.concat(new_args) ;
        return fn.apply(null, args);
      }
    }
    
    var add = function(x, y) {
        return x + y;
    }
    

    何时使用curry化?

    调用同一个函数,且传递的参数大多数是相同的

    拓展:偏函数应用 — from underscore

    • 偏函数应用是找一个函数,固定其中的几个参数值,从而得到一个新的函数。

    • 函数加里化是一种使用匿名单参数函数来实现多参数函数的方法。

    • 函数加里化能够让你轻松的实现某些偏函数应用。

      underscore : _.partial(func, _, value);//exp html
      

    四 · 初始化模式

    在不污染全局命名空间的情况下,使用临时变量以一种更加整洁,结构化的方式执行初始化以及设置任务

    即时函数
    //即时函数(提供局部作用域)
    (function() {
        //...
    }());
    (function(global) {
        //...
    })(window);
    
    //初始化时分支
    
    即时对象初始化
    //保护全局命名空间,更结构化
    ({
      //配置常量
      maxwidth:600,
      maxheight: 400,
      gimmeMax: function() {
        return this.maxwidth + "x" + this.maxheight;
      },
      //初始化
      init: function() {
        console.log(this.gimmeMax());
      }
    }).init();
    
    初始化时分支
    var utils = {
      addListener: function(el, type, fn) {
        if(typeof window.addEventListener === 'function') {
          el.addEventListener(type, fn, false);
        } else if (typeof document.attachEvent === 'function') {
          el.attachEvent('on' + type, fn);
        } else {
          el['on' + type] = fn
        }
      },
      removeListener: function(el, type, fn) {
        // 同上
      }
    }
    
    var utils = {
      addListener: null,
      removeListener: null
    }
    
    if(typeof window.addEventListener === 'function') {
      utils.addListener = function(el, type, fn) {
        el.addEventListener(type, fn, false);
      }
      utils.removeListener = function(el, type, fn) {
        el.removeEventListner(type, fn, false);
      }
    } else if (typeof document.attachEvent === 'function') {
      //...
    } else {
      //...
    }
    

    五 · 性能模式

    加速代码运行

    备忘模式(函数属性)
    var myFunc = function(param) {
      if (!myFunc.cache[param]) {
        var result = {};
        //开销很大的操作
        myFunc.cache[param] = result;
      }
      return myFunc.cache[param];
    };
    
    myFunc.cache = {};
    

    **应用:斐波那契数列第n项的计算 **

    F(0) = 0, F(1) = 1, F(n) = F(n-1) + F(n-2) (n≥2,n∈N*)

    var fibonacci = function(n) {
        if (n === 0) return 0;
        if (n === 1) return 1;
        return fibonacci(n-1) + fibonacci(n-2);
    }
    
    //用备忘模式优化后求斐波那契
    var fibonacci = function(n) {
        if (n === 0) return 0;
        if (n === 1) return 1;
        if (!fibonacci.cache[n]) {
          fibonacci.cache[n] = fibonacci(n-1) + fibonacci(n-2);
        }
        return fibonacci.cache[n];
    }
    fibonacci.cache = {};
    
    自定义模式

    函数在第一次运行时,才被正确的定义

    //惰性函数定义(初始化任务,且只需执行一次)
    var scareMe = function() {
        alert("Boo!");
        scareMe = function() {
            alert("Double boo!")
        }
    }
    //用自定义模式再次优化
    var fibonacci = function(n) {
        fibonacci = function(n) {
          if (n === 0) return 0;
          if (n === 1) return 1;
          if (!fibonacci.cache[n]) {
            fibonacci.cache[n] = fibonacci(n-1) + fibonacci(n-2);
          }
          return fibonacci.cache[n];
        }
        fibonacci.cache = {};
        return fibonacci(n);
    };
    

    对象

    命名空间模式

    var MYAPP = {};
    
    //构造函数&函数
    MYAPP.Parent = function() {};
    
    //变量
    MYAPP.someValue = 1;
    
    //对象
    MYAPP.modules = {};
    

    通用命名空间函数(非破坏性的)

    //不安全的
    var MYAPP = {};
    //更好的代码风格
    if(typeof MYAPP === "undefined") {
        var MYAPP = {};
    }
    var MYAPP = MYAPP || {};
    
    //通用命名空间函数
    MYAPP.namespace('MYAPP.modules.module2');
    
    var MYAPP = {
      modules : {
        modules2: {}
      }
    }
    
    MYAPP.namespace = function(ns_string) {
      var parts = ns_string.split('.'),
          parent = MYAPP,
          i, max;
      if (parts[0] === 'MYAPP') {
        parts = parts.slice(1);
      }
      
      for (i = 0, max = parts.length; i < max; i++) {
        if(typeof parent[parts[i]] === "undefined") {
            parent[parts[i]] = {};
        }
        parent = parent[parts[i]];
      }
      return parent;
    }
    
    MYAPP.namespace('first.second.third');
    

    依赖声明模式

    在函数或模块顶部声明代码所依赖的模块

    • 显式声明依赖更清晰
    • 解析速度加快
    • 更小的代码量
    var myFunction = function() {
        var event = YAHOO.util.Event,
            dom = YAHOO.util.Dom;
        //使用event dom 变量
    }
    

    模块模式

    命名空间, 即时函数, 私有和特权方法, 声明依赖

    MYAPP.namespace('MYAPP.utilities.array');
    
    MYAPP.utilities.array = (function(app, global) {
        //依赖
      var uobj = MYAPP.utilities.object,
          ulang = MYAPP.utilities.lang,
          
          //私有属性
          array_string = '[object Array]',
          ops = Object.prototype.toString;
      
          //私有方法
          someFunc = function() {
            
          };
            
      //可选的一次性初始化过程
      
      //公有api
      return {
        inArray: function (needle, haystack) {
          //...
        }, 
        isArray: function (a) {
          //...
        },
        //...更多方法和属性
      }
      
      /* return {
        inArray,
        isArray
      } */
    }(MYAPP, this));
    

    沙箱模式

    全局对象 -> 全局构造函数Sandbox()

    new Sandbox(【】function(box) {
      // ...
    })
    
    1. 将全局属性方法的初始化函数放在Sandbox构造函数的属性modules中
    2. 在构造函数内部获取所需的属性,赋值给this
    3. 运行callback(this)
      done!
    //增加模块
    Sandbox.modules = {};
    Sandbox.modules.dom = function(box) {
        box.getElement = function() {console.log('getElement')};
        box.getStyle = function() {console.log('getStyle')};
        box.foo = 'bar';
    };
    Sandbox.modules.event = function(box) {
        box.attachEvent = function() {console.log('attachEvent')};
        box.dettachEvent = function() {console.log('dettachEvent')};
    };
    Sandbox.modules.ajax = function(box) {
        box.makeRequest = function() {console.log('makeRequest')};
        box.getResponse = function() {console.log('getResponse')};
    }
    //实现构造函数
    function Sandbox() {
        var args = Array.prototype.slice.call(arguments),
            //最后一个参数是回调
            callback = args.pop(),
            //模块可以作为数组或单独的参数传递
            modules = (args[0] && args[0] === "string") ? args : args[0],
            i;
        if (!(this instanceof Sandbox)) {
            return new Sandbox(modules, callback);
        }
        //需要通用的变量
        this.a = 1;
        this.b = 2;
        //需要所有的模块
        if (!modules || modules === "*") {
            modules = [];
            for (i in Sandbox.modules) {
                if (Sandbox.modules.hasOwnProperty(i)) {
                    modules.push(i);
                }
            }
        }
        for (i = 0; i < modules.length; i++) {
            Sandbox.modules[modules[i]](this);
        }
        callback(this);
    }
    //原型属性
    Sandbox.prototype = {
        name: "My Application",
        version: "1.0",
        getName: function() {
            return this.name;
        }
    }
    new Sandbox(['dom', 'ajax'], function(box) {
        box.makeRequest();
    })
    

    静态成员(在类的层次上操作,包含非实例相关的方法和数据)

    公有静态成员(不需要特定的实例就能运行)
    // 公有静态成员
    var Gadget = function() {};
    
    //静态方法
    Gadget.isShiny = function() {
        var msg = "you bet";
        if (this instanceof Gadget) {
            msg += ", it costs $" + this.price + "!";
        }
        return msg;
    };
    
    //普通方法
    Gadget.prototype.setPrice = function(price) {
        this.price = price;
    };
    
    Gadget.isShiny();
    
    var iphone = new Gadget();
    iphone.setPrice(500);
    Gadget.prototype.isShiny = Gadget.isShiny;
    iphone.isShiny();
    
    Gadget.prototype.isShiny = function () {
        return Gadget.isShiny.call(this);
    };
    
    私有静态成员
    • 同一个构造函数的所有对象共享该成员
    • 构造函数外部不能访问

    exp: 对象id标识符

    var Gadget = (function() {
        var counter = 0;
    
        return function() {
            this.id = counter++;
        };
    }());
    

    链模式

    var obj = {
        value: 1,
        increment: function () {
            this.value += 1;
            return this;
        },
        add: function(v) {
            this.value += v;
            return this;
        },
        shout: function() {
            alert(this.value);
        }
    };
    
    obj.increment().add(3).shout();
    

    jquery DOM

    document.getElementById('father').appendChild(newNode)
    

    代码复用模式

    GoF的原则: 优先使用对象组合,而不是类继承

    类式继承模式

    //父构造函数
    function Parent(name) {
      this.name = name || 'Adam';
    }
    //向改原型添加功能
    Parent.prototype.say = function() {
      return this.name;
    }
    
    //空白的子构造函数
    function Child(name) {}
    
    inherit(Child, Parent);
    
    1. 默认模式(引用)

      function inherit(C, P) {
        C.prototype = new P();
      }
      

      缺点:

      • 同时继承了this上的特权属性和原型属性;(关于构造函数的一般经验法则是,应该将可复用的属性加到原型)

      • 不支持将参数传递到子构造函数(父?)中, 而子构造函数然后又将参数传递到父构造函数中

        var s = new Child('Seth');
        s.say();
        

    2. 借用构造函数(独立副本)

      //父构造函数
      function Article() {
        this.tags = ['js', 'css']
      }
      //父构造函数的实例
      var article = new Article();
      
      //子构造函数1, 默认模式继承
      function BlogPost() {}
      BlogPost.prototype = article;
      
      //子构造函数1的实例
      var blog = new BlogPost();
      
      //子构造函数2, 借助构造函数继承
      function StaticPage() {
       Article.call(this);
      }
      //子构造函数2的实例
      var page = new StaticPage();
      
      console.log(article.hasOwnProperty("tags"))
      console.log(page.hasOwnProperty("tags"))
      console.log(blog.hasOwnProperty("tags"))
      

      只能继承父构造函数this中的属性,变为自身属性;不能继承原型

      原型链是断开的

      function Parent(name) {
        this.name = name || 'Adam';
      }
      
      Parent.prototype.say = function() {
       return this.name
      };
      
      function Child(name) {
       Parent.apply(this, arguments);
      }
      
      var kid = new Child("Patrick");
      kid.name;
      typeof kid.say;
      

      可实现多重继承

      function Cat() {
       this.legs = 4;
           this.say = function() {
            return "meaowww"
           }
      }
      
      function Bird() {
        this.wings = 2;
        this.fly = true;
      }
      
      function CatWings() {
        Cat.apply(this);
        Bird.apply(this);
      }
      
      var jane = new CatWings();
      console.log(jane);
      

      缺点:

      • 无法从原型继承
    3. 借用和设置原型(结合1,2)

      function Parent(name) {
        this.name = name || 'Adam';
      }
      
      Parent.prototype.say = function() {
        return this.name;
      };
      
      function Child(name) {
        Parent.apply(this, arguments);
      }
      Child.prototype = new Parent();
      
      var kid = new Child("Patrick");
      kid.name;
      kid.say();
      delete kid.name;
      kid.say();
      

      优点:

      • 继承父对象的一切,且能安全的修改自身属性

      缺点:

      • 父构造函数被调用了两次,效率低下。父对象自身的属性,会被继承两次
    4. 共享原型

      //任何值得继承的东西都应该放置在原型中实现!
      function inherit(C, P) {
        C.prototype = P.prototype;
      }
      
    5. 临时构造函数

      function inherit(C, p) {
        var F = function () {};
        F.prototype = P.prototype;
        C.prototype = new F();
      }
      
    6. 重置构造函数指针(最终版本)

      function inherit() {
        var F = function() {};
        F.prototype = P.prototype;
        C.prototype = new F();
        C.uber = P.prototype;
        C.prototype.constructor = C;
      }
      
      //优化
      var inherit = (function() {
        var F = function() {};
        return function() {
          F.prototype = P.prototype;
           C.prototype = new F();
           C.uber = P.prototype;
           C.prototype.constructor = C;
        }
      }());
      

    原型继承(无类继承)

    var parent = {
      name: "Papa"
    };
    
    function object(o) {
      function F() {}
      F.prototype = o;
      return new F();
    }
    var child = object(parent);
    alert(child.name);
    
    
    
    var kid = Object.create(parent);
    

    通过复制属性继承(无原型)

    Object.assign();   //ES6 
    
    var a = {
      string: 'string',
      obj: {
        proper: 'some'
      }
    }
    var b = Object.assign(a);
    

    “mix-in”模式

    var cake = mix(
        {eggs: 2, large: true},
        {buffer: 1, salted: true},
        {flour: "3 cups"},
        {super: "sure"}
    )
    

    借用方法

    仅需要使用现有对象其中一个或两个方法

    apply(), call()

    function f() {
      var args = [].slice.call(arguments, 1, 3);
      //Array.prototype.slice.call(arguments, 1, 3);
      return args;
    }
    
    f(1, 2, 3, 4, 5); 
    
    var one ={
      name: 'object',
      say: function(greet) {
        return greet + ", " + this.name;
      }
    }
    
    var two = {
      name: 'another object'
    }
    
    one.say.apply(two, ['hello']);
    

    设计模式

    单体模式

    一个特定类仅有一个实例

    1. 静态属性实现
    function Universe() {
      if(typeof Universe.instance === 'object') {
        return Universe.instance;
      }
      this.start_time = 0;
      this.bang = 'Big';
      
      Universe.instance = this;
      
      //隐式返回
      //return this;
    }
    
    1. 闭包实现(惰性定义函数)

      function Universe() {
        var instance = this;
        
        this.start_time = 0;
        this.bang = "Big";
        
        Universe = function() {
          return instance;
        }
      }
      

      缺点:重定义后,加入的原型无效; 实例constructor指向重定向前的构造函数 ???

      function Universe() {
        var instance = this;
        Universe = function() {
          return instance;
        };
        
        //保留原型
        Universe.prototype = this;
        console.log(this.constructor);
        
        //实例
        instance = new Universe;
        
        //重置构造函数指针
        instance.constructor = Universe;
        
        instance.start_time = 0;
        instance.bang = "Big";
        
        return instance;
      }
      

      Universe1 prototype <—实例— this = Universe2.prototype < —实例— instance

      var Universe;
       
      (function(){
        var instance;
        Universe = function() {
          if (instance) {
            return instance;
          }
          
          instance = this;
          this.start_time = 0;
          this.bang = "Big";
        };
      }());
      

    var Universe = (function(){
    var instance;
    return function() {
    if (instance) {
    return instance;
    }

       instance = this;
       this.start_time = 0;
       this.bang = "Big";
     };
    

    }());

    
    ​
    
    #### 工厂模式 
    
    在编译时不知道具体类型,为工厂客户提供一种创建对象的接口
    
    通过工厂方法创建的对象在设计上都继承了相同的父对象这个思想,它们都是实现专门功能的特定子类
    
    ​```javascript
    var corolla = CarMaker.factory('Compact');
    var solistic = CarMaker.factory('Convertible');
    var cherokee = CarMaker.factory('SUV');
    corolla.drive();   //"Vroom, I have 2 doors"
    solistic.drive();   //"Vroom, I have 4 doors"
    cherokee.drive();   //"Vroom, I have 17 doors"
    
    
    
    //父构造函数
    function CarMaker() {}
    CarMaker.prototype.drive = function() {
        return "Vroom, I have " + this.doors + " doors";
    };
    //静态工厂方法
    CarMaker.factory = function (type) {
      var constr = type,
          newcar;
      //寻找种类构造函数
      if (typeof CarMaker[constr] !== "function") {
        throw {
          name: "Error",
          message: constr + " doesn't exist"
        };
      }
      //继承
      if(typeof CarMaker[constr].prototype.drive !== "function") {
        CarMaker[constr].prototype = new CarMaker();
      }
      newcar = new CarMaker[constr]();
      return newcar;
    };
    
    //不同种类的构造函数
    CarMaker.Compact = function() {
      this.doors = 4;
    }
    CarMaker.Convertible = function() {
      this.doors = 2;
    }
    CarMaker.SUV = function() {
      this.doors = 17;
    }
    
    内置对象工厂 Object
    var o = new Object(),
        n = new Object(1),
        s = Object('1'),
        b = Object(true);
    

    迭代器模式

    访问数据结构中的每个元素

    var agg = (function() {
      var index = 0,
          data = [1, 2, 3, 4, 5], //Object.keys(obj)
          length = data.length;
      return {
        next: function() {
          var element;
          if (!this.hasNext()) {
            return null;
          }
          element = data[index];
          index = index + 2;
          console.log(index);
          return element;
        },
        hasNext: function() {
          return index < length
        },
        rewind: function() {
          index = 0;
        },
        current: function() {
          return data[index];
        }
      }
    }());
    while (agg.hasNext()) {
      console.log(agg.next());
    }
    

    装饰者模式

    使用继承实现 运行时动态添加附加功能到对象中
    var sale = new Sale(100);       //价格100美元
    sale1 = sale.decorate('fedtax');   //增加联邦税
    sale2 = sale1.decorate('quebec');       //增加省级税
    sale3 = sale2.decorate('money');    //格式化为加元货币形式
    sale3.getPrice();
    sale4 = sale.decorate('fedtax').decorate('quebec').decorate('money')
    
    function Sale(price) {
      this.price = price || 100;
    }
    Sale.prototype.getPrice = function() {
      return this.price;
    };
    //定义decorators
    Sale.decorators = {};
    Sale.decorators.fedtax = {
      getPrice: function() {
        var price = this.uber.getPrice();
        price += price * 5 / 100;
        return price;
      }
    };
    Sale.decorators.quebec = {
      getPrice: function() {
        var price = this.uber.getPrice();
        price += price * 7.5 / 100;
        return price;
      }
    };
    Sale.decorators.money = {
      getPrice: function() {
        return "$" + this.uber.getPrice().toFixed(2);
      }
    };
    Sale.prototype.decorate = function(decorator) {
      var F = function() {},
          overrides = this.constructor.decorators[decorator],
          i, newobj;
      F.prototype = this;
      console.log(this);
      newobj = new F();
      newobj.uber = F.prototype;
      for(i in overrides) {
        if (overrides.hasOwnProperty(i)) {
          newobj[i] = overrides[i];
        }
      }
      return newobj;
    }
    
    使用列表实现
    var sale = new Sale(100);
    sale.decorate('fedtax');
    sale.decorate('quebec');
    sale1 = sale.decorate('money');
    sale.getPrice();
    
    sale.decorate('fedtax').decorate('money').getPrice();
    
    function Sale(price) {
      this.price = price || 100;
      this.decorators_list = [];
    }
    Sale.decorators = {};
    Sale.decorators.fedtax = {
      getPrice: function(price) {
        return price + price * 5 / 100;
      }
    };
    Sale.decorators.quebec = {
      getPrice: function(price) {
        return price + price * 7.5 / 100;
      }
    };
    Sale.decorators.money = {
      getPrice: function(price) {
        return "$" + price.toFixed(2);
      }
    };
    Sale.prototype.decorate = function(decorator) {
      this.decorators_list.push(decorator);
      return this;
    };
    Sale.prototype.getPrice = function() {
      var price = this.price,
          i,
          max = this.decorators_list.length,
          name;
      for (i = 0; i < max; i++) {
        name = this.decorators_list[i];
        price = Sale.decorators[name].getPrice(price);
      }
      return price;
    }
    

    策略模式

    运行时选择算法

    exp: 表单验证

    var data = {
      first_name: "Super",
      last_name: "Man",
      age: "unknown",
      username: "o_O"
    }
    validator.config = {
      first_name: 'isNonEmpty',
      age: 'isNumber',
      username: 'isAlphaNum'
    }
    
    var validator = {
      //所有可用检查
      types: {
        isNonEmpty : {
          validate: function(value) {
            return value !== "";
          },
          instructions: "the value cannot be empty"
        },
        isNumber : {
          validate: function(value) {
            return !isNaN(value);
          },
          instructions: "the value can only be a valid number"
        },
        isAlphaNum : {
          validate: function(value) {
            return !/[^a-z0-9]/i.test(value);
          },
          instructions: "the value can only contain characters and numbers, no special symbols"
        }
      },
      //当前验证会话中的错误信息
      messages: [],
      //配置
      config: {},
      //接口方法
      validate: function(data) {
        var i, msg, type, checker, result_ok;
        this.messages = [];
        for(i in data) {
          if(data.hasOwnProperty(i)) {
            type = this.config[i];
            checker = this.types[type];
            if (!type) {
              continue;
            }
            if (!checker) {
              throw {
                name: "ValidationError",
                message: "No handler to validate type " + type
              }
            }
                                                                
            result_ok = checker.validate(data[i]);
            if (!result_ok) {
              msg = 'Invalid value for*' + i + '*, ' + checker.instructions;
              this.messages.push(msg);
            }
          }
        }
        return this.hasErrors();
      },
      hasErrors: function() {
        return this.messages.length !== 0;
      }
    }
    

    拓展:包装成类

    外观模式

    把常用方法包装到一个新方法中,从而提供更为便利的api

    两个或者更多的方法可能普遍被一起调用👉创建另一个方法已包装重复的方法调用

    var myevent = {
      stop: function (e) {
        e.preventDefault();
        e.stopPropagation();
      }
    }
    

    代理模式

    通过包装一个对象以控制对它的访问,将访问聚集为组或仅当真正必要的时候才执行访问,避免造作开销。

    http://www.jspatterns.com/book/7/proxy.html

    延迟初始化

    var delay = function() {
      //开销很大的初始化操作
      delay = function() {
        //真正的操作方法
      }
    }
    

    http请求

    中介者模式

    对象之间不直接通话,而是通过一个中介者对象进行通信,从而实现送耦合

    mediator.html

    观察者模式(自定义事件)

    浏览器事件

    促成松散耦合

    subscribers  []
    subscribe() 将订阅者添加到subscribers数组
    unsubscribe() 从订阅者数组subscribers中删除订阅者
    publish()  循环遍历subscribers中的每个元素,并且调用他们注册时所提供的方法
    visitSubscribers()
    type
    

    exp. observer.html observer-game.html

    相关文章

      网友评论

          本文标题:《JavaScript模式》读书笔记

          本文链接:https://www.haomeiwen.com/subject/vgrgnttx.html