《JavaScript模式》讀書(shū)筆記

Javascript 模式

模式:更好的實(shí)踐經(jīng)驗(yàn),有用的抽象化表示和解決一類(lèi)問(wèn)題的模板

  • 設(shè)計(jì)模式
  • 編碼模式
  • 反模式

如何編寫(xiě)易維護(hù)的代碼

  • 閱讀性好
  • 具有一致性
  • 預(yù)見(jiàn)性好
  • 看起來(lái)如同一個(gè)人編寫(xiě)
  • 有文檔

一·全局變量問(wèn)題

哪些情況容易導(dǎo)致隱含全局變量出現(xiàn)?
  1. 暗示全局變量

  2. var聲明的鏈?zhǔn)劫x值

    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;
   //函數(shù)體  
}
  • 提供單一地址以查找到函數(shù)所需的所有局部變量

  • 防止出現(xiàn)變量在定義前就被使用的邏輯錯(cuò)誤(變量提升?只提升聲明,不賦值)

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

提升的實(shí)質(zhì):代碼處理的兩個(gè)階段

  1. 解析和進(jìn)入上下文階段(創(chuàng)建變量~函數(shù)聲明~形式參數(shù))
  2. 代碼運(yùn)行時(shí)的執(zhí)行階段(創(chuàng)建函數(shù)表達(dá)式和不合格標(biāo)識(shí)符【未定義變量】)
  • 幫助牢記要聲明變量,以盡可能少的使用全局變量

  • 更少的編碼!

二· for循環(huán)

//次優(yōu)循環(huán)
for(var i = 0; i < myarray.length; i++) {
  
}

//對(duì)長(zhǎng)度值只提取一次,用于整個(gè)循環(huán)
for(var i = 0, max = myarray.length; i < max; i++) {
  //如果需要在循環(huán)中修改容器,需要修改容器長(zhǎng)度
}

//遞減循環(huán) ???  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 循環(huán)

問(wèn)題:過(guò)濾原型鏈屬性
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])
  }
}
//最優(yōu)
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)

四 · 不要增加內(nèi)置的原型

Object(), Array(),Function()

  • 沒(méi)有定義不可枚舉時(shí),在循環(huán)中輸出
  • 使代碼變得不可預(yù)測(cè)
//瀏覽器兼容 ie8  文檔
Array.filter() , Array.map(), Array.forEach(), Array.indexOf()

五 · 避免使用隱式類(lèi)型轉(zhuǎn)換

使用===!==進(jìn)行比較,避免導(dǎo)致混淆不清

六 · 避免使用eval()

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

如果必須使用,優(yōu)先使用new Function(), 防止污染作用域鏈

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

七 · 編碼約定

一致的空格,縮進(jìn), 分號(hào), 命名約定

開(kāi)放的大括號(hào)的位置
if (true) {
    alert('it is true!')
}
if (true) 
{
    alert('it is true!')
}

分號(hào)插入機(jī)制:當(dāng)沒(méi)有正確使用分號(hào)結(jié)束語(yǔ)句時(shí),它會(huì)自動(dòng)補(bǔ)上

八 · 命名規(guī)則

構(gòu)造函數(shù)名:首字母大寫(xiě), 大駝峰式function MyConstructor(){}

函數(shù)名&屬性名: 小駝峰式 function calculateArea(){},object.getName

變量:小駝峰式, 下劃線(xiàn)式 first_name

常量:大寫(xiě) const MAX_WIDTH = 800

私有屬性: 下劃線(xiàn)前綴&后綴 __proto__,__ob__

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

九· 編寫(xiě)注釋

頭部文件·作者·時(shí)間信息

函數(shù)的作用和參數(shù)含義, 正則表達(dá)式

十 · 代碼檢查JSLint

  • 無(wú)法運(yùn)行的代碼
  • 變量未定義前使用
  • 不安全的UTF字符
  • 使用void, with, eval
  • 正則中不合適的轉(zhuǎn)義字符
  • 分號(hào)等格式問(wèn)題

gulp or 安裝到編輯器

