本节笔记记录JavaScript函数的陌生知识点,包括函数构造,函数参数,函数调用,闭包。


函数构造

函数表达式

JS函数可以通过一个表达式定义,表达式可以存储在变量中,变量也可作为函数使用:

1
2
var x = function (a, b) {return a * b};
var z = x(4,2);

这样的函数实际上是个匿名函数(没有名称)。函数存储在变量中,通过变量名调用。


Function()构造函数

函数通过function定义,也可用js函数构造器**(function())**定义:

1
2
3
4
5
6
   //原写法
var myFunction = function (a, b) {return a * b};
var x = myFunction(4, 3);
//构造函数
var myFunction = new Function("a", "b", "return a * b");
var x = myFunction(4, 3);

但在js中应当尽量避免使用new


自调用函数

函数表达式可以通过紧跟(),实现自动调用。不能自调用一个声明的函数,通过添加括号包裹函数来说明它是一个函数表达式:

1
2
3
(function () {
    var x = "Hello!!";
})();//一个匿名的自调用函数

函数是一个对象

js函数被描述为一个对象更加准确,有属性方法

属性举例

name:函数的名称属性,表示函数名称

length:函数的长度属性,表示函数接收的参数个数

1
2
3
4
5
6
function exampleFunc(param1, param2) {
// 函数体
}

console.log(exampleFunc.name); // 输出:exampleFunc
console.log(exampleFunc.length); // 输出:2

方法举例

toString():将函数作为一个字符串返回。

call(): 调用一个函数,可以设置函数内部的 this 指向,并传入参数列表。

apply(): 与 call() 类似,但参数以数组形式传入。

bind(): 创建一个新函数,将原函数的 this 绑定到指定对象,并可预先传入部分参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function myFunction(a, b) {    return a * b;}
var txt = myFunction.toString();


function greet(name) {
console.log(`Hello, ${name}!`);
}
// 使用 call 方法
greet.call(null, 'Alice'); // 输出:Hello, Alice!
// 使用 apply 方法
greet.apply(null, ['Bob']); // 输出:Hello, Bob!
// 使用 bind 方法
const boundGreet = greet.bind(null, 'Charlie');
boundGreet(); // 输出:Hello, Charlie!

函数提升(Hoisting)

提升(Hoisting)是 JavaScript 默认将当前作用域提升到前面去的行为。提升(Hoisting)是 JavaScript 默认将当前作用域提升到前面去的行为。因此,函数可以在声明之前调用:

1
2
3
4
5
myFunction(5);

function myFunction(y) {
return y * y;
}

使用表达式定义函数时无法提升。


箭头函数

箭头函数可替代传统函数定义方法,比普通函数表达式更简洁:

1
2
3
4
(参数1, 参数2, …, 参数N) => { 函数声明 }

(参数1, 参数2, …, 参数N) => 表达式(单一)
// 相当于:(参数1, 参数2, …, 参数N) =>{ return 表达式; }

只有一个参数时,原括号可以不写;没有参数时,则必须保留原括号:

1
2
3
4
(单一参数) => {函数声明}
单一参数 => {函数声明}

() => {函数声明}

箭头函数实例:

1
2
3
4
5
6
7
8
// ES5
var x = function(x, y) {
return x * y;
}

// ES6
const x = (x, y) => { return x * y }
//如果函数只有一个语句,则可以省略return和大括号

箭头函数通常用于创建匿名函数和回调函数,这样写更简洁;

使用箭头函数时,箭头函数会默认绑定为外层this的值,所以箭头函数的this和外层一样;

箭头函数不能提升,使用前先定义;

函数表达式始终是一个常量,因此使用const更安全。



函数参数

显式参数(Parameters)与隐式参数(Arguments)

函数显式参数在函数定义时列出(形参)。

函数隐式参数在函数调用时传递给函数真正的值(实参)。


默认参数

函数在调用时未提供隐式函数,参数会被默认设置为undefined

1
2
3
4
5
6
7
8
9
10
function myFunction(x, y) {
if (y === undefined) {
y = 0;
}
}//默认为undefined

//或者写为
function myFunction(x, y) {
y = y || 0;
}

可以为参数设置默认值:

1
2
3
4
5
6
7
function myFunction(x, y = 10) {
// y is 10 if not passed or undefined
return x + y;
}

myFunction(0, 2) // 输出 2
myFunction(5); // 输出 15, y 参数的默认值

arguments 对象

js函数中有一个内置的对象arguments对象,是一个由函数参数构成的参数数组,可以通过此数组进行各种操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//查找最大值
x = findMax(1, 123, 500, 115, 44, 88);

function findMax() {
var i, max = arguments[0];

if(arguments.length < 2) return max;

for (i = 0; i < arguments.length; i++) {
if (arguments[i] > max) {
max = arguments[i];
}
}
return max;
}

//统计数值的和
y = sumAll(1, 123, 500, 115, 44, 88);

function sumAll() {
var i, sum = 0;
for (i = 0; i < arguments.length; i++) {
sum += arguments[i];
}
return sum;
}

延申:js没有函数重载的功能,但可通过对arguments的元素和数组长度等方式模拟重载


传参方式

通过值传递

在函数中调用的参数是函数的隐式参数。

JavaScript 隐式参数通过值来传递:函数仅仅只是获取值

如果函数修改参数的值,不会修改显式参数的初始值(在函数外定义),隐式参数的改变在函数外是不可见的。

通过对象传递

在JavaScript中,可以引用对象的值。

因此我们在函数内部修改对象的属性就会修改其初始的值

修改对象属性可作用于函数外部(全局变量)。修改对象属性在函数外是可见的。



函数调用

此部分内容围绕函数的this的初始化展开,详情参考:

JavaScript 函数调用 | 菜鸟教程 (runoob.com)



闭包

函数生命周期

普通函数在执行完毕后会将结果返回给调用者,函数内部的局部变量和参数等在函数执行结束后会被销毁,除非有其他引用指向它们;

当函数执行完成后,函数内部的局部变量会被垃圾回收机制回收,无法再被访问。

变量生命周期

全局变量的作用是全局性的;

而在函数内部声明的变量是局部性的,只在函数内部起作用。

闭包:形成函数私有的作用域

闭包是一种保护私有变量的机制,在函数执行时形成私有的作用域,保护里面的私有变量不受外界干扰。直观的说就是形成一个不销毁的栈环境。

通过在函数内部设置内嵌函数,在内嵌函数中引用函数的变量,因为变量被引用着所以不会被回收,于此同时函数的其他未引用部分会被回收。

计数器闭包实例:

1
2
3
4
5
6
7
8
9
10
var add = (function () {
var counter = 0;//只会初始化进行一次
return function () {return counter += 1;}//内嵌函数实现闭包
})();

add();
add();
add();

// 计数器为 3

也可写为:

1
2
3
4
5
6
7
8
 function x() {
var counter = 0;
return function () {return counter += 1;}
};
var add = x();//接收内嵌函数
add();
add();
add();

错误写法:

1
2
3
4
5
6
7
8
9
10
var add = function () {
var counter = 0;
return function () {return counter += 1;}
};
//此时add得到的返回值是内部的匿名函数,而非匿名函数的返回值
//需要另一个变量接收add()的返回值
add();
add();
add();
//只会得到function () {return counter += 1;}

参考资料:JavaScript 闭包 | 菜鸟教程 (runoob.com)