跳至主要內容

函数

Mr.Chen前端基石JS基础函数大约 5 分钟约 1483 字

定义

和变量类似,函数必须先定义然后才能使用

使用 function 关键字定义函数, function 是“功能”的意思

function fun() {
  // 函数体语句
}

函数表达式

var fun = function () {
  // 函数体语句
}

函数的调用

执行函数体中的所有语句,就称为“调用函数"

调用函数非常简单,只需在函数名字后书写圆括号对即可:fun()

函数声明的提升

和变量声明提升类似,函数声明也可以被提升

如果函数是用函数表达式的写法定义的,则没有提升特性

fun() //引发错误

var fun = function () {
  console.log(1)
}

而且函数是优先变量进行提升的:

fun() //2

function fun() {
  console.log(2)
}

// 变量声明提升,无法覆盖提升的函数
var fun = function () {
  console.log(1)
}

fun() //1

JS 的预解析机制

全局作用域,函数(局部)作用域,都是通过以下两个步骤进行预解析的。

  1. 先读取有 var 的变量(没有使用 var 的变量是不会被预解析的),给赋值为:undefined。如果两个变量重名,并不影响预解析的过程,就写一个变量就行,因为都将变量赋值为 undefined(在逐行读取时,只是不同的赋值而已。),如果有函数名和变量重名,那就直接去掉变量,不进行解析。如果函数中存在参数,那么参数也一样使用 var 进行解析。如:var argument=undefined;

  2. 再读取 function 后面的函数---fn,如果有多个函数名重复,那么取最后面一个函数进行声明。

console.log(a) // 打印函数a函数体(function a() { console.log('a') })

var a = 10

console.log(a) // 10

function a() {
  console.log('a')
}

console.log(a) // 10

预解析过程

  1. 首先预解析到有变量 a存在,因此记录下 a这个名字,和其值 undefined
  2. 接着预解析到有函数 a声明,记录下函数名 a
  3. 但是发现已经记录了一个 a ,因此第 1 步操作无效,(如果有函数名和变量重名,那就直接去掉变量,不进行解析)将函数体与 a 这个名字相关联
  4. 解析完毕

结果a = function () { console.log('a') }

执行过程

  1. 打印变量 a 的值 function a() { console.log('a') }
  2. 变量 a 赋值为 10,将原来关联的函数覆盖
  3. 打印变量 a 的值 10
  4. 打印变量 a 的值 10
  5. 执行完毕

函数的参数

  • 参数是函数内的一些待定值,在调用函数时,必须传入这些参数的具体值
  • 函数的参数可多可少,函数可以没有参数,也可以有多个参数,多个参数之间需要用逗号隔开
  • 形参和实参,"形实结合"
  • 函数的参数也是局部变量

arguments

函数内 arguments 表示它接收到的实参列表,它是一个类数组对象
类数组对象:所有属性均为从 0 开始的自然数序列,并且有 length 属性,和数组类似可以用方括号书写下标访问对象的某个属性值,但是不能调用数组的方法

function fun(a, b) {
  console.log(arguments)
  console.log('arguments长度' + arguments.length)
  for (var i = 0; i < arguments.length; i++) {
    console.log(arguments[i])
  }
}
fun(1, 2)

函数的返回值

  • 函数体内可以使用 return 关键字表示“函数的返回值”

  • 调用一个有返回值的函数,可以被当做一个普通值,从而可以出现在任何可以书写值的地方

function add(a, b) {
  return a + b
}
var result = add(2, 3) + add(4, 5)
  • 调用函数时,一旦遇见 return 语句则会立即退出函数,将执行权交还给调用者
function fun() {
  console.log('a')
  return 'b'
  console.log('c') // 这条语句不会输出
}
console.log(1)
var char = fun() // b
// console.log(char);
console.log(2)

闭包

定义

在函数中被使用,但它既不是函数参数、也不是函数的局部变量,而是一个不属于当前作用域的变量,此时它相对于当前作用域来说,就是一个自由变量。而引用了自由变量的函数,就叫闭包

它的最大用处有两个,一个是可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。

使用闭包的注意点

不能滥用闭包 否则会造成网页的性能问题,严重时可能导致内存泄露。所谓内存泄漏是指程序中已动态分配的内存由于某种原因未释放或无法释放。解决这个问题的办法就是在不使用这些变量时,及时把不需要的局部变量全部删除,即将它们置为 null

闭包的两道面试题

function addCount() {
  var count = 0
  return function () {
    count = count + 1
    console.log(count)
  }
}
var fun1 = addCount()
var fun2 = addCount()
fun1()
fun2()
fun2()
fun1()

::: details 答案 1 1 2 2 :::

function fn1() {
  var num1 = 1
  var num2 = 100
  numadd = function () {
    num2 += 10
    alert(num2)
  }
  function fn2() {
    num1 += 1
    alert(num1)
  }
  return fn2
}
var result = fn1()
result()
numadd()
result()

::: details 答案 执行结果: 2,110,3 (numdd 是定义在全局作用域的函数) :::

立即执行函数 IIFE

IIFE( Immediately Invoked Function Expression,立即调用函数表达式)是一种特殊的 JavaScript 函数写法,一旦被定义,就立即被调用。

IIFE 会通过声明并立即执行一个函数来创建作用域------你不知道的 js(上)

;(function () {
  console.log(1)
})()

IIFE 可以在一些场合(如 for 循环中)将全局变量变为局部变量:

var arr = []
for (var i = 0; i < 5; i++) {
  arr.push(function () {
    alert(i) // 变量i是全局变量,所有函数都共享内存中的同一个变量i
  })
}
arr[2]() // 5

使用 IIFE 解决:

var arr = []
for (var i = 0; i < 5; i++) {
  ;(function (i) {
    arr.push(function () {
      alert(i)
    })
  })(i)
}
arr[2]() // 2
上次编辑于: