定义函数

函数定义的两种方式:

  • 函数声明式
1
2
3
4
// 函数声明式
function fun(arg0, arg1, ...){
// 函数体
}
  • 函数表达式
1
2
3
4
// 函数表达式
const fun = function (arg0, arg1, ...) {
// 函数体
}

函数声明式的特性:函数声明提升

函数声明提升是指在执行代码之前,会先读取函数声明。这样函数的调用就不必放在函数声明之后。

1
2
3
4
5
6
// 函数声明提升 示例 先调用在声明
sayHi() // Hi!
function sayHi() {
console.log('Hi!')
}

递归

递归函数是指应该函数通过函数名调用自身。

1
2
3
4
5
6
7
8
// 递归经典示例: 乘阶
function factorial(num) {
if(num <= 1 ){
return 1
}else {
return num * factorial(num - 1)
}
}

但是上例函数如果这样调用就会出现报错

1
2
3
const anotherFactorial = factorial
factorial = null
console.log(anotherFactorial(5)) // 出错

原因:factorial 的函数体中,递归调用是通过调用函数名调用的方式,由于函数指向时 factorial 指向 null ,所有无法找到 factorial 并执行,因此会报错。

解决方法: 将函数递归调用变成 arguments.callee 的方式,arguments.callee 指向当前正在执行的函数。

1
2
3
4
5
6
7
function factorial(num) {
if(num <= 1 ){
return 1
}else {
return num * arguments.callee(num - 1)
}
}

闭包

闭包是指有权访问另一个函数作用域的变量函数。

创建闭包的常见方式: 在一个函数内部创建另一个函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 闭包示例
function createComparisonFunction(propertyName) {
return function(object1, object2) {
let value1 = object1[propertyName]
let value2 = object2[propertyName]
if (value1 < value2) {
return -1
}else if (value1 > value2) {
return 1
}else {
return 0
}
}
}

闭包的使用

1
2
3
4
// 闭包的使用
const compareFunction = createComparisonFunction("name")
const res = compareFunction({name: 'zs'}, {name: 'ls'})
console.log(res) // 1

因为闭包会携带函数的执行环境(函数的作用域),因此会比其他函数占用更多的内存资源。所有需要谨慎使用闭包。

闭包与变量

闭包只能取得包含函数中任何变量的最后一个值。

1
2
3
4
5
6
7
8
9
10
11
12
function createFunction() {
const res = new Array()
for (var i = 0; i < 10; i++) {
res[i] = function () {
return i
}
}
return res
}
console.log(createFunction()[0]()) // 10
console.log(createFunction()[1]()) // 10
console.log(createFunction()[2]()) // 10

上面例子中的函数应该返回应该函数数组,表面上看,函数中的每个函数执行后应该返回它的索引值,但是实际上每个函数返回的都是 10 。原因在于每个函数中所引用的变量 i 都是同一个变量 i 。当 createFunction() 执行完成后, i 的值等于 10 ,所有数组中每个函数返回的都是同一个变量的值 10 。我们可以通过创建另一个匿名函数强制让闭包的行为符合预期( var 定义的变量在函数内有块级作用域)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function createFunction() {
const res = new Array()
for (var i = 0; i < 10; i++) {
res[i] = function (num) {
return function() {
return num
}
}(i)
}
return res
}
console.log(createFunction()[0]()) // 0
console.log(createFunction()[1]()) // 1
console.log(createFunction()[2]()) // 2

当然上面的问题也与 var 定义的变量没有块级作用域有关,在 ES6 语法中可以通过 let 关键字定义变量 i 解决这个问题。

关于 this 对象

this 对象是在运行时基于函数的执行环境绑定的:在全局函数中, this 等于 window ,而在函数被作为对象的方法调用时, this 等于当前调用方法的对象。匿名函数的执行具有全局性,它 this 对象通常指向 window 。

1
2
3
4
5
6
7
8
9
10
11
let name = 'window'
const object = {
name: 'object',
getName: function () {
return function () {
return this.name
}
}
}

console.log(object.getName()()) // undefined

这里和书上写的不太一样,暂不清楚什么原因