1. 程式人生 > >javascript物件、函式、建構函式、原型、作用域、閉包、this概論

javascript物件、函式、建構函式、原型、作用域、閉包、this概論

1、物件

一切引用型別都是物件,函式function其實也是物件。物件多用字面量表示法建立。所有物件都是鍵值對的集合,這個值當然也可以是物件/函式,可以有很多層級,這個跟json有點像,在現代化js裡,json和js物件可以無縫轉換。

2、函式

函式是一種特殊的物件,函式名只是函式的一個指標,可以被當做變數傳遞,給程式設計帶來極大的靈活性,比如回撥。

3、建構函式

建構函式也是函式,約定首字母大寫,但是不是必須的。

建構函式用於構造一個新的例項,但是如果函式中有返回例項的程式碼,那麼直接返回該例項,否則返回建立的新例項。

建立物件的過程:首先建立一個空的obj,然後他的原型指向建構函式的原型,然後用新物件去call建構函式(改變this指向,指向obj),只要構造建構函式包含this.屬性=值的程式碼,新的物件就會被新增屬性;如果建構函式中沒有this相關程式碼,返回的是一個空的obj,這就是普通function也能new的原因,但是意義不大。

4、原型

ES6之前JS都是沒有繼承的,原型也是一個例項,預設原型是Object物件的一個例項,擁有一些通用方法,比如toString。所有的引用型別建立時都會被指定一個預設原型,可以通過程式碼修改這個原型,指定為另一個具體的例項,依次指下去就可以形成一個鏈,就是原型鏈。所有同一類物件的例項的原型鏈是一樣的,只有本示例的屬性不同,所以原型實現了“程式碼的共享”,也模擬了繼承。

但是ES6開始,JS有了class,有了extends,原型的寫法沒有必要了。個人認為原型是JS前期的設計不良,ES5、ES6一直在修正和優化JS。

5、作用域

作用域就是一個函式能訪問的變數的範圍,從自身函式程式碼覆蓋的範圍一直往上,最終都會到全域性作用域(window),形成的鏈也叫作用域鏈。作用域其實跟很多高階語言原理一樣,比較好理解。

6、閉包

閉包程式碼表現上往往是一個函式內包含另一個函式,子函式中有引用外層函式的區域性變數,而這個子函式又被外界呼叫或者引用,就會“導致”區域性變數不能被釋放。

正常情況下,一個函式被呼叫,執行結束完區域性變數就會被釋放,但是閉包提供了一種儲存“區域性變數”的手段。

let:let關鍵字宣告變數,同時會在最近的塊中開闢塊級作用域,並將該變數和該作用域繫結。而如果這個變數被一個函式引用(形參),只要這個函式不消失,這個變數和當前作用域就不會消失,從而形成一個閉包。

這就是在for迴圈中用let接收引數,然後給頁面上的元件繫結方法時,形參不會出錯亂的原因

7、this

this的指向在函式(非箭頭函式)定義的時候是確定不了的,只有函式執行的時候才能確定this到底指向誰,實際上this的最終指向的是那個呼叫它的物件。它有如下三種情況:

情況1:如果一個函式中有this,但是它沒有被上一級的物件所呼叫,那麼this指向的就是window,這裡需要說明的是在js的嚴格版中this指向的不是window,但是我們這裡不探討嚴格版的問題,你想了解可以自行上網查詢。

情況2:如果一個函式中有this,這個函式有被上一級的物件所呼叫,那麼this指向的就是上一級的物件。

情況3:如果一個函式中有this,這個函式中包含多個物件,儘管這個函式是被最外層的物件所呼叫,this指向的也只是它上一級的物件。

以上參考這裡

ES6箭頭函式

箭頭函式表示式的語法比函式表示式更短,寫法為:(引數1,引數2)=>{函式體},否則就得這麼寫function(引數1,引數2){函式體}

並且沒有自己的this,arguments,super或new.target。這些函式表示式更適用於那些本來需要匿名函式的地方,並且它們不能用作建構函式。

箭頭函式表示式對非方法函式是最合適的,不推薦作為obj的屬性使用。

那麼在箭頭函式中寫this.會指向誰?mozilla的解釋是:箭頭函式不會建立自己的this,它只會從自己的作用域鏈的上一層繼承this

以下兩個例子可以體會下,其中第一種是不推薦用的。

示例①:以下程式碼會輸出33,11而不是33,22(obj物件處於全域性作用域,因此say2從obj的作用域穿透到全域性,列印11)

    var x = 11;
    var obj = {
      x: 22,
      methods: {
        x: 33,
        say: function () { console.log(this.x) },
        say2: () => { console.log(this.x) }
      }
    }

    obj.methods.say();
    obj.methods.say2();

示例②:以下程式碼會輸出1,而不是100 

    var age = 99;

    function PersonX() {
      this.age = 0;
      setTimeout(() => {
        this.age++;
        console.log(age)
      }, 1000);
    }

    PersonX();