字面量和構(gòu)造函數(shù)

一 · 對(duì)象字面量

var car1 = {goes: "far"}  //沒(méi)有作用域解析
//使用new Object() 構(gòu)造函數(shù)
var car2 = new Object()   
car2.goes = "far"
//需要用調(diào)用Object()的位置開(kāi)始一直向上查詢(xún)作用域鏈,直到發(fā)現(xiàn)全局object構(gòu)造函數(shù)

//返回的值不一定是對(duì)象exp
默認(rèn)構(gòu)造函數(shù)形式(強(qiáng)制使用new的模式)
var Person = function(name) {
  this.name = name
  this.say = function() {
        console.log('hello ', this.name)
    }
}

//new操作符
var Person = function(name) {
  //創(chuàng)建了一個(gè)空對(duì)象,并this變量引用該對(duì)象,同時(shí)繼承函數(shù)原型
  //var this = {}      
  //var this = Object.create(Person.prototype)
  
  //屬性和方法被加入this引用的對(duì)象中
  this.name = name
  this.say = function() {
    console.log('hello ', this.name)
  }
  
  //隱式返回this
  //return this
}
代碼命名規(guī)范:大寫(xiě)開(kāi)頭
自定義構(gòu)造函數(shù)返回值(優(yōu)先)
//可以根據(jù)需要返回任意其他對(duì)象(必須是對(duì)象)
var Objectmaker = function() {
    this.name = 'this is it'
    var that = {}
    that.name = 'And that\'s that'
    return that
}

優(yōu)點(diǎn):避免忘記new帶來(lái)的問(wèn)題

缺點(diǎn):丟失原型鏈

自調(diào)用構(gòu)造函數(shù)
function Waffle() {
    if (!(this instanceof Waffle)) {
        return new Waffle()
    }
    this.state = "yummy"
}

具體的做法就是,在構(gòu)造函數(shù)中檢查this是否為構(gòu)造函數(shù)的一個(gè)實(shí)例,如果不是,構(gòu)造函數(shù)可以通過(guò)new關(guān)鍵字進(jìn)行自調(diào)用。

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

Object.defineProperty(obj, prop, descriptor)
//返回對(duì)象obj
  1. 數(shù)據(jù)描述符 可寫(xiě),不可寫(xiě)
  2. 存取描述符 getter, setter

兩者不能同時(shí)存在!

同時(shí)具有的可選鍵值:

  • configurable

    當(dāng)且僅當(dāng)該屬性的 configurable 為 true 時(shí),該屬性描述符才能夠被改變,也能夠被刪除。默認(rèn)為 false。

  • enumerable

    當(dāng)且僅當(dāng)該屬性的 enumerable 為 true 時(shí),該屬性才能夠出現(xiàn)在對(duì)象的枚舉屬性中。默認(rèn)為 false。

數(shù)據(jù)描述符可選鍵值:

  • value

    該屬性對(duì)應(yīng)的值。可以是任何有效的 JavaScript 值(數(shù)值,對(duì)象,函數(shù)等)。默認(rèn)為 undefined。

  • writable

    當(dāng)且僅當(dāng)該屬性的 writable 為 true 時(shí),該屬性才能被賦值運(yùn)算符改變。默認(rèn)為 false

存取描述符可選鍵值:

  • get

    一個(gè)給屬性提供 getter 的方法,如果沒(méi)有 getter 則為 undefined。該方法返回值被用作屬性值。默認(rèn)為 undefined

  • set

    一個(gè)給屬性提供 setter 的方法,如果沒(méi)有 setter 則為 undefined。該方法將接受唯一參數(shù),并將該參數(shù)的新值分配給該屬性。默認(rèn)為 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);

二 · 數(shù)組字面量

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('?')

//檢查數(shù)組性質(zhì)
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]";
    }
}

三 · 基本值類(lèi)型包裝器

var n = 100;   //基本類(lèi)型值,可臨時(shí)轉(zhuǎn)換成對(duì)象,使用屬性方法
console.log(typeof n);

