关注前端小讴,浏览更多原创技术文章
- 函数是 对象 ,每个函数都是
Function
类型的实例,都与其余援用类型一样具备 属性 和办法 - 函数名是 指向函数对象的指针,不会与某个函数绑定(一个函数可能会有多个名字)
相干代码 →
4 种定义形式
// 1. 函数申明定义
function sum(num1, num2) {return num1 + num2}
// 2. 函数表达式定义
let sum = function (num1, num2) {return num1 + num2}
// 4. 箭头函数定义
let sum = (num1, num2) => {return num1 + num2}
// 4.Function 构造函数定义
/* 不举荐应用构造函数定义,因为会造成解析 2 次代码(1 次解析惯例 js 代码,1 次解析传入构造函数中的字符串)*/
var sum = new Function('num1', 'num2', 'return num1+num2')
10.1 箭头函数
- 任何能够应用函数表达式的中央 都能够 应用箭头函数
let arrowSum = (a, b) => {return a + b}
let functionExpressionSum = function (a, b) {return a + b}
console.log(arrowSum(5, 8)) // 13
console.log(functionExpressionSum(5, 8)) // 13
- 如果只有 1 个参数,能够 不必括号(多个参数或无参数必须用括号)
let double = (x) => {return x * 3}
console.log(double(3)) // 9
- 能够 不必大括号 ,如果这样则箭头前面 只能有 1 行代码(赋值操作或表达式),且 隐式返回这行代码的值(不能有
return
)
let person = {}
let setName = (obj) => (obj.name = 'Matt') // 相当于 {return obj.name = 'Matt'}
// let setName = (obj) => {return (obj.name = 'Matt') } // 用大括号的写法
setName(person)
console.log(person.name) // 'Matt'
-
箭头函数 不实用 的场合:
- 不能应用
arguments
、super
和new.target
- 不能用作构造函数
- 没有
prototype
属性()
- 不能应用
10.2 函数名
- 函数名是 指向函数的指针 ,一个函数能够有 多个 名称
function sum(num1, num2) {return num1 + num2}
console.log(sum(10, 10)) // 20
var anotherSum = sum // 应用不带括号的函数名是拜访函数指针,而非调用函数
console.log(anotherSum(10, 10)) // 20
sum = null // 切断 sum 与函数的关系
console.log(anotherSum(10, 10)) // 20,anotherSum()仍可失常调用
// console.log(sum(10, 10)) // 会报错,sum is not a function
- ES6 所有函数对象都裸露一个 只读 的
name
属性,默认保留 函数标识符(字符串化的函数名) - 无函数名 则标识成空字符串,应用
Function
构造函数创立则标识成anonymous
function foo() {} // 函数申明
let bar = function () {} // 函数表达式
let baz = () => {} // 箭头函数
console.log(foo.name) // 'foo'
console.log(bar.name) // 'bar'
console.log(baz.name) // 'baz'
console.log((() => {}).name) // 空字符串
console.log(new Function().name) // 'anonymous'
- 如果函数是一个 获取函数 、 设置函数 或应用
bind()
实例化 ,标识符前会加上一个 前缀
console.log(foo.bind(null).name) // 'bound foo',标识符前加前缀
let dog = {
years: 1,
get age() {return this.years},
set age(newAge) {this.years = newAge},
}
let propertyDescriptor = Object.getOwnPropertyDescriptor(dog, 'age') // 获取属性描述符
console.log(propertyDescriptor) // {get: [Function: get age], set: [Function: set age], enumerable: true, configurable: true }
console.log(propertyDescriptor.get.name) // 'get age',标识符前加前缀
console.log(propertyDescriptor.set.name) // 'set age',标识符前加前缀
10.3 了解参数
- JS 函数 不关怀 传入的 参数个数 和数据类型
- 可在 非箭头函数 外部拜访
arguments
类数组 对象,获得每个参数(如arguments[0]
) - JS 函数的命名参数在调用时 不必必须匹配函数签名,不存在验证命名参数的机制
function sayHi(name, message) {console.log(`Hello ${name}, ${message}`)
}
function sayHi2(name, message) {console.log(`Hello ${arguments[0]}, ${arguments[1]}`)
}
function sayHi3() {
// 没有命名参数
console.log(`Hello ${arguments[0]}, ${arguments[1]}`)
}
sayHi('Jake', 'How are you') // 'Hello Jake, How are you'
sayHi2('Jake', 'How are you') // 'Hello Jake, How are you'
sayHi3('Jake', 'How are you') // 'Hello Jake, How are you'
- 能够通过
arguments.length
查看传入参数的个数
function howManyArgs() {console.log(arguments.length) // 查看参数个数
}
howManyArgs('string', 45) // 2
howManyArgs() // 0
howManyArgs(12) // 1
arguments
对象能够跟命名参数一起应用
function doAdd(num1, num2) {if (arguments.length === 1) {console.log(num1 + 10)
} else if (arguments.length === 2) {console.log(arguments[0] + num2)
}
}
doAdd(10) // 20,10+10
doAdd(30, 20) // 50,30+20
-
arguments
对象的值始终会与命名参数 同步- 在内存中中离开(非拜访同一个地址),仅值同步
- 调用函数时未传的参数,不会因为
arguments
扭转而扭转(始终是 undefined)
function args(num1, num2) {arguments[1] = 10
console.log(num1, num2)
}
args(2, 3) // 2 10
function args2(num1, num2) {arguments[1] = 10
console.log(num1, num2)
}
args2(2) // 2 undefined,调用函数时只传入一个参数,批改 arguments 不会扭转第二个命名参数,仍旧是 undefined
function args3(num1, num2) {
num1 = 10
console.log(arguments[0], arguments[1])
}
args3(2, 3) // 10 3
-
严格模式下,
arguments
会有一些变动:arguments
对象的值与命名参数 不再同步 ,批改arguments
不再对命名参数产生影响- 在函数中重写
arguments
对象会报错,代码也不会执行
function strictArgs(num1, num2) {
'use strict'
arguments[1] = 10
console.log(num1, num2)
// arguments = [] // SyntaxError: Unexpected eval or arguments in strict mode,不能重写 arguments}
strictArgs(2, 3) // 2 3,arguments 与命名参数不再同步
- 箭头函数 不能拜访
arguments
,只能拜访命名参数
let bar2 = (num1, num2) => {// console.log(arguments[0], arguments[1]) // Uncaught ReferenceError: arguments is not defined
console.log(num1, num2)
}
bar2(2, 3) // 2 3
- JS 中的所有参数都是 按值传递 的,如果 把对象作为参数传递,传递的值是这个对象的援用(仍是按值传递)
10.4 没有重载
- JS 的函数 没有签名 ,因而 没有重载 ,后定义的同名函数会 笼罩 先定义的
function addSomeNumber(num) {return num + 100}
function addSomeNumber(num) {return num + 200}
let result = addSomeNumber(100)
console.log(result) // 300
// 把函数名当成指针
let addSomeNumber2 = function (num) {return num + 100}
addSomeNumber2 = function (num) {return num + 200}
let result2 = addSomeNumber2(100)
console.log(result2) // 300
10.5 默认参数值
- ES5 及以前,实现默认参数的罕用办法是 检测某个参数是否等于
undefined
function makeKing(name) {
name = typeof name !== 'undefined' ? name : 'Henry' // 检测参数 name 是否为 undefined,如果是则赋值
return `King ${name} VIII`
}
console.log(makeKing()) // 'King Henry VIII'
console.log(makeKing('James')) // 'King James VIII'
- ES6 及当前反对 显式定义默认参数 ,在 函数定义中的参数后赋值 即可
function makeKing2(name = 'Henry') {return `King ${name} VIII`
}
console.log(makeKing2()) // 'King Henry VIII'
console.log(makeKing2('James')) // 'King James VIII'
- 应用默认参数时,
arguments
对象 不反映参数默认值,只反映传给函数的参数 ,批改命名参数不会影响arguments
对象
function makeKing3(name = 'Henry') {
name = 'Louis' // 批改命名参数
return `King ${arguments[0]}`
}
console.log(makeKing3()) // 'King undefined',传给函数的参数为 undefined
console.log(makeKing3('James')) // 'King James',传给函数的参数为 'James'
- 能够应用 调用函数的返回值 作为默认参数值,计算默认值的函数在 未传相应参数 时被调用
let romanNumerals = ['Ⅰ', 'Ⅱ', 'Ⅲ', 'Ⅳ', 'Ⅴ', 'Ⅵ']
let ordinality = 0
function getNumerals() {return romanNumerals[ordinality++] // 每次调用后递增
}
function makeKing4(name = 'Henry', numerals = getNumerals()) {return `King ${name} ${numerals}`
}
console.log(makeKing4()) // 'King Henry Ⅰ',未传 numerals 参数,调用函数 getNumerals()
console.log(makeKing4('James', 'Ⅸ')) // 'King James Ⅸ',已传 numerals 参数,不调用函数
console.log(makeKing4()) // 'King Henry Ⅱ',未传 numerals 参数,调用函数 getNumerals()
console.log(makeKing4()) // 'King Henry Ⅲ',未传 numerals 参数,调用函数 getNumerals()
- 箭头函数 同样能够应用默认参数,只有一个参数时不能省略括号
let makeKing5 = (name = 'Henry') => `King ${name}`
console.log(makeKing5()) // 'King Henry'
默认参数作用域与暂时性死区
- 给参数定义默认值,实际上相当于应用
let
关键字 按程序 申明变量一样
function makeKing6(name = 'Henry', numerals = 'Ⅷ') {return `King ${name} ${numerals}`
}
// 应用 let 按程序申明变量
function makeKing7() {
let name = 'Henry'
let numerals = 'Ⅷ'
return `King ${name} ${numerals}`
}
- 后定义默认值 的参数能够 援用先定义的参数 ;反之会因为 暂时性死区 报错(
let
申明的变量 不会在作用域中被晋升)
function makeKing8(name = 'Henry', numerals = name) {return `King ${name} ${numerals}`
}
console.log(makeKing8()) // 'King Henry Henry'
function makeKing9(name = numerals, numerals = 'Ⅷ') {return `King ${name} ${numerals}`
}
// console.log(makeKing9()) // ReferenceError: Cannot access 'numerals' before initialization
- 参数存在本人的作用域,不能 援用函数体的作用域
function makeKing10(name = 'Henry', numerals = defaultNumeral) {
let defaultNumeral = 'Ⅷ'
return `King ${name} ${numerals}`
}
// console.log(makeKing10()) // ReferenceError: defaultNumeral is not defined
10.6 参数扩大与收集
- ES6 新增 扩大操作符
...
,能够用于 调用函数时传参 和定义函数参数
10.6.1 扩大参数
- 可对 可迭代对象 利用扩大操作符并将其 作为一个参数 传入,会将可迭代对象 拆分 并传入迭代返回的 每个值
let values = [1, 2, 3, 4]
function getSum() {
let sum = 0
for (let i = 0; i < arguments.length; i++) {sum += arguments[i]
}
return sum
}
console.log(getSum(...values)) // 10,1+2+3+4
- 可在扩大操作符 后面 、 候面 再传其余的值
console.log(getSum(-1, ...values)) // 9,-1+1+2+3+4
console.log(getSum(...values, 5)) // 15,1+2+3+4+5
console.log(getSum(-1, ...values, 5)) // 14,-1+1+2+3+4+5
console.log(getSum(...values, ...[5, 6, 7])) // 28,1+2+3+4+5+6+7
arguments
对象仍依照调用函数时传入的参数接管每一个值
function countArgs() {console.log(arguments.length)
}
countArgs(-1, ...values) // 5
countArgs(...values, 5) // 5
countArgs(-1, ...values, 5) // 6
countArgs(...values, ...[5, 6, 7]) // 7
- 应用扩大操作符的同时,也能够应用默认参数
function getProduct(a, b, c = 1) {return a * b * c}
console.log(getProduct(...[1, 2])) // 2,1*2*1
console.log(getProduct(...[1, 2, 3])) // 6,1*2*3
console.log(getProduct(...[1, 2, 3, 4])) // 6,1*2*3
let getSum2 = (a, b, c = 0) => {return a + b + c}
console.log(getSum2(...[0, 1])) // 1,0+1
console.log(getSum2(...[0, 1, 2])) // 3,0+1+2
console.log(getSum2(...[0, 1, 2, 3])) // 3,0+1+2
10.6.2 收集参数
- 能够应用扩大操作符把 不同长度 的独立参数组合为一个 数组
function getSum3(...values) {return values.reduce((pre, cur) => pre + cur, 0)
}
console.log(getSum3(1, 2, 3)) // 6
- 因为收集参数的后果可变,因而 只能 把它作为 最初 一个参数
- 收集参数的后面如果还有命名参数,则只收集 其余 参数;若 没收集到 则取得 空数组
function getProduct(...values, lastValue) {} // SyntaxError: Rest parameter must be last formal parameter
function ignoreFirst(firstValue, ...values) {console.log(values)
}
ignoreFirst() // [],没收集到
ignoreFirst(1) // [],没收集到
ignoreFirst(1, 2) // [2],收集其余参数
ignoreFirst(1, 2, 3) // [2, 3],收集其余参数
- 箭头函数 反对 收集参数的定义形式,可用其实现与应用
arguments
一样的逻辑
let getSum4 = (...values) => values.reduce((pre, cur) => pre + cur, 0)
console.log(getSum4(1, 2, 3, 4)) // 10
- 收集参数 不影响
arguments
对象,仍反映调用时传给函数的参数
function getSum5(...values) {console.log(arguments.length) // 4
console.log(arguments) // [Arguments] {'0': 1, '1': 2, '2': 3, '3': 4}
console.log(values) // [1, 2, 3, 4]
}
getSum5(1, 2, 3, 4)
10.7 函数申明与函数表达式
- js 引擎在代码开始执行之前,解析器通过 函数申明晋升(function declaration hoisting)的过程,将 申明函数 放到源代码树的顶部,使其 在执行任何代码之前可用(能够拜访);而 函数表达式 则必须等到解析器执行到所在代码行才被解释执行。
- 函数申明 和函数表达式 的惟一区别就是 什么时候能够通过变量拜访函数
console.log(sumDeclare(10, 10)) // 函数申明会提前
function sumDeclare(num1, num2) {return num1 + num2}
console.log(sumExpression(10, 10)) // 函数表达式不会提前,会报错,sumExpression is not a function
let sumExpression = function (num1, num2) {return num1 + num2}
10.8 函数作为值
- 函数名 在 ECMAScript 中是 变量 ,函数即可作为另一个函数的 参数 ,也可在一个函数中 返回 另一个函数
- 如果是 拜访函数指针 而非调用函数,必须 不带括号
function callSomeFunction(someFunction, someArgument) {return someFunction(someArgument)
}
function add10(num) {return num + 10}
let result3 = callSomeFunction(add10, 10) // 拜访函数的指针而不是执行函数,add10 不带括号
console.log(result3) // 20
function getGreeting(name) {return 'Hello,' + name // Hello,Nicholas}
let result4 = callSomeFunction(getGreeting, 'Nicholas') // 拜访函数的指针而不是执行函数,getGreeting 不带括号
console.log(result4) // 'Hello,Nicholas'
- 依据数组对象的某个对象属性进行排序:定义一个依据属性名来 创立比拟函数的函数
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'))) // [{name: 'Tom', id: 2},{name: 'Tony', id: 3},{name: 'Jack', id: 5}],按 id 正序排列
console.log(userList.sort(arraySort('id', 'desc'))) // [{name: 'Jack', id: 5},{name: 'Tony', id: 3},{name: 'Tom', id: 2}],按 id 倒序排列
console.log(userList.sort(arraySort('name'))) // [{name: 'Jack', id: 5},{name: 'Tom', id: 2},{name: 'Tony', id: 3}],按 name 正序排列
总结 & 问点
- 函数是什么?函数有哪几种定义形式?
- 箭头函数在什么状况下能够不必参数的括号?什么状况下能够不必函数体的大括号?
- 箭头函数为什么不能用作构造函数或定义原型办法?
- 函数名是什么?一个函数能够有多少函数名?如何获取函数标识符?
- 函数外部的 arguments 对象是什么?其应用有哪些特点和限度?
- 如何了解并证实 JS 的函数没有重载?
- ES5 及之前、ES6 及之后,别离如何定义函数的默认参数?
- arguments 对象与函数默认参数有什么关联?如何了解默认参数的作用域与暂时性死区?
- 扩大操作符有什么作用?定义参数时应用其对 arguments 有什么影响?
- 箭头函数如何实现与 arguments 一样的逻辑,获取每个参数?
- 函数申明与函数表达式有什么区别?
- 写一段代码,依据对象数组的某个对象属性进行排序,可依据参数决定排序属性及升 / 降序