今天的知識怎麼說呢,說多也多,說少也少,有些凌亂。主要的知識點是閉包的三個應用、嚴格模式的相關知識點、ES5數組新方法、建立對象的方式、繼承的方式。下面對各個知識點作詳細介紹。編程
小案例:音樂模板的js代碼數組
onload = function() { //入口函數 function bindNav(fn) { var lis = document.querySelectorAll("#nav li"); for(var i = 0, l = lis.length; i < l; i++){ lis[i].addEventListener("click", function() { switch(this.getAttribute('data-name')){ case "find": fn("這是發現音樂。"); break; case "mine": fn("這是個人音樂。"); break; case "friend": fn("這是個人朋友。"); break; } }); } } function curryingBindContent(content) { return function(text) { document.querySelector(content).innerHTML = text; }; } bindNav(curryingBindContent("#content")); };
小案例:統計某地一週售樓的數量瀏覽器
/** * [curryingSales 建立能夠獲取和保存售樓數閉包函數] * @param {Function} fn [fn函數決定是獲取周總數] * @return {[type]} [閉包函數] */ function curryingSales(fn){ var dailySales=[]; return function(val){ if(val==undefined){ return fn.apply(null,dailySales); }else{ dailySales.push(val); } }; } var weeklyTotal=curryingSales(function(){ var sum=0; for(var i=0;l=arguments.length,i<l;i++){ sum+=arguments[i]; } return sum; }); weeklyTotal(2); weeklyTotal(1); weeklyTotal(2); weeklyTotal(3); weeklyTotal(1); weeklyTotal(5); weeklyTotal(4); console.log(weeklyTotal());
小案例:對addEventListener和attachEvent的兼容代碼安全
var addEvent = function() { // 瀏覽器支持addEventListener方法 if(window.addEventListener){ return function(element, type, callback, captrue) { elem.addEventListener(type, callback, captrue); }; } else { // 瀏覽器支持attachEvent方法 return function(element, type, callback) { elem.attachEvent('on' + type, callback); }; } }();
(1)消除JavaScript語法的一些不合理、不嚴謹之處,減小一些怪異行爲;閉包
(2)消除代碼運行的一些不安全之處,保證代碼運行的安全;app
(3)提升編譯效率,增長運行效率;函數
(4)爲將來新版本的JavaScript作好鋪墊。this
直接在js代碼中直接添加 "use strict";便可, 在老版本瀏覽器會將其當作一行普通字符串忽略。spa
(1)針對整個腳本文件prototype
將"use strict;"放在腳本文件的第一行,則整個腳本將以「嚴格模式」運行。若是此語句不放在第一行,則無效,整個腳本以「正常模式」運行。
<script> "use strict"; console.log("這是嚴格模式"); </script> <script> console.log("這是正常模式"); "use strict"; </script>
(2)針對單個函數
將"use strict;"放在函數體的第一行,則整個函數以"嚴格模式"運行。
function strict() { "use strict"; console.log("這是嚴格模式"); } function noStrict() { console.log("這是正常模式"); "use strict"; }
(3)腳本文件的變通寫法
因爲第一種方式不利於文件合併,因此最好的作法是:將整個腳本文件放在一個沙箱模式(當即執行的匿名函數)中。
(function() { "use strict"; // 代碼塊 }());
在正常模式中,若是一個變量沒有聲明就賦值,默認是全局變量。可是在嚴格模式已禁止這種用法,全局變量必須顯式聲明。
"use strict";
v = 1; // 報錯,v is not defined。
在嚴格模式下,變量必須先使用var定義,再賦值。
JavaScript語言一個特色,就是容許"動態綁定",即某些屬性和方法屬於哪個對象,不是在編譯時肯定的,而是在運行時肯定的。
嚴格模式對動態綁定作一些限制。某些狀況下,只容許靜態綁定。也就是說,屬性和方法到底歸屬哪一個對象,在編譯階段就肯定。這樣作有利於編譯效率的提升,也使代碼更易閱讀,更少出現bug。
a. 禁止使用with語句
緣由:with語句沒法在編譯階段就肯定屬性到底歸屬哪一個對象。
"use strict"; var a = 1; // 報錯,語法異常 with(obj){ a = 2; }
b. eval做用域
正常模式,JavaScript語言具備兩種變量做用域:全局做用域 和 函數做用域(局部做用域)。
嚴格模式,具備第三種做用域:eval做用域。
正常模式下,eval語句的做用域取決於 它處於全局做用域,仍是函數做用域。
嚴格模式下,eval語句自己就是一個做用域,再也不可以產生全局變量,其所生產的變量只能用於eval內部。
<script> "use strict"; var x = 2; console.log(eval("var x = 3; x")); // 3 console.log(x); // 2 </script>
c. 加強安全性
在普通函數執行模式下,禁止this關鍵字指向全局對象. 所以,在使用構造函數時,忘記寫new,this就再也不指向window對象,而是報錯。就不會意外給window對象添加屬性或方法。
function foo() { console.log(this); // window } function foo() { "use strict"; console.log(this); // undefined } function foo() { "use strict"; this.name = "tom"; } var f = foo(); // 報錯
d. 禁止在函數內部訪問caller以及arguments
function fn() { "use strict"; fn.caller; // 報錯 fn.arguments; // 報錯 } fn();
e. 禁止刪除變量
在嚴格模式下沒法刪除變量。只有configurable設置爲true的對象屬性,才能被刪除。
"use strict"; var x; delete x; // 報錯 var obj = Object.create(null, { "x": { value: 10, configurable: true } }); delete obj.x; // success
f. 顯式報錯
(1) 在正常模式下,爲一個對象的只讀屬性進行賦值,不會報錯,只會默默的失敗;而嚴格模式下,會拋出異常。
"use strict"; var obj = {}; Object.defineProperty(o, "a", { value: 1, writable: false }); obj.a = 2; // 報錯
(2) 嚴格模式下,對一個使用getter方法讀取的屬性進行賦值,會報錯。
"use strict"; var obj = { get a() { return 1; } }; obj.a = 2; // 報錯
(3) 嚴格模式下,對禁止擴展的對象添加新屬性,會報錯。
"use strict"; var obj = {}; Object.preventExtensions(obj); obj.a = 1; // 報錯
(4)嚴格模式下,刪除一個不可刪除的屬性,會報錯。
"use strict"; delete Object.prototype; // 報錯
g. 重名錯誤: 嚴格模式新增了一些語法錯誤。
(1)對象不能有重名的屬性
正常模式下,若是對象有多個重名屬性,最後賦值的那個屬性會覆蓋前面的值。嚴格模式下,這屬於語法錯誤。
"use strict"; var obj = { p: 1, p: 2 }; // 語法錯誤
(2)函數不能有重名的參數
正常模式下,若是函數有多個重名的參數,能夠用arguments[i]讀取。嚴格模式下,這屬於語法錯誤。
"use strict"; function f(a, a, b) { // 語法錯誤 return; }
arguments是函數的參數對象,嚴格模式對它的使用作了限制。
a. 不容許對arguments賦值
"use strict"; arguments++; // 語法錯誤 var obj = { set p(arguments) { } }; // 語法錯誤 try { } catch (arguments) { } // 語法錯誤 function arguments() { } // 語法錯誤 var f = new Function("arguments", "'use strict'; return 17;"); // 語法錯誤
b. arguments再也不追蹤參數的變化
function f(a) { a = 2; return [a, arguments[0]]; } f(1); // 正常模式爲[2,2] function f(a) { "use strict"; a = 2; return [a, arguments[0]]; } f(1); // 嚴格模式爲[2,1]
c. 禁止使用arguments.callee
這意味着,沒法在匿名函數內部調用自身了。
"use strict"; var f = function() { return arguments.callee; }; f(); // 報錯
未來Javascript的新版本會引入"塊級做用域"。爲了與新版本接軌,嚴格模式只容許在全局做用域或函數做用域的頂層聲明函數。也就是說,不容許在非函數的代碼塊內聲明函數。
"use strict"; if (true) { function f1() { } // 語法錯誤 } for (var i = 0; i < 5; i++) { function f2() { } // 語法錯誤 }
爲了向未來Javascript的新版本過渡,嚴格模式新增了一些保留字:implements, interface, let, package, private, protected, public, static, yield。
使用這些詞做爲變量名將會報錯。
function package(protected) { // 出現保留字,語法錯誤 "use strict"; var implements; //出現保留字, 語法錯誤 }
此外,ECMAscript第五版自己還規定了另外一些保留字(class, enum, export, extends, import, super),以及各大瀏覽器自行增長的const保留字,也是不能做爲變量名的。
forEach
是Array新方法中最基本的一個,就是遍歷,循環。Array在ES5新增的方法中,參數都是function
類型,默認有傳參,forEach
方法中的function
回調支持3個參數,第1個是遍歷的數組內容;第2個是對應的數組索引,第3個是數組自己。
[].forEach(function(value, index, array) { // ... });
這裏的map
不是「地圖」的意思,而是指「映射」。[].map();
基本用法跟forEach
方法相似:array.map(callback,[ thisObject]);callback參數與forEach相似:
[].map(function(value, index, array) { // ... });
案例:數組項求平方
var data = [1, 2, 3, 4]; var squares = data.map(function (item) { return item * item; }); alert(squares); // 1, 4, 9, 16
filter
爲「過濾」、「篩選」的意思。指數組filter
後,返回過濾後的新數組。用法跟map
極爲類似:array.filter(callback,[ thisObject]);
filter
的callback
函數須要返回布爾值true
或false
.
some:指「某些」的意思,指是否「某些項」合乎條件。every:表示是否「每一項」都要靠譜.用法以下:
array.some(callback,[ thisObject]);
簡單演示:
var scores = [5, 8, 3, 10]; var current = 7; function higherThanCurrent(score) { return score > current; } if (scores.some(higherThanCurrent)) { alert("朕準了!"); }
array.every(callback,[ thisObject]);
簡單演示:
var scores = [5, 8, 3, 10]; var current = 7; if (scores.every(higherThanCurrent)) { console.log("朕準了!"); } else { console.log("來人,拖出去斬了!"); }
indexOf
方法在字符串中自古就有,string.indexOf(searchString, position)
。數組的indexOf
方法與之相似。用法:array.indexOf(searchElement[, fromIndex]); 返回整數索引值,若是沒有匹配(嚴格匹配),返回-1
. fromIndex
可選,表示從這個位置開始搜索,若缺省或格式不合要求,使用默認值0.
小案例:
var data = [2, 5, 7, 3, 5]; console.log(data.indexOf(5, "x")); // 1 ("x"被忽略) console.log(data.indexOf(5, "3")); // 4 (從3號位開始搜索) console.log(data.indexOf(4)); // -1 (未找到) console.log(data.indexOf("5")); // -1 (未找到,由於5 !== "5")
lastIndexOf
方法與indexOf
方法相似:array.lastIndexOf(searchElement[, fromIndex]);只是lastIndexOf
是從字符串的末尾開始查找,而不是從開頭。還有一個不一樣就是fromIndex
的默認值是array.length - 1
而不是0
.
var data = [2, 5, 7, 3, 5]; console.log(data.lastIndexOf(5)); // 4 console.log(data.lastIndexOf(5, 3)); // 1 (從後往前,索引值小於3的開始搜索) console.log(data.lastIndexOf(4)); // -1 (未找到)
迭代」、「遞歸(recursion)」的含義,用法:array.reduce(callback[, initialValue]);callback
函數接受4個參數:以前值、當前值、索引值以及數組自己。initialValue
參數可選,表示初始值。若指定,則看成最初使用的previous
值;若是缺省,則使用數組的第一個元素做爲previous
初始值,同時current
日後排一位,相比有initialValue
值少一次迭代。
小案例:
var arr = [10,11,12,13,14]; var sum = arr.reduce(function(prev, current, index, arr) { return prev + current; }, 0); console.log(sum); //60
實現上:
// 初始設置 previous = initialValue = 10, current = 11 // 第一次迭代 previous = (10 +11) = 21, current = 12 // 第二次迭代 previous = (21+ 12) = 33, current =13 // 第三次迭代 previous = (33 + 13) = 46, current = 14 //第四次迭代 previous = (46 + 14) = 60, current = undefined(退出)
用法與reduce類似,實現上差別在於reduceRight
是從數組的末尾開始實現。
小案例:
var data = [1, 2, 3, 4]; var special = data.reduceRight(function (previous, current, index) { if (index == 0) { return previous + current; } return previous - current; }); console.log(special); // 0
實現上:
// 初始設置 index = 3, previous = initialValue = 4, current = 3 // 第一次迭代 index = 2, previous = (4- 3) = 1, current = 2 // 第二次迭代 index = 1, previous = (1 - 2) = -1, current = 1 // 第三次迭代 index = 0, previous = (-1 + 1) = 0, current = undefined (退出)
建立對象,返回帶有屬性和方法的person對象
function createPerson(name, age,gender) { var person = new Person(); person.name=name; person.age=age; person.gender=gender person.sayName=function() { alert(this.name); }; return person; } createPerson("tom",20,"男").sayName();
建立對象,這種方式有個缺陷是sayName這個方法,它的每一個實例都是指向不一樣的函數實例,而不是同一個。
function Person(name,age,gender) { this.name=name; this.age=age; this.gender=gender; this.sayName=function() { console.log(this.name); }; } var person = new Person("rose",23,"女"); person.sayName();
建立對象,解決了上述中提到的缺陷,使不一樣的對象的函數(如sayFriends)指向了同一個函數。但它自己也有缺陷,就是實例共享了引用類型friends,從下面的代碼執行結果能夠看到,兩個實例的friends的值是同樣的。
function Person() { } Person.prototype = { constructor : Person, name:"Tom", age:20, gender:"男", friends:["Jack","Rose"], sayFriends:function() { console.log(this.friends); } }; var person1 = new Person(); person1.friends.push("Mary"); person1.sayFriends(); //Jack,Rose,Mary var person2 = new Person(); person2.sayFriends(); //Jack,Rose,Mary
使用原型模式和構造函數建立對象,解決了上述方法中提到的缺陷,並且這也是使用最普遍、認同度最高的建立對象的方法,強烈推薦使用。
function Person(name,age,gender) { this.name=name; this.age=age; this.gender=gender; this.friends=["Jack","Rose"]; } Person.prototype.sayFriends=function() { console.log("My name is"+this.name+","+"My friends are"+this.friends); }; var person1 = new Person("Tom",20,"男"); var person2 = new Person("Alice",18,"女"); person1.friends.push("Marck"); person1.sayFriends(); //My name is Tom ,My friends are Jack,Rose,Marck person2.sayFriends(); //My name is Alice,My friends are Jack,Rose
這個模式的好處在於看起來更像傳統的面向對象編程,具備更好的封裝性,由於在構造函數裏完成了對原型建立。這也是一個推薦的建立對象的方法。
function Person(name, age) { this.name = name; this.age = age; // method if(typeof this.sayName !== 'function'){ Person.prototype.sayName = function() { console.log(this.name); } } }
function Person(name, age) { var obj = new Object; obj.name = name; obj.age = age; obj.sayName = function() { console.log(this.name); }; return obj; }
function Person(name, age) { var obj = new Object; obj.sayName = function() { console.log(name); }; }
利用原型讓一個引用類型繼承另一個引用類型的屬性和方法
function A() { } A.prototype.location = "地球上。"; A.prototype.say = function() {}; A.prototype.talk = function() {}; A.prototype.run = function() {}; var a = new A; console.log(a.location); A.prototype = { say:function() {}, talk:function() {}, run:function() {} }; var na = new A; console.log(na);
var o1 = { name: 'tom' }; var o2 = { age: 18 }; var o3 = { gender: '男' }; console.log(o1); o1.extend = function() { var k, i = 0, l = arguments.length, args = arguments; // 遍歷在arguments上的每一對象 for (; i < l; i++) { // 枚舉當前對象上的全部屬性,添加到this上 for (k in args[i]) { this[k] = args[i][k]; } } }; o1.extend(o2, o3); console.log(o1);
在子類型構造函數的內部調用超類構造函數,經過使用call()和apply()方法能夠在新建立的對象上執行構造函數。
function SuperType() { this.colors = ["red","blue","green"]; } function SubType() { SuperType.call(this);//繼承了SuperType } var instance1 = new SubType(); instance1.colors.push("black"); console.log(instance1.colors);//"red","blue","green","black" var instance2 = new SubType(); console.log(instance2.colors);//"red","blue","green"
function parent(name, age) { this.name = name; this.age = age; } function child(name, age, gender, address) { // 將parent做爲child對象的一個方法來調用 this.parent = parent; this.parent(name, age); delete this.parent; this.gender = gender; this.address = address; } var ch = new child('tom', 18, 'boy', 'beijing'); console.log(ch);
將原型鏈和借用構造函數的技術組合在一塊,從而發揮二者之長的一種繼承模式。
建立一個僅用於封裝繼承過程的函數,該函數在內部以某種方式來加強對象,最後再像真正是它作了全部工做同樣返回對象。
經過借用函數來繼承屬性,經過原型鏈的混成形式來繼承方法