var nobj = new Number(100);  //包裝對(duì)象
console.log(typeof nobj);

var test = Number(100);  //基本類(lèi)型值
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;

四 · 錯(cuò)誤對(duì)象

throw語(yǔ)句創(chuàng)建或拋出錯(cuò)誤對(duì)象

var errorHandle = function() {
}
try {
  throw {
    name: 'MyErrorType',
    message: "oops",
    extra: "This was rather embarrassing",
    remedy: errorHandle  //指定應(yīng)該處理該錯(cuò)誤的函數(shù)
  };
} catch (e) {
  //通知用戶(hù)
  alert(e.message);
  throw e;
  e.remedy();  //處理錯(cuò)誤
}

函數(shù)

函數(shù)是一個(gè)由Function()構(gòu)造函數(shù)創(chuàng)建的對(duì)象

函數(shù)提供局部作用域

一 · 定義函數(shù)的方法

  • 命名函數(shù)表達(dá)式(需要分號(hào)結(jié)尾)不需要特殊的語(yǔ)言結(jié)構(gòu),推薦

    var func = function add(a, b) {
      return a + b;
    };
    
  • 匿名函數(shù)表達(dá)式(需要分號(hào)結(jié)尾)不需要特殊的語(yǔ)言結(jié)構(gòu),推薦

    var func = function (a, b) {
      return a + b;
    };
    
    //匿名函數(shù)和命名函數(shù)表達(dá)式,函數(shù)聲明的區(qū)別在于有無(wú)函數(shù)對(duì)象name屬性
    var printFuncName = function(cb) {
      console.log(cb.name);
    }
    printFuncName(function() {})
    
  • 函數(shù)聲明(不需要分號(hào)結(jié)尾, 不能以參數(shù)形式出現(xiàn)在函數(shù)調(diào)用中)

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

二 · 函數(shù)提升

== 函數(shù)聲明及定義同時(shí)提升!==

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模式(為函數(shù)提供更好更整潔的接口)

回調(diào)模式

回調(diào)函數(shù)為全局函數(shù)或匿名函數(shù)時(shí)(非對(duì)象方法)

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";
});

回調(diào)與作用域(回調(diào)函數(shù)為對(duì)象方法)

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);
配置對(duì)象
addPerson('firstName', 'lastName', 22, 'address', 'gender');
addPerson({
  firstName: 'firstName',
  lastName: 'lastName',
  addr: 'address',
  age: 'age',
  gender: 0
})
返回函數(shù)

儲(chǔ)存函數(shù)私有數(shù)據(jù)

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);

函數(shù)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;
}

何時(shí)使用curry化?

調(diào)用同一個(gè)函數(shù),且傳遞的參數(shù)大多數(shù)是相同的

拓展:偏函數(shù)應(yīng)用 — from underscore

  • 偏函數(shù)應(yīng)用是找一個(gè)函數(shù),固定其中的幾個(gè)參數(shù)值,從而得到一個(gè)新的函數(shù)。

  • 函數(shù)加里化是一種使用匿名單參數(shù)函數(shù)來(lái)實(shí)現(xiàn)多參數(shù)函數(shù)的方法。

  • 函數(shù)加里化能夠讓你輕松的實(shí)現(xiàn)某些偏函數(shù)應(yīng)用。

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

四 · 初始化模式

在不污染全局命名空間的情況下,使用臨時(shí)變量以一種更加整潔,結(jié)構(gòu)化的方式執(zhí)行初始化以及設(shè)置任務(wù)

即時(shí)函數(shù)
//即時(shí)函數(shù)(提供局部作用域)
(function() {
    //...
}());
(function(global) {
    //...
})(window);

//初始化時(shí)分支
即時(shí)對(duì)象初始化
//保護(hù)全局命名空間,更結(jié)構(gòu)化
({
  //配置常量
  maxwidth:600,
  maxheight: 400,
  gimmeMax: function() {
    return this.maxwidth + "x" + this.maxheight;
  },
  //初始化
  init: function() {
    console.log(this.gimmeMax());
  }
}).init();
初始化時(shí)分支
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 {
  //...
}

