欢迎关注前端小讴的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