# 一、函数
# 1、函数写法
TIP
JavaScript 语言将函数看作一种值,与其它值(数值、字符串、布尔值等等)地位相同。
下面看看函数的几种写法(个人习惯于匿名函数的书写方式):
上面的 声明式函数 和 函数表达式 两种写法有什么区别:
函数声明会被提升到作用域的顶部,因此可以在声明之前调用
回顾:变量提升
JavaScript 引擎的工作方式是,先解析代码,获取所有被声明的变量,然后再一行一行地运行。
- 示例
console.log(a) // undefined
var a = 1
- 代码分析
// 真正的执行过程为:
var a
console.log(a)
a = 1
并且我们要注意的是 函数提升 > 变量提升 的:
console.log(s) // ƒ s() {}
var s = 2
function s() {}
console.log(s) //2
// 正确
subtract(5, 3) // 可以在声明之前调用
function subtract(a, b) {
return a - b
}
// 错误
subtract(5, 3) // "TypeError: subtract is not a function
var subtract = function (a, b) {
return a - b
}
# 2、第一公民
TIP
函数与其他数据类型地位平等,他可以把函数赋值给变量和对象的属性,也可以当作参数传入其他函数,或者作为函数的return 值,甚至可以存储在数据结构(数组、对象等)中。
JavaScript 中的函数被认为是“第一类公民”(First-Class Citizens),它具有如下特性:
# 3、函数作用域 ✨
JavaScript 使用大括号{}
,将多个相关的语句组合在一起,称为“区块”(block)。
提示
作用域(scope)指的是变量存在的范围
对于 var 命令 ✍ 来说,JavaScript 的区块不构成单独的作用域
在 ES5 的规范中,JavaScript 只有两种作用域:一种是全局作用域,另一种是函数作用域。
在后来的 es6 语法中,JavaScript 提供了let变量
、const变量
,它们都是具有块级作用域的。
面试题 ✍️
- 变量声明提升问题
var x = 6
function example() {
console.log(x) // undefined
var x = 5
}
example()
console.log(x) // 6
- 作用域问题
函数本身也是一个值,也有自己的作用域(定义时所在的作用域),与其运行时所在的作用域无关。
var a = 1
var x = function () {
console.log(a)
}
function f() {
var a = 2
x()
}
f() // 1
var a = 1
function f() {
var a = 2
function demo() {
console.log(a)
}
demo()
}
f() // 2
# 4、函数传参
JavaScript 函数对传递的参数数量没有限制,即使声明时指定的参数个数与实际传递的参数个数不一致也是允许的。
# 5、值传递 🚩
在 JavaScript 中,函数参数的传递是通过值传递的,但对于引用类型的值(对象、数组等),传递的是引用的副本。
以上面的引用数据类型为例,修改函数参数的值可能会导致代码难以理解和维护。解决问题的核心就是 —— 避免共享引用,常见的方案如下:
提示
在《Array 增删改查》章节中的所有数组方法,都是改变了原数组的方法。
- 使用深拷贝
- 改变引用指向
- 使用不可变数据结构(Immutable.js (opens new window))
let arr = [2, 7, 11]
function update(arr) {
// 1、使用拷贝
// const newArr = arr.concat() // 对象的话,可以使用Object.assign({}, obj)
// const newArr = arr.slice()
const newArr = [...arr]
newArr[0] = 6
// 2、改变引用指向
// const newArr = [6, 7, 11]
console.log(newArr) // [6, 7, 11]
}
// 改变内容
update(arr)
console.log(arr) // [2, 7, 11]
# 二、函数类型
在开发中,除了常规的函数外,与函数相关的还有:
# 1、箭头函数
箭头函数的一大特点就是,它没有自己的 this,它们继承了外部(定义时所在的)作用域的 this 只
因此箭头函数可以解决传统函数中场景的
this
绑定问题。
更多的,它还有如下特点:
- 是匿名函数
- 无法访问 arguments
- 不建议在构造函数使用
# 2、IIFE 函数
IIFE(Immediately Invoked Function Expression)是 JavaScript 中一种常见的函数表达式,它会立即执行一次。
这种模式有助于创建私有作用域、防止污染全局命名空间,并且允许在函数内部定义变量和函数,而不会影响外部作用域。
# 3、函数闭包
函数闭包是指在 JavaScript 中,一个函数(innerFunction)可以访问其外部函数(outerFunction)作用域中的的变量和参数,即使在外部函数执行完毕后,内部函数仍然保持对外部函数(outerFunction)作用域的引用。
提示
闭包的使用有点像 react 中的 hook 函数的感觉
由于这样的函数调用方式可以访问原函数内部的变量和参数的特性,可以广泛应用于:
应用示例
- 应用示例 1:
for (var i = 0; i < oli.length; i++) {
oli[i].onclick = (function () {
// 使用闭包😄
return function (index) {
// index不会被销毁
console.log(index)
}
})(i)
}
- 应用示例 2:
mysearch.oninput = (function () {
// timer放在函数里面
var timer = null
return function () {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(function () {
console.log('发起ajax请求')
}, 500)
}
})()
# 4、定时器函数
在 JavaScript 中,定时器函数用于在指定的时间间隔内执行代码。有两种主要的定时器函数:setTimeout
和 setInterval
。