JavaScript函數、閉包、原型、面向對象

JavaScript函數、閉包、原型、面向對象

斷言

單元測試框架的核心是斷言方法,一般叫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關鍵字
    • 建立一個新的空對象;
    • 傳遞給構造器都對象是this參數,從而成爲構造器的函數上下文;
    • 若是沒有顯式都返回值,新建立的對象則做爲構造器的返回值進行返回。
    function Ninja() {
        this.skulk = function() { return this; }
    }
    
    var ninja1 = new Ninja();
    var ninja2 = new Ninja();
    • 構造器的目的是經過函數調用初始化建立新的對象。

函數調用方式差別

  • 函數調用方式之間點主要差別是:做爲this參數傳遞給執行函數的上下文對象之間點區別。
    • 做爲方法調用,該上下文是方法的擁有者;
    • 做爲全局函數進行調用,其上下文永遠是window(也就說,該函數是window的一個方法)。
    • 做爲構造器進行調用,其上下文對象則是新建立的對象實例。

使用apply()和call()方法

  • 經過函數的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()能夠選擇任意對象做爲函數上下文;

函數總結

  • 函數是第一型對象;
    • 經過字面量進行建立。
    • 賦值給變量或屬性。
    • 做爲參數進行傳遞。
    • 做爲函數結果進行返回。
    • 擁有屬性和方法。
  • 函數是經過字面量進行建立的,其名稱是可選的。
  • 在頁面生命週期內,瀏覽器能夠將函數做爲各類類型的事件處理程序進行調用。
  • 變量的做用域開始於聲明處,結束於函數尾部,其會跨域邊界(如:大括號)
  • 內部函數在當前函數的任何地方均可用(提高),即使是提早引用。
  • 函數的形參列表和實際參數列表的長度能夠是不一樣的。
    • 未賦值的參數被設置爲undefined。
    • 多出的參數是不會綁定到參數名稱的。
  • 每一個函數調用都會傳入兩個隱式參數。
    • arguments,實際傳入的參數集合。
    • this,做爲函數上下文的對象引用。
  • 能夠用不一樣的方法進行函數調用,不一樣的調用機制決定了函數上下文的不一樣。
    • 做爲普通函數進行調用時,其上下文是全局對象(window)。
    • 做爲方法進行調用時,其上下文是擁有該方法的對象。
    • 做爲構造器進行調用時,其上下文是新分配的對象實例。
    • 經過函數的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操做,能夠判斷函數是否繼承了其原型鏈中任何對象的功能。

相關文章
相關標籤/搜索