五 · 性能模式

加速代碼運(yùn)行

備忘模式(函數(shù)屬性)
var myFunc = function(param) {
  if (!myFunc.cache[param]) {
    var result = {};
    //開(kāi)銷(xiāo)很大的操作
    myFunc.cache[param] = result;
  }
  return myFunc.cache[param];
};

myFunc.cache = {};

**應(yīng)用:斐波那契數(shù)列第n項(xiàng)的計(jì)算 **

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);
}

//用備忘模式優(yōu)化后求斐波那契
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 = {};
自定義模式

函數(shù)在第一次運(yùn)行時(shí),才被正確的定義

//惰性函數(shù)定義(初始化任務(wù),且只需執(zhí)行一次)
var scareMe = function() {
    alert("Boo!");
    scareMe = function() {
        alert("Double boo!")
    }
}
//用自定義模式再次優(yōu)化
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);
};

對(duì)象

命名空間模式

var MYAPP = {};

//構(gòu)造函數(shù)&函數(shù)
MYAPP.Parent = function() {};

//變量
MYAPP.someValue = 1;

//對(duì)象
MYAPP.modules = {};

通用命名空間函數(shù)(非破壞性的)

//不安全的
var MYAPP = {};
//更好的代碼風(fēng)格
if(typeof MYAPP === "undefined") {
    var MYAPP = {};
}
var MYAPP = MYAPP || {};

//通用命名空間函數(shù)
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');

依賴(lài)聲明模式

在函數(shù)或模塊頂部聲明代碼所依賴(lài)的模塊

  • 顯式聲明依賴(lài)更清晰
  • 解析速度加快
  • 更小的代碼量
var myFunction = function() {
    var event = YAHOO.util.Event,
        dom = YAHOO.util.Dom;
    //使用event dom 變量
}

模塊模式

命名空間, 即時(shí)函數(shù), 私有和特權(quán)方法, 聲明依賴(lài)

MYAPP.namespace('MYAPP.utilities.array');

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

沙箱模式

全局對(duì)象 -> 全局構(gòu)造函數(shù)Sandbox()

new Sandbox(【】function(box) {
  // ...
})
  1. 將全局屬性方法的初始化函數(shù)放在Sandbox構(gòu)造函數(shù)的屬性modules中
  2. 在構(gòu)造函數(shù)內(nèi)部獲取所需的屬性,賦值給this
  3. 運(yùn)行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')};
}
//實(shí)現(xiàn)構(gòu)造函數(shù)
function Sandbox() {
    var args = Array.prototype.slice.call(arguments),
        //最后一個(gè)參數(shù)是回調(diào)
        callback = args.pop(),
        //模塊可以作為數(shù)組或單獨(dú)的參數(shù)傳遞
        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();
})

靜態(tài)成員(在類(lèi)的層次上操作,包含非實(shí)例相關(guān)的方法和數(shù)據(jù))

公有靜態(tài)成員(不需要特定的實(shí)例就能運(yùn)行)
// 公有靜態(tài)成員
var Gadget = function() {};

//靜態(tài)方法
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);
};
私有靜態(tài)成員
  • 同一個(gè)構(gòu)造函數(shù)的所有對(duì)象共享該成員
  • 構(gòu)造函數(shù)外部不能訪(fǎng)問(wèn)

exp: 對(duì)象id標(biāo)識(shí)符

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)

代碼復(fù)用模式

GoF的原則: 優(yōu)先使用對(duì)象組合,而不是類(lèi)繼承

類(lèi)式繼承模式

//父構(gòu)造函數(shù)
function Parent(name) {
  this.name = name || 'Adam';
}
//向改原型添加功能
Parent.prototype.say = function() {
  return this.name;
}

//空白的子構(gòu)造函數(shù)
function Child(name) {}

