歡迎關注前端小謳的github,閱讀更多原創技術文章
Function
類型的實例,都與其餘引用類型同樣具備屬性和方法 相關代碼 →javascript
// 1.函數聲明定義 function sum(num1, num2) { return num1 + num2 } // 2.函數表達式定義 var sum = function (num1, num2) { return num1 + num2 } // 3.Function構造函數定義 /* 不推薦使用構造函數定義,由於會形成解析2次代碼(1次解析常規js代碼,1次解析傳入構造函數中的字符串) */ var sum = new Function('num1', 'num2', 'return num1+num2')
function sum(num1, num2) { return num1 + num2 } console.log(sum(10, 10)) var anotherSum = sum // 使用不帶圓括號的函數名是訪問函數指針,而非調用函數 console.log(anotherSum(10, 10)) /* sum 和 anotherSum 同時指向同一個函數 */ sum = null // sum與函數斷絕關係 console.log(anotherSum(10, 10)) // 但anotherSum()仍可正常調用 console.log(sum(10, 10)) // 會報錯,sum is not a function
function addSomeNumber(num) { return num + 100 } // 建立第二個函數 function addSomeNumber(num) { return num + 200 } // 第二個函數等價於下列代碼 -> 覆蓋了引用第一個函數的變量addSomeNumber addSomeNumber = function (num) { return num + 200 } var result = addSomeNumber(100) console.log(result) // 300
js 引擎在代碼開始執行以前,解析器經過函數聲明提高(function declaration hoisting)的過程,將聲明函數放到源代碼樹的頂部,使其在執行任何代碼以前可用(能夠訪問);而函數表達式則必須等到解析器執行到所在代碼行才被解釋執行。前端
函數聲明和函數表達式的惟一區別就是何時能夠經過變量訪問函數java
console.log(sumDeclare(10, 10)) // 函數聲明會提早 function sumDeclare(num1, num2) { return num1 + num2 } console.log(sumExpression(10, 10)) // 函數表達式不會提早,會報錯,sumExpression is not a function var sumExpression = function (num1, num2) { return num1 + num2 }
像傳遞參數同樣,把一個函數傳遞給另外一個函數,也能夠將一個函數做爲另外一個函數的結果返回node
function callSomeFunction(someFunction, someArgument) { return someFunction(someArgument) } function add10(num) { return num + 10 } var result1 = callSomeFunction(add10, 10) // 訪問函數的指針而不是執行函數 console.log(result1) // 20 function getGreeting(name) { return 'Hello,' + name } var result2 = callSomeFunction(getGreeting, 'Nicholas') // 訪問函數的指針而不是執行函數 console.log(result2) // Hello,Nicholas
【例】想要根據數組對象的某個對象屬性進行排序:git
/** * 按照對象數組的某個object key,進行數組排序 * @param {String} key 要排序的key * @param {String} sort 正序/倒序:asc/desc,默認爲asc */ function arraySort(key, sort) { return function (a, b) { if (sort === 'asc' || sort === undefined || sort === '') { // 正序:a[key] > b[key] if (a[key] > b[key]) return 1 else if (a[key] < b[key]) return -1 else return 0 } else if (sort === 'desc') { // 倒序:a[key] < b[key] if (a[key] < b[key]) return 1 else if (a[key] > b[key]) return -1 else return 0 } } } var userList = [ { name: 'Tony', id: 3 }, { name: 'Tom', id: 2 }, { name: 'Jack', id: 5 }, ] console.log(userList.sort(arraySort('id'))) // 按 id 正序排列 console.log(userList.sort(arraySort('id', 'desc'))) // 按 id 倒序排列
函數內部有 2 個特殊對象 arguments
和 this
,1 個內部屬性 caller
github
arguments
是一個類數組對象,保存着函數的全部參數。對象有 callee
屬性,該屬性是一個指針,指向擁有這個 arguments 對象的函數數組
// 遞歸函數:計算階乘 function factorial(num) { if (num <= 1) return 1 else return num * factorial(num - 1) } // 內部再也不引用函數名,不管函數名是什麼,均可以保證完成遞歸調用 function factorial(num) { if (num <= 1) return 1 else return num * arguments.callee(num - 1) // callee指向擁有這個arguments對象的函數 } var trueFactorial = factorial // 保存函數的指針 factorial = function () { return 0 } console.log(trueFactorial(5)) // 120,已用arguments.callee解除函數體內代碼與函數名的耦合,仍能正常計算 console.log(factorial(5)) // 0
this
引用的是函數執行的環境對象,即 this 值(全局做用域調用函數時引用的是 window
)app
// vscode是node運行環境,沒法識別全局對象window,測試時需作微調 window.color = 'red' var o = { color: 'blue' } function sayColor() { console.log(this.color) } sayColor() // red,此時this指向對象window o.sayColor = sayColor o.sayColor() // blue,此時this指向對象o
ES5 定義了 caller
屬性,保存着調用當前函數的函數引用—— 誰調用了當前函數,caller 就是誰(全局做用域中調用當前函數 caller 值爲 null)函數
function callerTest() { console.log(callerTest.caller) } callerTest() // 在全局做用域種調用當前函數,caller 的值爲 null function outer() { inner() } function inner() { console.log(inner.caller) } outer() // outer()調用了inner,所以打印outer()函數的源代碼 function inner() { console.log(arguments.callee.caller) // 能夠解除緊密耦合 } outer() // 結果不變,打印outer()函數的源代碼
- 函數在嚴格模式下運行時,訪問
arguments.callee
會報錯- ES5 定義了
arguments.caller
屬性,嚴格模式會報錯,非嚴格模式始終是undefined
,該屬性是爲了區分arguments.caller
和 函數的caller
。- 嚴格模式不能爲函數的
caller
屬性賦值,會報錯
每一個函數都包含 2 個屬性:length
和 prototype
測試
length
表示函數但願接收的命名參數的個數
function nameLength(name) { return name } function sumLength(sum1, sum2) { return sum1 + sum2 } function helloLength() { return 'Hello' } console.log(nameLength.length, sumLength.length, helloLength.length) // 1,2,0
prototype
保存着函數全部實例方法且不可枚舉(使用for-in
沒法發現),每一個函數都包含 3 個非繼承而來的方法apply()
、call()
和 bind()
。
apply()
和 call()
的用途和結果相同 —— 都是在特定的做用域中調用函數,apply()
接收 arguments 對象或數組實例,call()
接收每個參數
function sumPrototype(num1, num2) { return num1 + num2 }
apply()
接收 2 個參數:① 運行函數的做用域;② 參數數組(實例或 arguments 對象都可)
function applySum1(num1, num2) { return sumPrototype.apply(this, arguments) // 傳入arguments對象 } function applySum2(num1, num2) { return sumPrototype.apply(this, [num1, num2]) // 傳入數組實例 } console.log(applySum1(10, 10)) console.log(applySum2(10, 10))
call()
接收若干參數:① 運行函數的做用域;剩餘參數分別傳入
function callSum(num1, num2) { return sumPrototype.call(this, num1, num2) // 分別傳入每一個參數 } console.log(callSum(10, 10))
apply()
和 call()
真正強大的地方在於可以擴充函數運行的做用域
// vscode是node運行環境,沒法識別全局對象window,測試時需作微調 window.color = 'red' var o = { color: 'blue' } function sayColor() { console.log(this.color) } sayColor() // red,此時this指向對象window sayColor().call(this) // red,此時this指向對象window sayColor().call(window) // red,此時this指向對象window sayColor().call(o) // blue,此時this指向對象o
ES5
追加 bind()
方法,其建立一個函數實例,其 this 被綁定到傳給 bind() 函數的值
// vscode是node運行環境,沒法識別全局對象window,測試時需作微調 window.color = 'red' var o = { color: 'blue' } function sayColor() { console.log(this.color) } var bindColor = sayColor.bind(o) bindColor() // blue,此時this被綁定給對象o