JS開發中函式知識點梳理(二)

源自世界發表於2018-05-03

作為一個Jser,不光要會用js,還要明白它的執行原理,不然就會一直停留在表面。

函式在JavaScript中被稱作第一等公民,這個第一等公民是什麼鬼?看看知乎上是怎麼回答的。就像我的引路人剛開始跟我說的要想學好一門語言,就要先掌握好一門外語(英語)一樣,因為這些計算機程式語言或直譯器語言基本都是源於老外開發,所以要想學到原汁原味的東西,檢視英文文件是必不可少的。

英文原文中本來是 first-class object ,而翻譯成 第一類公民 其實就是一種比喻。從這裡可以知道兩點:

  • 函式本質上也是物件,
  • 可以用函式實現其它的任何物件

函式的用法

  1. 可以動態的建立函式 (new Function())

這種方式不常用,也不推薦。具體原因是(來自於JavaScript高階程式設計):這種語法會導致解析兩次程式碼,從而影響效能。

  1. 可以將函式賦值給變數(函式表示式)

  2. 可以將函式最為一個引數傳遞給另一個函式(回撥函式)

  3. 函式可以包含自己的屬性和方法(建構函式)

  4. 將一個函式作為另一個函式的返回值

物件陣列的排序,程式碼如下:

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既然是個物件,那麼它就可以擁有自己的屬性。這個我們可以在瀏覽器控制檯輸入 函式名. 後,瀏覽器就可以自動提示函式的屬性。而我們常用的式函式的內部屬性,我們常見的就是 argumentsthis。前者是一個包含函式傳入的引數偽陣列,後者指向函式物件本身。同時我們也注意到了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 ,而翻譯成 第一類公民 其實就是一種比喻。從這裡可以知道兩點:

  • 函式本質上也是物件,
  • 可以用函式實現其它的任何物件

函式的用法

  1. 可以動態的建立函式 (new Function())

這種方式不常用,也不推薦。具體原因是(來自於JavaScript高階程式設計):這種語法會導致解析兩次程式碼,從而影響效能。

  1. 可以將函式賦值給變數(函式表示式)

  2. 可以將函式最為一個引數傳遞給另一個函式(回撥函式)

  3. 函式可以包含自己的屬性和方法(建構函式)

  4. 將一個函式作為另一個函式的返回值

物件陣列的排序,程式碼如下:

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既然是個物件,那麼它就可以擁有自己的屬性。這個我們可以在瀏覽器控制檯輸入 函式名. 後,瀏覽器就可以自動提示函式的屬性。而我們常用的式函式的內部屬性,我們常見的就是 argumentsthis。前者是一個包含函式傳入的引數偽陣列,後者指向函式物件本身。同時我們也注意到了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。

相關文章