inherit(Child, Parent);
  1. 默認(rèn)模式(引用)

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

    缺點(diǎn):

    • 同時(shí)繼承了this上的特權(quán)屬性和原型屬性;(關(guān)于構(gòu)造函數(shù)的一般經(jīng)驗(yàn)法則是,應(yīng)該將可復(fù)用的屬性加到原型)

    • 不支持將參數(shù)傳遞到子構(gòu)造函數(shù)(父?)中, 而子構(gòu)造函數(shù)然后又將參數(shù)傳遞到父構(gòu)造函數(shù)中

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

      ?

  2. 借用構(gòu)造函數(shù)(獨(dú)立副本)

    //父構(gòu)造函數(shù)
    function Article() {
      this.tags = ['js', 'css']
    }
    //父構(gòu)造函數(shù)的實(shí)例
    var article = new Article();
    
    //子構(gòu)造函數(shù)1, 默認(rèn)模式繼承
    function BlogPost() {}
    BlogPost.prototype = article;
    
    //子構(gòu)造函數(shù)1的實(shí)例
    var blog = new BlogPost();
    
    //子構(gòu)造函數(shù)2, 借助構(gòu)造函數(shù)繼承
    function StaticPage() {
     Article.call(this);
    }
    //子構(gòu)造函數(shù)2的實(shí)例
    var page = new StaticPage();
    
    console.log(article.hasOwnProperty("tags"))
    console.log(page.hasOwnProperty("tags"))
    console.log(blog.hasOwnProperty("tags"))
    

    只能繼承父構(gòu)造函數(shù)this中的屬性,變?yōu)樽陨韺傩裕徊荒芾^承原型

    原型鏈?zhǔn)菙嚅_(kāi)的

    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;
    

    可實(shí)現(xiàn)多重繼承

    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);
    

    缺點(diǎn):

    • 無(wú)法從原型繼承
  3. 借用和設(shè)置原型(結(jié)合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();
    

    優(yōu)點(diǎn):

    • 繼承父對(duì)象的一切,且能安全的修改自身屬性

    缺點(diǎn):

    • 父構(gòu)造函數(shù)被調(diào)用了兩次,效率低下。父對(duì)象自身的屬性,會(huì)被繼承兩次
  4. 共享原型

    //任何值得繼承的東西都應(yīng)該放置在原型中實(shí)現(xiàn)!
    function inherit(C, P) {
      C.prototype = P.prototype;
    }
    
  5. 臨時(shí)構(gòu)造函數(shù)

    function inherit(C, p) {
      var F = function () {};
      F.prototype = P.prototype;
      C.prototype = new F();
    }
    
  6. 重置構(gòu)造函數(shù)指針(最終版本)

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

    ?

原型繼承(無(wú)類(lèi)繼承)

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);

通過(guò)復(fù)制屬性繼承(無(wú)原型)

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"}
)

借用方法

僅需要使用現(xiàn)有對(duì)象其中一個(gè)或兩個(gè)方法

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']);

設(shè)計(jì)模式

單體模式

一個(gè)特定類(lèi)僅有一個(gè)實(shí)例

  1. 靜態(tài)屬性實(shí)現(xiàn)
function Universe() {
  if(typeof Universe.instance === 'object') {
    return Universe.instance;
  }
  this.start_time = 0;
  this.bang = 'Big';
  
  Universe.instance = this;
  
  //隱式返回
  //return this;
}
  1. 閉包實(shí)現(xiàn)(惰性定義函數(shù))

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

    缺點(diǎn):重定義后,加入的原型無(wú)效; 實(shí)例constructor指向重定向前的構(gòu)造函數(shù) ???

    function Universe() {
      var instance = this;
      Universe = function() {
        return instance;
      };
      
      //保留原型
      Universe.prototype = this;
      console.log(this.constructor);
      
      //實(shí)例
      instance = new Universe;
      
      //重置構(gòu)造函數(shù)指針
      instance.constructor = Universe;
      
      instance.start_time = 0;
      instance.bang = "Big";
      
      return instance;
    }
    

    Universe1 prototype <—實(shí)例— this = Universe2.prototype < —實(shí)例— 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";
 };

}());


?

#### 工廠(chǎng)模式 

