作為一個Jser,不光要會用js,還要明白它的執行原理,不然就會一直停留在表面。
函式在JavaScript中被稱作第一等公民
,這個第一等公民是什麼鬼?看看知乎上是怎麼回答的。就像我的引路人剛開始跟我說的要想學好一門語言,就要先掌握好一門外語(英語)一樣,因為這些計算機程式語言或直譯器語言基本都是源於老外開發,所以要想學到原汁原味的東西,檢視英文文件是必不可少的。
英文原文中本來是 first-class object
,而翻譯成 第一類公民
其實就是一種比喻。從這裡可以知道兩點:
- 函式本質上也是物件,
- 可以用函式實現其它的任何物件
函式的用法
- 可以動態的建立函式 (new Function())
這種方式不常用,也不推薦。具體原因是(來自於JavaScript高階程式設計):這種語法會導致解析兩次程式碼,從而影響效能。
-
可以將函式賦值給變數(函式表示式)
-
可以將函式最為一個引數傳遞給另一個函式(回撥函式)
-
函式可以包含自己的屬性和方法(建構函式)
-
將一個函式作為另一個函式的返回值
物件陣列的排序,程式碼如下:
function compare(prop) {
return fucntion(obj1, obj2) {
var v1 = obj1[prop],
v2 = obj2[prop]
return v1 > v2 ? 1 : v1 < v2 ? -1 : 0
}
}
var arr = [{
name: 'li',
age: 18
}, {
name: 'an',
age: 19
}, {
name: 'tian',
age: 18
}]
arr.sort(compare('name'))
複製程式碼
函式與物件之間的關係
通過上面的描述,不管之前知道不知道,但是現在應該知道,我們可以通過函式來建立物件。
程式碼說明:
function Robot(name) {
this.name = name
}
var robert = new Robot('robert')
robert.__proto__.constructor // ƒ Robot(name) {this.name = name}
roboert.__proto__.constructor.__proto__.constructor // ƒ Function() { [native code] }
robert.__proto__.__proto__.constructor // ƒ Object() { [native code] }
robert.__proto__.__proto__.constructor.__proto__.constructor // ƒ Function() { [native code] }
robert.__proto__.__proto__.__proto__ // null
複製程式碼
通過原型鏈,我們可以知道我們的例項物件源於誰。如上面的例子,我們建立了建構函式 Robot,用它例項化了一個robert物件,所以robert物件源自於建構函式Robot,而建構函式Robot的原型通過列印值,我們知道它源自於物件Function;接著看,通過原型鏈繼承我們可以知道,Robot繼承自物件Object,而Object的建構函式則源自於Function;而順著原型鏈我們查詢Object的原型的物件,會得到一個空值。所以,通過上述的結果,我們發現在js中不管我們是用建構函式建立的物件還是用js本身提供的資料型別建立的物件都源自於Function。
在js中建立物件的基本方式大致分為四類:
- 建構函式 (如:一般建構函式,寄生建構函式,穩妥建構函式等)
- 包裝器(如:new Number()/new Object()等等)
- 物件字面量 (如:var obj = {name: 'robert', age: 18})
- 原型
然後,就是根據需要使用上面的基本方式的隨機組合。
Function物件的屬性
Function既然是個物件,那麼它就可以擁有自己的屬性。這個我們可以在瀏覽器控制檯輸入 函式名.
後,瀏覽器就可以自動提示函式的屬性。而我們常用的式函式的內部屬性,我們常見的就是 arguments
和 this
。前者是一個包含函式傳入的引數偽陣列,後者指向函式物件本身。同時我們也注意到了arguments
物件包含一個屬性 callee
,它是一個指標,指向包含 arguments
屬性的函式。它和 this
的區別就是arguments.callee()可以代表函式本身,而 this
就是函式執行環境的物件。
使用arguments.callee()可以解除函式體內程式碼和函式名的耦合狀態。
參考程式碼如下:
function fic(n) {
return n <=1 ? 1 : n * arguments.callee(n-1)
}
function fic2(n) {
return n <= 1 ? 1 : n * fic(n-1)
}
function fic3() {
return 0
}
fic(5) // 120
fic2(5) // 120
var fic4 = fic
var fic5 = fic2
fic = fic3
fic2 = fic3
fic(5) // 0
fic2(5) // 0
fic4(5) // 120
fic5(5) // 0
複製程式碼
通過函式fic4和fic5的比較我們可以的出上面的結論。
- name: 函式的名字
- length: 函式傳入引數的個數
Function物件的方法
我們常見的是 apply/call
, apply方法接收兩個引數,第一個都是在其中執行的函式作用域,第二個引數為一個引數陣列。
在ES5中還有一個方法bind也可以改變函式執行時內部的作用域,它有一個引數,該引數就是函式內部this要繫結的物件。
後續可能還會繼續修改,也歡迎各位批評指正。有問題或者有其他想法的可以在我的GitHub上pr。 作為一個Jser,不光要會用js,還要明白它的執行原理,不然就會一直停留在表面。
函式在JavaScript中被稱作第一等公民
,這個第一等公民是什麼鬼?看看知乎上是怎麼回答的。就像我的引路人剛開始跟我說的要想學好一門語言,就要先掌握好一門外語(英語)一樣,因為這些計算機程式語言或直譯器語言基本都是源於老外開發,所以要想學到原汁原味的東西,檢視英文文件是必不可少的。
英文原文中本來是 first-class object
,而翻譯成 第一類公民
其實就是一種比喻。從這裡可以知道兩點:
- 函式本質上也是物件,
- 可以用函式實現其它的任何物件
函式的用法
- 可以動態的建立函式 (new Function())
這種方式不常用,也不推薦。具體原因是(來自於JavaScript高階程式設計):這種語法會導致解析兩次程式碼,從而影響效能。
-
可以將函式賦值給變數(函式表示式)
-
可以將函式最為一個引數傳遞給另一個函式(回撥函式)
-
函式可以包含自己的屬性和方法(建構函式)
-
將一個函式作為另一個函式的返回值
物件陣列的排序,程式碼如下:
function compare(prop) {
return fucntion(obj1, obj2) {
var v1 = obj1[prop],
v2 = obj2[prop]
return v1 > v2 ? 1 : v1 < v2 ? -1 : 0
}
}
var arr = [{
name: 'li',
age: 18
}, {
name: 'an',
age: 19
}, {
name: 'tian',
age: 18
}]
arr.sort(compare('name'))
複製程式碼
函式與物件之間的關係
通過上面的描述,不管之前知道不知道,但是現在應該知道,我們可以通過函式來建立物件。
程式碼說明:
function Robot(name) {
this.name = name
}
var robert = new Robot('robert')
robert.__proto__.constructor // ƒ Robot(name) {this.name = name}
roboert.__proto__.constructor.__proto__.constructor // ƒ Function() { [native code] }
robert.__proto__.__proto__.constructor // ƒ Object() { [native code] }
robert.__proto__.__proto__.constructor.__proto__.constructor // ƒ Function() { [native code] }
robert.__proto__.__proto__.__proto__ // null
複製程式碼
通過原型鏈,我們可以知道我們的例項物件源於誰。如上面的例子,我們建立了建構函式 Robot,用它例項化了一個robert物件,所以robert物件源自於建構函式Robot,而建構函式Robot的原型通過列印值,我們知道它源自於物件Function;接著看,通過原型鏈繼承我們可以知道,Robot繼承自物件Object,而Object的建構函式則源自於Function;而順著原型鏈我們查詢Object的原型的物件,會得到一個空值。所以,通過上述的結果,我們發現在js中不管我們是用建構函式建立的物件還是用js本身提供的資料型別建立的物件都源自於Function。
在js中建立物件的基本方式大致分為四類:
- 建構函式 (如:一般建構函式,寄生建構函式,穩妥建構函式等)
- 包裝器(如:new Number()/new Object()等等)
- 物件字面量 (如:var obj = {name: 'robert', age: 18})
- 原型
然後,就是根據需要使用上面的基本方式的隨機組合。
Function物件的屬性
Function既然是個物件,那麼它就可以擁有自己的屬性。這個我們可以在瀏覽器控制檯輸入 函式名.
後,瀏覽器就可以自動提示函式的屬性。而我們常用的式函式的內部屬性,我們常見的就是 arguments
和 this
。前者是一個包含函式傳入的引數偽陣列,後者指向函式物件本身。同時我們也注意到了arguments
物件包含一個屬性 callee
,它是一個指標,指向包含 arguments
屬性的函式。它和 this
的區別就是arguments.callee()可以代表函式本身,而 this
就是函式執行環境的物件。
使用arguments.callee()可以解除函式體內程式碼和函式名的耦合狀態。
參考程式碼如下:
function fic(n) {
return n <=1 ? 1 : n * arguments.callee(n-1)
}
function fic2(n) {
return n <= 1 ? 1 : n * fic(n-1)
}
function fic3() {
return 0
}
fic(5) // 120
fic2(5) // 120
var fic4 = fic
var fic5 = fic2
fic = fic3
fic2 = fic3
fic(5) // 0
fic2(5) // 0
fic4(5) // 120
fic5(5) // 0
複製程式碼
通過函式fic4和fic5的比較我們可以的出上面的結論。
- name: 函式的名字
- length: 函式傳入引數的個數
Function物件的方法
我們常見的是 apply/call
, apply方法接收兩個引數,第一個都是在其中執行的函式作用域,第二個引數為一個引數陣列。
在ES5中還有一個方法bind也可以改變函式執行時內部的作用域,它有一個引數,該引數就是函式內部this要繫結的物件。
後續可能還會繼續修改,也歡迎各位批評指正。有問題或者有其他想法的可以在我的GitHub上pr。