單元測試框架的核心是斷言方法,一般叫assert()。
該方法一般接收一個值--須要斷言的值,以及一個表示該斷言目的的描述。
若是該值執行的結果爲true,斷言就會經過;
不然,斷言就會被認爲是失敗的。
一般用一個相應的經過(pass)/ 失敗(fail)標記記錄相關的信息;javascript
function assert(value, desc) { let li = document.createElement('li'); li.className = value ? 'pass' : 'fail'; li.appendChild(document.createTextNode(desc)); document.getElementById('results').appendChild(li); } // 斷言函數 function assert(value, desc) { if (value) { console.log(`\033[32m ${desc} \033[0m`); // 斷言經過 綠色字體 } else { console.log(`\033[31m ${desc} \033[0m`); // 斷言失敗 紅色字體 } }
JavaScript是一門函數式語言
java
在JavaScript中,函數是第一型對象。函數能夠共處,能夠視做爲其餘任意類型的對象。就像普通的JavaScript數據類型,,函數能夠被任意變量進行引用,或聲明成對象字面量,甚至能夠將其做爲函數參數進行傳遞。
跨域
函數是第一型對象
命名一個函數時,該名稱在整個函數聲明範圍內是有效的。若是函數聲明在頂層,window對象上的同名屬性則會引用到該函數。
數組
全部的函數都有一個name屬性,該屬性保存的是該函數名稱的字符串。匿名函數的name屬性值爲空。
瀏覽器
在JavaScript中,做用域是由function進行聲明的,而不是代碼塊。聲明的做用域建立於代碼塊,但不是終結於代碼塊(其餘語言是終結於代碼塊的)
閉包
if (window) { var x = 123; } alert(x); 執行代碼後,會彈出123,是由於JavaScript在大括號關閉處並無終止其做用域。
變量聲明的做用域開始於聲明的地方,結束於函數的結尾,與代碼嵌套無關。
app
命名函數的做用域是指聲明該函數的整個函數範圍,與代碼嵌套無關;
框架
對於做用域聲明,全局上下文就像一個包含頁面全部代碼的超大型函數。
函數
全部的函數調用都會傳遞兩個隱式參數:argument和this
單元測試
若是一個數不是做爲方法、構造器、或者經過apply()或call()進行調用的,則認爲它是「做爲函數」進行調用的。
function ninja() {}; ninja() var samurai = function() {}; samurai()
以這種方式調用時,函數的上下文是全局上下文---window對象。
當一個函數被賦值給對象的一個屬性,並使用引用該函數的這個屬性進行調用時,那麼函數就是做爲該對象的一個方法進行調用的。
var 0 = {}; o.whatever = function() {}; o.whatever();
將函數做爲對象的一個方法進行調用時,該對象就變成了函數上下文,而且在函數內部能夠以this參數的形式進行訪問。
將函數做爲構造器進行調用,須要在函數調用前使用new關鍵字
function Ninja() { this.skulk = function() { return this; } } var ninja1 = new Ninja(); var ninja2 = new Ninja();
構造器的目的是經過函數調用初始化建立新的對象。
函數調用方式之間點主要差別是:做爲this參數傳遞給執行函數的上下文對象之間點區別。
經過函數的apply()方法來調用函數,須要給apply()傳入兩個參數:一個是函數上下文的對象,另外一個是做爲函數參數所組成的數組;
經過函數的call()方法來調用函數,須要給call()傳入兩個參數:一個是函數上下文的對象,另外一個是做爲函數參數的參數列表,而不是單個數組;
function juggle() { var result = 0; for (var n = 0; n < arguments.length; n++) { result += arguments[n] } this.result = result; } var ninja1 = {}; var ninja2 = {}; juggle.apply(ninja1, [1,2,3,4]); juggle.call(ninja2, 5,6,7,8) assert(ninja1.result === 10, 'juggled via apply'); assert(ninja2.result === 26, 'juggled via call');
使用apply()和call()能夠選擇任意對象做爲函數上下文;
爲了避免讓沒必要要的函數名稱污染全局命名空間,能夠建立大量的小型函數進行傳遞,而不是建立包含大量命令語句的大型函數。
遞歸:當函數調用自身,或調用另一個函數,但這個函數的調用樹中的某個地方又調用到了本身時,就產生了遞歸。
遞歸的兩個條件:引用自身,而且有終止條件。
閉包是一個函數在建立時容許自身函數訪問並操做該自身函數以外的變量時所建立的做用域
閉包可讓函數訪問全部的變量和函數,只要這些變量和函數存在於該函數聲明時的做用域內就行。
var outerValue = 'ninja'; var later; function outerFunction() { // 該變量的做用域限制在該函數內部,而且在函數外部訪問不到; var innerValue = 'samurai'; // 在外部函數內,聲明一個內部函數。 // 注意:聲明該函數時,innerValue是在做用域內的 function innerFunction() { assert(outerValue, 'I can see the ninja'); assert(innerValue, 'I can see the samurai'); // 將內部函數引用到later變量上,因爲later在全局做用域內,因此能夠對它進行調用。 later = innerFunction; } } // 調用外部函數,將會聲明內部函數,並將內部函數賦值給later變量。 outerFunction(); // 經過later調用內部函數。 // 咱們不能直接調用內部函數,由於它的做用域(和innerValue一塊兒)被限制在outerFunction內。 later();
在構造器內隱藏變量,使其在外部做用域不可訪問,可是能夠存在於閉包內。
function Ninja() { var feints = 0; this.getFenits = function() { return feints; } this.feint = function() { feints++; } } var ninja = new Ninja(); ninja.feint(); assert(ninja.getFenits() === 1, '調用一次,內部變量++'); assert(ninja.feints === undefined, '函數外部不可訪問')
變量的做用域依賴於變量所在的閉包
閉包記住的是變量的引用,而不是閉包建立時刻該變量的值
全部的函數在初始化時都有一個prototype屬性,該屬性的初始值是一個空對象。
使用new操做符將函數做爲構造器進行調用的時候,其上下文被定義爲新對象的實例。
在構造器內的綁定操做優先級永遠高於在原型上的綁定操做優先級。由於構造器的this上下文指向的是實例自身,因此咱們能夠在構造器內對核心內容執行初始化操做。
經過instanceof操做,能夠判斷函數是否繼承了其原型鏈中任何對象的功能。