在編譯時(shí)不知道具體類(lèi)型,為工廠(chǎng)客戶(hù)提供一種創(chuàng)建對(duì)象的接口

通過(guò)工廠(chǎng)方法創(chuàng)建的對(duì)象在設(shè)計(jì)上都繼承了相同的父對(duì)象這個(gè)思想,它們都是實(shí)現(xiàn)專(zhuān)門(mén)功能的特定子類(lèi)

?```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"


//父構(gòu)造函數(shù)
function CarMaker() {}
CarMaker.prototype.drive = function() {
    return "Vroom, I have " + this.doors + " doors";
};
//靜態(tài)工廠(chǎng)方法
CarMaker.factory = function (type) {
  var constr = type,
      newcar;
  //尋找種類(lèi)構(gòu)造函數(shù)
  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;
};

//不同種類(lèi)的構(gòu)造函數(shù)
CarMaker.Compact = function() {
  this.doors = 4;
}
CarMaker.Convertible = function() {
  this.doors = 2;
}
CarMaker.SUV = function() {
  this.doors = 17;
}
內(nèi)置對(duì)象工廠(chǎng) Object
var o = new Object(),
    n = new Object(1),
    s = Object('1'),
    b = Object(true);

迭代器模式

訪(fǎng)問(wèn)數(shù)據(jù)結(jié)構(gòu)中的每個(gè)元素

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());
}

裝飾者模式

使用繼承實(shí)現(xiàn) 運(yùn)行時(shí)動(dòng)態(tài)添加附加功能到對(duì)象中
var sale = new Sale(100);       //價(jià)格100美元
sale1 = sale.decorate('fedtax');   //增加聯(lián)邦稅
sale2 = sale1.decorate('quebec');       //增加省級(jí)稅
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;
}
使用列表實(shí)現(xiàn)
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;
}

策略模式

運(yùn)行時(shí)選擇算法

exp: 表單驗(yàn)證

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"
    }
  },
  //當(dāng)前驗(yàn)證會(huì)話(huà)中的錯(cuò)誤信息
  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;
  }
}

拓展:包裝成類(lèi)

外觀模式

把常用方法包裝到一個(gè)新方法中,從而提供更為便利的api

兩個(gè)或者更多的方法可能普遍被一起調(diào)用??創(chuàng)建另一個(gè)方法已包裝重復(fù)的方法調(diào)用

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

代理模式

通過(guò)包裝一個(gè)對(duì)象以控制對(duì)它的訪(fǎng)問(wèn),將訪(fǎng)問(wèn)聚集為組或僅當(dāng)真正必要的時(shí)候才執(zhí)行訪(fǎng)問(wèn),避免造作開(kāi)銷(xiāo)。

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

延遲初始化

var delay = function() {
  //開(kāi)銷(xiāo)很大的初始化操作
  delay = function() {
    //真正的操作方法
  }
}

http請(qǐng)求

中介者模式

對(duì)象之間不直接通話(huà),而是通過(guò)一個(gè)中介者對(duì)象進(jìn)行通信,從而實(shí)現(xiàn)送耦合

mediator.html

觀察者模式(自定義事件)

瀏覽器事件

促成松散耦合

subscribers  []
subscribe() 將訂閱者添加到subscribers數(shù)組
unsubscribe() 從訂閱者數(shù)組subscribers中刪除訂閱者
publish()  循環(huán)遍歷subscribers中的每個(gè)元素,并且調(diào)用他們注冊(cè)時(shí)所提供的方法
visitSubscribers()
type

exp. observer.html observer-game.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,791評(píng)論 6 545
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,795評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 178,943評(píng)論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 64,057評(píng)論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,773評(píng)論 6 414
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 56,106評(píng)論 1 330
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,082評(píng)論 3 450
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 43,282評(píng)論 0 291
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,793評(píng)論 1 338
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,507評(píng)論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,741評(píng)論 1 375
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,220評(píng)論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,929評(píng)論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 35,325評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 36,661評(píng)論 1 296
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,482評(píng)論 3 400
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,702評(píng)論 2 380

推薦閱讀更多精彩內(nèi)容