如:javascript
var jsstring = "var un = 1;"; eval(jsstring); console.log(typeof un); // "number"
方法1:使用new Function()。new Function()中的代碼將在局部函數空間中運行。css
var jsstring = "var un2 = 1;"; new Function(jsstring)(); console.log(typeof un2); // "undefined"
方法2:將eval()封裝到一個即時函數中。html
var jsstring = "var un3 = 1;"; (function() { eval(jsstring); })() console.log(typeof un3); // "undefined"
(function() { var local = 1; eval("local = 3; console.log(local); "); // 3 console.log(local); // 3 })(); (function() { var local = 1; new Function("console.log(typeof local);")(); // undefined })();
做用:java
function Waffle() { if (!(this instanceof Waffle)) { // 或者: if (!(this instanceof arguments.callee)) { return new Waffle(); } this.tastes = "yummy"; } Waffle.prototype.wantAnother = true; // 測試調用 var first = new Waffle(), second = Waffle(); console.log(first.tastes); // yummy console.log(second.tastes); // yummy console.log(first.wantAnother); // true console.log(second.wantAnother); // true
var a = new Array(3); console.log(a.length); // 3 console.log(a[0]); // undefined var a = new Array(3.14); // RangeError: invalid array length console.log(typeof a); // undefined;
使用Array()構造函數,返回一個具備255個空白字符的字符串:數組
var white = new Array(256).join(' ');
ES5定義了Array.isArray()方法。或調用Object.prototype.toString()方法。瀏覽器
if (typeof Array.isArray() === 'undefined') { Array.isArray = function(arg) { return Object.prototype.toString.call(arg) === '[object Array]'; } }
函數體內部,以一個新函數覆蓋了舊函數,回收了舊函數指針以指向一個新函數。即,該函數以一個新的實現覆蓋並從新定義了自身。緩存
var scareMe = function() { alert("Boo!"); scareMe = function() { alert("Double boo!"); } } // 使用自定義函數 scareMe(); // Boo! scareMe(); // Double boo!
做用1:存儲私有數據。即時函數能夠返回函數,所以能夠利用即時函數的做用域存儲一些私有數據,而這特定於返回函數的內部函數。安全
var getResult = (function() { var res = 2+2; return function() { return res; } })(); console.log(getResult()); // 4
做用2:定義對象屬性。數據結構
var o = { message: (function() { var who = "me", what = "call"; return what + " " + who; })(), getMsg: function() { return this.message; } } // 用法 console.log(o.getMsg()); // call me console.log(o.message); // call me
使用帶有init()方法的對象,該方法在建立對象後將會當即執行。init()函數負責全部的初始化任務。閉包
優勢:能夠在執行一次初始化任務時保護全局命名空間。
這種模式主要適用於一次性任務,並且init()完畢後也沒有對該對象的訪問。若是想在init()完畢後保存對該對象的一個引用,能夠在init()尾部添加return this
({ max: 60, getMax: function() { return this.max; }, // 初始化 init: function() { console.log(this.getMax()); // 60 // 更多初始化任務 } }).init();
函數能夠在任什麼時候候將自定義屬性添加在函數中。自定義屬性的其中一個用例是緩存函數結果(即返回值),所以,在下一次調用該函數時就不用重作潛在的繁重計算。
能夠爲函數建立一個屬性cache,該屬性是一個對象,其中使用傳遞給函數的參數做爲鍵,而計算結果做爲值。計算結果能夠是須要的任意複雜數據結構。對於有更多以及更復雜的參數,通用的解決方案是將它們序列化。例如,能夠將參數對象序列化爲一個JSON字符串,並使用該字符串做爲cache對象的鍵。
var myFunc = function() { var cachekey = JSON.stringify(Array.prototype.slice.call(arguments)), result; if (!myFunc.cache[cachekey]) { result = {}; // ``` 開銷很大的操做``` console.log('Do Job'); myFunc.cache[cachekey] = result; } return myFunc.cache[cachekey]; } // 緩存存儲 myFunc.cache = {}; // 測試 myFunc(0); // Do Job myFunc(0); // 無
function curry(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); } } // 普通函數 function add(a, b, c, d, e) { return a + b + c + d + e; } // 可運行於任意數量的參數 curry(add, 1, 2, 3)(5, 5); // 16 // 兩步curry化 var addOne = curry(add, 1); addOne(10, 10, 10, 10); // 41 var addSix = curry(addOne, 2, 3); addSix(5, 5); // 16
添加到命名空間的一些屬性可能已經存在,這致使可能會覆蓋它們。所以,在添加一個屬性或者建立一個命名空間以前,最好是首先檢查它是否已經存在:
if (typeof MYAPP === 'undefined') { var MYAPP = {}; } // 或者: var MYAPP = MYAPP || {};
能夠定義一個處理命名空間細節的可重用的函數。這個實現是非破壞性的,也就是說,若是已經存在一個命名空間,便不會再從新建立它。
var MYAPP = MYAPP || {}; MYAPP.namespace = function(ns_string) { var parts = ns_string.split('.'), parent = MYAPP, i; // 剝離最前面的冗餘全局變量 if (parts[0] === "MYAPP") { parts = parts.slice(1); } for (i=0; i<parts.length; i++) { // 若是不存在,就建立一個屬性 parent[parts[i]] = parent[parts[i]] || {}; parent = parent[parts[i]]; } return parent; } // 測試 var module1 = MYAPP.namespace('MYAPP.modules.module1'); module1 === MYAPP.modules.module1; // 忽略最前面的MYAPP var module2 = MYAPP.namespace('modules.module2'); module2 === MYAPP.modules.module2; // 長命名空間 MYAPP.namespace('one.two.three.four.five.six.seven.eight'); P93. 聲明依賴 var myFunction() { // 依賴 var event = YAHOO.util.Event, dom = YAHOO.util.Dom; // 使用事件和DOM變量 // ... }
構造函數建立一個閉包,而在閉包範圍內部的任意變量都不會暴露給構造函數之外的代碼。然而,這些私有變量仍然能夠用於公共方法中,也稱爲特權方法。
function Gadget(){ // 私有成員 var name = 'iPod'; // 公有函數或特權方法 this.getName = function() { return name; } } // 測試 var toy = new Gadget(); console.log(toy.name) // undefined console.log(toy.getName()); // iPod
私有性失效:當直接從一個特權方法中返回一個私有變量,且該變量剛好是一個對象或者數組,那麼外面的代碼仍然能夠訪問該私有變量。由於這是經過引用傳遞的。
function Gadget() { // 私有成員 var specs = { screen_width: 320, screen_height: 480, color: "white" }; // 公有函數或特權方法 this.getSpecs = function() { return specs; } } // 測試 var toy = new Gadget(), specs = toy.getSpecs(); specs.color = "black"; specs.price = "free"; console.dir(toy.getSpecs());
私有性失效的解決方法:
使getSpecs()返回一個新對象,該對象僅包含客戶關注的原對象中的數據。即最低受權原則。
使用一個通用性的對象克隆函數,以建立specs對象的副本。如淺複製、深複製。
可使用一個額外的匿名即時函數建立閉包來實現私有性。
方法1:
var myobj; (function(){ // 私有成員 var name = "Peter"; // 公有部分 myobj = { // 特權方法 getName: function() { return name; } } })(); // 測試 console.log(myobj.name) // undefined console.log(myobj.getName()); // iPod
方法2:模塊模式
var myobj = (function(){ // 私有成員 var name = "Peter"; // 公有部分 return { getName: function() { return name; } } })(); // 測試 console.log(myobj.name) // undefined console.log(myobj.getName()); // iPod
當私有成員與構造函數一塊兒使用時,一個缺點在於每次調用構造函數以建立對象時,這些私有成員都會被從新建立。爲了不復制工做以節省內存,能夠將經常使用屬性和方法添加到構造函數的prototype屬性中。這樣,經過同一個構造函數建立的多個實例,能夠共享常見的部分數據。
可使用如下兩個模式的組合:構造函數中的私有屬性,以及對象字面量中的私有屬性。因爲prototype屬性僅是一個對象,所以可使用對象字面量建立該對象。
function Gadget() { // 私有成員 var name = 'iPod'; // 公有函數或特權方法 this.getName = function() { return name; } } Gadget.prototype = (function() { // 私有成員 var browser = "Mobile Webkit"; // 公有原型成員 return { getBrowser: function() { return browser; } } })(); // 測試 var toy = new Gadget(); console.log(toy.getName()); // iPod console.log(toy.getBrowser()); // Mobile Webkit
模塊模式是如下模式的組合:
方法1:返回對象
MYAPP.namespace('MYAPP.utilities.array'); MYAPP.utilities.array = (function() { // 依賴 var uobj = MYAPP.utilities.object, ulang = MYAPP.utilities.lang, // 私有屬性 array_string = "[object Array]", ops = Object.prototype.toString; // 私有方法 // ... // 可選的一次性初始化過程 console.log('Creating namespace: array'); // 公有API return { isArray: function(a) { return ops.call(a) === array_string; } // ...更多方法和屬性 } })();
方法2:返回構造函數
MYAPP.namespace('MYAPP.utilities.array'); MYAPP.utilities.array = (function(){ // 依賴 var uobj = MYAPP.utilities.object, ulang = MYAPP.utilities.lang, // 私有屬性和方法 Constr; // 可選的一次性初始化過程 console.log('Creating namespace: array'); // 公有API——構造函數 Constr = function(o) { this.elements = this.toArray(o); }; // 公有API——原型 Constr.prototype = { constructor: MYAPP.utiltities.array, version: "2.0", toArray: function(obj) { for (var i=0, a=[], len=obj.length; i<len; i++) { a[i] = obj[i]; } return a; } } // 返回新的構造函數 return Constr; })(); // 測試 var arr = new MYAPP.utilities.array(obj);
靜態屬性和方法,是指從一個實例到另外一個實例都不會發生改變的屬性和方法。
靜態成員的優勢:能夠包含非實例相關的方法和屬性,而且不會爲每一個實例從新建立靜態屬性。
可使用構造函數,而且向其添加屬性這種方式。靜態成員,不須要特定的對象就可以運行。同時爲了使實例對象也能夠調用靜態成員,只須要向原型中添加一個新的成員便可,其中該新成員做爲一個指向原始靜態成員的外觀。
// 構造函數 var Gadget = function() {}; // 靜態方法 Gadget.isShiny = function() { return 'you bet'; }; // 向原型中添加一個普通方法 Gadget.prototype.setPrice = function(price) { this.price = price; } // 向原型中添加一個外觀 Gadget.prototype.isShiny = Gadget.isShiny; // 測試 // 調用靜態方法 Gadget.isShiny(); // you bet; // 實例調用普通方法 var iphone = new Gadget(); iphone.setPrice(500); // 實例調用靜態方法 iphone.isShiny(); // you bet
私有靜態成員具備以下屬性:
實現方法:閉包+即時函數。可參考模塊模式的返回構造函數。
// 構造函數 var Gadget = (function() { // 靜態變量 var counter = 0, NewGadget; // 新的構造函數的實現 NewGadget = function() { counter ++; }; // 特權方法 NewGadget.prototype.getLastId = function() { return counter; }; // 覆蓋該構造函數 return NewGadget; })(); // 測試 var iphone = new Gadget(); console.log(iphone.getLastId()); var ipod = new Gadget(); console.log(ipod.getLastId()); var ipad = new Gadget(); console.log(ipad.getLastId());
做用:向構造函數的原型中添加新方法。
if (typeof Function.prototype.method !== "function") { Function.prototype.method = function(name, implementation) { this.prototype[name] = implementation; return this; // 返回this(指向構造函數),支持鏈式調用 } } // 測試 // 構造函數 var Person = function(name) { this.name = name; }. method('getName', function() { return this.name; }). method('setName', function(name) { this.name = name; return this; }); // 對象 var a = new Person('Adam'); a.getName(); // Adam; a.setName('Eve').getName(); // Eve
實現方式:
// 父構造函數 function Parent(name) { this.name = name || 'Adam'; } // 向原型中添加方法 Parent.prototype.say = function() { return this.name; } // 子構造函數(空白) function Child(name) {} // 繼承:設置原型 Child.prototype = new Parent(); // 測試 var kid = new Child(); kid.say(); // Adam
原型鏈:
注意:
__proto__
屬性僅用來解釋原型鏈,不可用於開發中。// 演示缺點1 var s = new Child('Seth'); s.say(); // Adam
// 演示缺點2 // 父構造函數 function Article() { this.tags = ['js', 'css']; } var article = new Article(); // 子構造函數及繼承 function Blog() {} Blog.prototype = article; // 子對象意外修改父對象的引用屬性 var blog = new Blog(); blog.tags.push('html'); console.log(article.tags.join(' ')); // js css html
實現方式:
// 父構造函數 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('Partrick'); kid.name; // Partick typeof kid.say; // undefined
原型鏈:
注意:
實現方式:
// 父構造函數 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('Partrick'); kid.name; // Partick kid.say(); // Partick delete kid.name; kid.say(); // Adam
原型鏈:
注意:
實現方式:
// 父構造函數 function Parent(name) { this.name = name || 'Adam'; } // 向原型中添加方法 Parent.prototype.say = function() { return this.name; } // 子構造函數 function Child() {} // 繼承:共享原型 child.prototype = Parent.prototype;
原型鏈:
注意:
實現方式:
// 父構造函數 function Parent(name) { this.name = name || 'Adam'; } // 向原型中添加方法 Parent.prototype.say = function() { return this.name; } // 子構造函數 function Child(name) {} // 繼承:設置原型 inherit(Child, Parent); // 實現: function inherit(C, P) { var F = function() {}; F.prototype = P.prototype; C.prototype = new F(); C.prototype.constructor = C; } // 優化:避免在每次須要繼承時,都建立臨時(代理)構造函數。 // 實現:即時函數+閉包 var inherit2 = (function() { var F = function() {}; return function(C, P) { F.prototype = P.prototype; C.prototype = new F(); C.prototype.constructor = C; } })(); // 測試 var kid = new Child(); kid.say(); // undefined kid.name = "Peter"; kid.say(); // Peter
原型鏈:
注意:
實現方式:
function object(P) { var F = function() {}; F.prototype = P; return new F(); }
對象字面量方式建立父對象
var parent = { name: "papa" } var child = object(parent); // 測試 console.log(child.name);
構造函數方式建立父對象
// 父構造函數 function Parent() { this.name = "papa"; } Parent.prototype.getName = function() { return this.name; } // 建立一個父對象 var papa = new Parent(); // 繼承方式1:父構造函數中的this屬性、父原型的屬性都被繼承 var kid = object(papa); console.log(typeof kid.name); // string console.log(typeof kid.getName); // function // 繼承方式2:僅繼承父原型的屬性 var kid = object(Parent.prototype); console.log(typeof kid.name); // undefined console.log(typeof kid.getName); // function
ES5: Object.create()
在使用淺複製時,若是改變了子對象的屬性,而且該屬性剛好是一個對象,那麼這種操做也將修改父對象。
function extend(parent, child) { var i; child = child || {}; for (i in parent) { if (parent.hasOwnProperty(i)) { child[i] = parent[i]; } } return child; } // 測試 var dad = { counts: [1, 2, 3], reads: { paper: true } }; var kid = extend(dad); kid.counts.push(4); dad.counts.toString(); // 1,2,3,4 dad.reads === kid.reads; // true
檢查父對象的某個屬性是否爲對象,若是是,則須要遞歸複製出該對象的屬性。
function extendDeep(parent, child) { var i, toStr = Object.prototype.toString, astr = "[object Array]"; child = child || {}; for (i in parent) { if (parent.hasOwnProperty(i)) { if (typeof parent[i] === 'object') { child[i] = (toStr.call(parent[i]) === astr) ? [] : {}; extendDeep(parent[i], child[i]); } else { child[i] = parent[i]; } } } return child; } // 測試 var dad = { counts: [1, 2, 3], reads: { paper: true } }; var kid = extendDeep(dad); kid.counts.push(4); dad.counts.toString(); // 1,2,3 dad.reads === kid.reads; // false
從多個對象中複製出任意成員,並將這些成員組合成一個新的對象。
遇到同名屬性,老是使用靠後對象的值,即越日後優先級越高。
function mix() { var i, prop, child = {}; for (i = 0; i<arguments.length; i++) { for (prop in arguments[i]) { if (arguments[i].hasOwnProperty(prop)) { child[prop] = arguments[i][prop]; } } } return child; } // 測試 var cake = mix( { eggs: 2, large: true }, { buter: 1, saleted: true }, { flour: "3 cups" }, { sugar: "sure!" }, { eggs: 3 } // 同名屬性,越日後優先級越高 ); console.dir(cake);
function bind(obj, fn) { return function() { return fn.apply(obj, Array.prototype.slice.call(arguments)); }; }
ES5: Funtion.prototype.bind()
if (typeof Function.prototype.bind === 'undefined') { Function.prototype.bind = function(obj) { var fn = this, slice = Array.prototype.slice, args = slice.call(arguments, 1); return function() { return fn.apply(obj, args.concat(slice.call(arguments))); } ; }; }
function lazyload(file) { var script = document.createElement("script"); script.src = file; document.getElementsByTagName("head")[0].appenChild(script); } // 使用 window.onload = function() { lazyload('extra.js'); }
function require(file, callback){ var script = document.createElement ("script"); script.type = "text/javascript"; if (script.readyState){ //IE script.onreadystatechange = function(){ if (script.readyState === "loaded" || script.readyState === "complete"){ script.onreadystatechange = null; callback(); } }; } else { //Others script.onload = function(){ callback(); }; } script.src = file; document.getElementsByTagName("head")[0].appendChild(script); } // 使用 require("extra.js", function() { // 執行extra.js中定義的函數 });
<object>
來代替腳本元素,並向其data屬性指向腳本的URL。var preload; if (/*@cc_on!@*/false) { // 使用條件註釋的IE嗅探 preload = function(file) { new Image().src = file; }; } else { preload = function(file) { var obj = document.createElement('object'); obj.width = 0; obj.height = 0; obj.data = file; document.body.appendChild(obj); }; } // 使用: preload('extra.js');