// 回調函數 function callback(data) {} // 邏輯函數 function func(callback) { // 函數回調,判斷回調函數是否存在 if (callback) callback(data); } func(callback); // 函數回調的本質:在一個函數中(調用函數),當知足必定條件,調用參數函數(回調函數) // 回調函數做爲調用函數的參數傳入,知足必定的條件,調用回調函數,回調函數能夠獲取調用函數中的局部變量 // 回調函數目的:經過參數將調用函數內部數據傳出,請求數據 => 數據(return | 函數回調) => 外界,匿名函數的自調用,沒有調用者,因此沒法獲取返回值,只能經過回調函數來實現
<!-- 外部要接收數據 --> <!-- 1.外部給內部提供回調函數(函數名callback) --> <!-- 2.內部將數據反饋給外部回調函數.外部就可使用內部數據 --> <script type="text/javascript"> var callback = function (data) { // 使用數據 console.log(data); } </script> <!-- 請求數據 --> <script type="text/javascript"> // 利用異常處理捕獲callback未定義的異常,不作處理 try { // 採用匿名函數自調用請求數據,達到頁面一加載就獲取到數據 (function (callback) { console.log("開始請求數據..."); // ... var data = [1, 2, 3, 4, 5]; console.log("數據請求完畢!!!"); // 若是回調函數存在,那麼回調對應的函數,並將數據攜帶出去 if (callback) { callback(data); } })(callback) // 請求數據完畢以後,須要讓外界獲取請求的數據: } catch (err) { } </script>
<script type="text/javascript"> // 鉤子:知足條件狀況下被系統回調的函數(方法),稱之爲鉤子函數(方法) <=> 回調函數 document.onclick = function (a, b , c) { console.log("點擊事件"); console.log(a, b , c); } </script>
function outer() { var data = {} function inner() { return data; } return inner; } // 使用閉包的緣由:不能使用函數回調(調用函數已有固定參數,或不能擁有參數),只能將函數定義到擁有局部變量函數的內部 // 閉包目的:不容許提高變量做用域時,該函數的局部變量須要被其餘函數使用 // 閉包本質:函數的嵌套,內層函數稱之爲閉包 // 閉包的解決案例:①影響局部變量的生命週期,持久化局部變量;②解決循環綁定致使的變量污染
局部變量的生命週期在函數運行結束時就結束,將局部變量傳到外部函數,實現了延長局部變量聲明週期的效果javascript
<script type="text/javascript"> function outer() { // eg: 請求獲得的數據,如何不持久化,方法執行完畢後,數據就會被銷燬 var data = [1, 2, 3, 4, 5]; console.log(data); // 經過閉包解決該類問題,因此閉包因此代碼都可以隨意自定義 function inner() { return data; } // 數據被inner操做返回,inner屬於outer,屬於須要outer將inner返回出去(跟外界創建起聯繫) return inner; } // 將局部變量生命週期提高於inner函數相同,inner存在,局部變量data就一直存在 var inner = outer(); console.log(inner()); </script>
變量污染:前幾回變量的定義,被最後一次定義覆蓋。html
// 在循環綁定中,onclick函數中的變量i指向的內存地址,通過循環以後i變成了5,因此每一個元素事件運行的時候變量i都是5 var list = document.getElementById("ulDemo").getElementsByTagName("li"); for (var i = 0; i < list.length; i++) { var li = list[i]; li.onclick= function () { alert(i); } }
利用閉包解決:java
var lis = document.querySelectorAll('ul li'); // 2.循環綁定 for (var i = 0; i < lis.length; i++) { // 解決的原理:一共產生了5個外層函數,存儲的形參i的值分別是0, 1, 2, 3, 4 // 內層函數也產生了5個,且和外層函數一一對應,打印的i就是外層函數的形參i // 外層函數 (function (i) { // 內層函數:閉包 lis[i].onclick = function () { alert(i) } })(i) } console.log(i);
.html文件 <ul> <li>列表項</li> <li>列表項</li> <li>列表項</li> </ul>
.js文件 var lis = document.querySelector('li'); for (var i = 0; i < lis.length; i++) { lis[i].onclick = function () { // 打印列表項的索引 console.log(i); } } // 循環綁定會致使變量污染,解決方法以下: // 1.獲取局部(塊級)做用域解決:在ES5中沒有塊級做用域,而在ES6中有塊級做用域 // 2.閉包解決,如:1、2.2 利用閉包解決循環綁定所帶來的變量污染 // 3.對象屬性解決
// 在ES5中沒有塊級做用域,在ES6中有塊級做用域,因此只要將 var 改成 let 便可解決變量污染 let lis = document.querySelectorAll('li'); for (let i = 0; i < lis.length; i++) { lis[i].onclick = function () { alert(i) } }
<script type="text/javascript"> var lis = document.querySelectorAll('li'); for (var i = 0; i < lis.length; i++) { // lis[i]依次表明五個li頁面元素對象 // 給每個li設置一個(臨時)屬性 lis[i].index = i; // 第一個li的index屬性存儲的就是索引0,以此類推,最後一個存儲的是索引4 lis[i].onclick = function () { // var temp = lis[i].index; // lis[i]中的i同樣存在變量污染 var temp = this.index; // 當前被點擊的li alert(temp) // temp => this.index(lis[i].index) => i(索引) } } </script>
// 建立對象 var obj = {}; | var obj = new Object(); // 前者通常是建立一個對象是用,後者在須要建立多個對象是用 // 屬性 obj.屬性名 = ""; // 方法 obj.func = function () {} // 刪除屬性與方法 delete obj.prop delete obj.func
JS中沒有字典(鍵值對存儲數據的方式),但能夠經過對象實現字典方式存儲數據,並使用數據es6
var dict = {name: "zero", age: 18}
// key在JS標識符語法支持狀況下,能夠省略引號,但key爲js標識符不支持的語法狀況下,必須添加引號 var dict = {"my-name": "zero", fn: function () {}, fun () {}}
dict.name | dict["my-name"] | dict.fn()
// 增 dict.key4 = true; console.log(dict); // 刪 delete dict.key4; console.log(dict); // 改 dict["my-key3"] = [5, 4, 3, 2, 1]; console.log(dict); // 查 console.log(dict.key1); console.log(dict["key1"]);
function People(name, age) { this.name = name; this.age = age; this.eat = function () { return 'eat'; } }
// 父級 function Sup(name) { this.name = name; this.fn = function () { console.log('fn class'); } } // 原型 console.log(Sup.prototype); console.log(sup.__proto__); // 子級 function Sub(name) { // 繼承屬性,在繼承函數中由被繼承(父級)函數調用,傳入繼承函數的this與被繼承函數建立屬性對屬性進行賦值的全部須要的數據(name) Sup.call(this, name); } // 繼承方法,利用prototype原型,將new Sup賦值給Sub.prototype Sub.prototype = new Sup; // 建立子級對象 var sub = new Sub("subClass"); // 使用屬性 console.log(sub.name); // 使用方法 sub.fn(); // 指向自身構造函數 Sub.prototype.constructor = Sub;
// 案例 // 1.完成繼承,必須擁有兩個構造函數 // 2.一個函數要使用另一個函數的屬性與方法,須要對其進行繼承(屬性與方法的複用) // 3.屬性的繼承call方法,在繼承函數中由被繼承函數調用,傳入繼承函數的this與被繼承函數建立屬性對屬性進行賦值的全部須要的數據,eg:如Sup有name屬性,那麼能夠經過call將Sub的name傳給Sup // 4.方法的繼承prototype原型,將new Sup賦值給Sub.prototype function Sup(name) { // 屬性存放某個值 this.name = name; // 方法完成某項功能 this.func = function () { console.log("func"); } } var sup = new Sup("supClass"); console.log(sup.name); sup.func(); // 相似於子級 function Sub(name) { // 屬性的繼承 Sup.call(this, name); } // 方法的繼承 Sub.prototype = new Sup; var sub = new Sub("subClass"); console.log(sub.name); sub.func();
// 父類 class People { // 構造器 constructor (name, age) { this.name = name; this.age = age; } // 實例方法 eat () { console.log('吃吃吃'); } // 類方法 static create () { console.log('誕生'); } } // 子類 class Student extends People { constructor (name, age) { // super關鍵詞 super(name, age) } }
應用場景:完成JS自啓動畫;完成CSS沒法完成的動畫閉包
// 一次性定時器 /* 異步執行 setTimeout(函數, 毫秒數, 函數所需參數(能夠省略)); */ console.log("開始"); setTimeout(function (data) { console.log(data); }, 1000, "數據"); console.log("結束"); // 持續性定時器 /*異步執行 setInterval(函數, 毫秒數, 函數所需參數(能夠省略)); */ console.log("又開始"); var timer = setInterval(function() { // timer變量,是定時器編號,是普通的數字類型 console.log("呵呵"); }, 1000) console.log(timer); console.log("又結束");
// 清除定時器 // 1.持續性與一次性定時器一般都應該有清除定時器操做 // 2.清除定時器操做能夠混用 clearTimeout() | clearInterval() // 3.定時器的返回值就是定時器編號,就是普普統統的數字類型 document.onclick = function () { console.log("定時器清除了"); clearInterval(timer); // clearTimeout(timer); } // 清除全部定時器 // 若是上方建立過n個定時器,那麼timeout值爲n+1 var timeout = setTimeout(function(){}, 1); for (var i = 0; i < timeout; i++) { // 循環將編號[0, n]全部定時器都清除一次 clearTimeout(i) } // 1.容許重複清除 // 2.容許清除不存在的定時器編號