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();