1. 函数案例

  1. // 声明函数
  2. function getSum(a, b) {
  3. return a + b
  4. }
  5. // 调用函数
  6. let sum = getSum(1, 3)
  7. console.log(sum) // 4
  • 函数是实现特定功能的n条语句的封装体
  • 函数类似于榨汁机,将苹果放进去,出来的是苹果汁
  • 当一段代码需要多次执行时,考虑封装为函数(提高代码复用)
  • 先声明,再调用;调用才真正执行

    2. 两种声明方式

  • 函数声明 ```javascript function sayHello(name) { console.log(‘Hello’ + name) // “Hello刘德华” }

sayHello(‘刘德华’)

  1. - **函数表达式**
  2. ```javascript
  3. const sayHello = function(name) {
  4. console.log('Hello' + name) // "Hello刘德华"
  5. }
  6. sayHello('刘德华')

3. 参数

  • 声明时可设置形参 ```javascript function printInfo(name, age) { console.log(name) console.log(age) } printInfo(‘刘德华’, 50) // 刘德华 50 printInfo(‘刘德华’) // 刘德华 undefined

// 默认参数 function printInfo(name, age=50) { console.log(name) console.log(age)
} printInfo(‘刘德华’) // 刘德华 50


- **arguments对象**
   - 使用arguments对象获取到该函数的所有传入参数
```javascript
function printInfo() {
  console.log(arguments[0])  // "刘德华"
  console.log(arguments[1])   // 50
  console.log(arguments.length)  // 2
}

printInfo('刘德华', 50)
/*
  求任意个数参数的和
*/

function getSum() {
  let sum = 0
  for(let i = 0; i < arguments.length; i++) {
    sum += arguments[i]
  }
  return sum
}

console.log(getSum(8))         // 8
console.log(getSum(1, 2))      // 3
console.log(getSum(1, 3, 4))   // 8

4. 返回值

  • 使用return把结果导出
    • 函数执行到return就立即终止
    • 如果没设置return,默认返回的是undefined ```javascript

function printInfo() { console.log(1) // 1 console.log(2) // 2 return console.log(3) // 不执行 console.log(4) // 不执行 } printInfo()

function getSum(a, b) { let res = a + b // 没有return }

let result = sum(1, 4) console.log(result) // undefined


- **console.log() 和 return**
```javascript
function sum(a, b) {
  return console.log(a + b)  // console.log()的返回值是undefined
}

let res = sum(1, 4)
console.log(res)   // undefined

5. 箭头函数

  • 箭头函数简化了函数表达式
  • 箭头函数和函数表达式有细微差异,体现在内部的this指代上

    • 一个参数

      /*
      const sayHello = function(name) {
      return 'Hello' + name
      }
      等同于下面箭头函数
      */
      const sayHello = name => 'Hello' + name
      
      /*
      当函数体有多条语句时要加大括号
      下面的箭头函数求数组元素之和  
      */
      const getSum = arr => {
      let sum = 0
      for(let val of arr) {
      sum += val
      }
      return sum
      }
      let arr = [1, 3, 5]
      let result = getSum(arr)
      console.log(result)  // 9
      
    • 多个参数 ```javascript / const getSum = function(a, b) { return a + b } 等同于下面箭头函数 / const getSum = (a, b) => a + b

```javascript

/*

  function add(a) {
    return function(b) {
      return a + b
    }
  }
  // 等价于下面箭头函数

*/


const add = a => b => a + b
let result = add(1)(5)
console.log(result)  // 6

6. 函数命名

  • 动词、动宾
    • sum、hide、show
    • printName、sayHello
  • 返回一个非布尔值
    • getData、getBooks
  • 设置内容
    • setInfo、setData
  • 返回一个布尔值
    • hasMoney、isEnabled

7. 声明前置

  • var声明前置 ```javascript / // var a 提前 // var b 提前 var a a = 3 console.log(a) function doSomething() { var b console.log(b) b = 10 console.log(b) } doSomething() /

a = 3 console.log(a) // 3 var a function doSomething() { console.log(b) // undefined var b = 10 console.log(b) // 10 } doSomething()


- **function声明前置**
```javascript
fn1()  // 正常执行
function fn1() {
  console.log('fn1')
}


fn2()  //会报错,函数表达式不会前置
const fn2 = function() {
  console.log('fn2')
}

8. 运行环境

  • 全局环境
  • 函数环境
    console.log('in 全局 start')
    foo()
    console.log('in 全局 end')
    function foo() {
    console.log('in foo')
    bar()
    function bar() {
      console.log('in bar')
    }
    }
    
    image.png

9. 作用域链

  • 函数在执行的过程中,先从自己内部找变量
  • 如果找不到,再从创建当前函数所在的作用域(词法作用域)去找, 以此往上, 直到全局作用域
  • 注意找的是变量的此刻的状态
    let a = 0
    function foo() {
    let a = 1
    bar()
    }
    function bar() {
    console.log(a)  // 输出0
    }
    foo()
    
    let a = 0
    function foo() {
    let a = 1
    bar()
    function bar() {
      console.log(a)  // 输出1
    }
    }
    foo()
    
    ```javascript let a = 0 function foo() { bar() var a = 1 function bar() { console.log(a) // 输出undefined } } foo()
```javascript
let a = 1

function fn1(){
  function fn2(){
    console.log(a)  // 输出2
  }
  function fn3(){
    let  a = 3
    fn2()
  }
  let a = 2
  return fn3  
}

let fn = fn1()
fn()

10. 立即执行函数表达式(IIFE)

  • 声明一个函数、立即执行它
  • 意义:隔离作用域 ```javascript / (function() { //函数体 })() /

(function() { let a = 9 let b = 8 console.log(a + b) })()

(function () { var a = 1 function test() { console.log(++a) } window.$ = function() { // 向外暴露一个全局函数 return { test: test } } })()

$().test() // 2

<a name="Efduw"></a>
# 11. 递归

- 自己调用自己
- 设置终止条件
- 缺点:多次重复计算,性能差
```javascript
// 求阶乘
function fac(n) {
  if(n === 1) return 1
  return n * fac(n - 1)
}

let a = fac(3)
console.log(a)  // 6
// 斐波那契数列
// 1 2 3 4 5 6
// 1 1 2 3 5 8 13
function fib(n) {
  if(n === 1 || n === 2) return 1
  return fib(n - 1) + fib(n - 2)
}

let a = fib(4)
console.log(a)
// 斐波那契数列
// 1 2 3 4 5 6
// 1 1 2 3 5 8 13
// 非递归方法
function fib(n) {
  let first = 1, second = 1, temp  
  for(let i = 3; i <= n; i++) {
    temp = first + second
    first = second
    second = temp
  }
  return second
}

let a = fib(6)
console.log(a)

12. 柯里化

  • 柯里化
    • 是把接受多个参数的函数变换成接受一个单一参数的函数
    • 一个函数里返回一个新函数 ```javascript function add(a) { return function(b) { return a + b } }

let sum = add(3)(4) console.log(sum)

<a name="zwN3V"></a>
# 13. 回调函数

- 自定义
- 没有调用
- 最终执行了
```html
    <button id="btn">回调函数</button>
  <script>
    let btn = document.querySelector('#btn')

    btn.onclick = function() {
      alert('dom事件回调函数')
    }

    setTimeout(function() {
      alert('定时器回调函数')
    }, 2000)
  </script>