乐趣区

关于javascript:javascript高级程序设计学习笔记-101108函数基础

关注前端小讴,浏览更多原创技术文章

  • 函数是 对象 ,每个函数都是 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'
  • 箭头函数 不实用 的场合:

    • 不能应用 argumentssupernew.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 一样的逻辑,获取每个参数?
  • 函数申明与函数表达式有什么区别?
  • 写一段代码,依据对象数组的某个对象属性进行排序,可依据参数决定排序属性及升 / 降序
退出移动版