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)?
暗示全局變量
-
var
聲明的鏈?zhǔn)劫x值var a = b = 1; var a = (b = 1);
-
隱含全局變量 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è)階段
- 解析和進(jìn)入上下文階段(創(chuàng)建變量~函數(shù)聲明~形式參數(shù))
- 代碼運(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
- 數(shù)據(jù)描述符 可寫(xiě),不可寫(xiě)
- 存取描述符 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) {
// ...
})
- 將全局屬性方法的初始化函數(shù)放在Sandbox構(gòu)造函數(shù)的屬性modules中
- 在構(gòu)造函數(shù)內(nèi)部獲取所需的屬性,賦值給this
- 運(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);
-
默認(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();
?
-
借用構(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ú)法從原型繼承
-
借用和設(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ì)被繼承兩次
-
共享原型
//任何值得繼承的東西都應(yīng)該放置在原型中實(shí)現(xiàn)! function inherit(C, P) { C.prototype = P.prototype; }
-
臨時(shí)構(gòu)造函數(shù)
function inherit(C, p) { var F = function () {}; F.prototype = P.prototype; C.prototype = new F(); }
-
重置構(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í)例
- 靜態(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;
}
-
閉包實(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