第4章 函数
声明函数
函数声明
js
function 函数名() {
函数体;
return 需要返回的结果; // 只能返回一个值(最后一个值) 可返回数组
}
函数表达式
js
let 变量名 = function () {
函数体;
return 需要返回的结果; // 只能返回一个值(最后一个值) 可返回数组
};
区别
- 函数名称:在函数表达式中可省略它,从而创建匿名函数
- 函数声明可以先调用再声明,而函数表达式必须先定义再调用
调用函数
直接调用
js
函数名();
立即执行函数
- 立即执行函数:函数在定义完后,直接调用自身
- 优点:外部无法访问内部代码,且内部形成自己的作用域,防止与外部变量命名冲突
js
var num1 = 10;
(function() {
var num1 = 20;
console.log(num1); // 20
})();
console.log(num1); // 10
变量和函数提升
提升可以让我们先使用,后声明
js
// 变量的提升
x = 6;
console.log(x); //6
var x;
// 函数的提升
console.log(divide(8, 4));
function divide(a, b) {
return a / b;
}
默认参数
- 调用时不加参数,使用默认参数
- 调用时有参数,则使用调用时的参数
js
function greetings(name = '世界') {
console.log('你好,' + name);
}
greetings(); // 你好,世界
greetings('中国'); // 你好,中国
- 调用时如需要用默认参数 则可以使用 undefined 表示默认参数,不可使用 null
js
function greetingsWithWeather(name = '世界', weather) {
console.log('你好,' + name + ',今天是' + weather);
}
greetingsWithWeather(undefined, '晴天'); // 你好,世界,今天是晴天
递归
js
// 需要一个递归出口
// 从1加到n的递归
function sum(n) {
if (n === 1) return 1; // 递归出口
return n + sum(n - 1);
}
console.log(sum(3)); // 6
Arguments
Arguments 的特点
- 具有数组的 length 属性
- 按照索引的方式进行存储
- 没有真正数组的一些方法 pop() push() 等等
js
function getMax() {
// arguments = [2,125,123,74,114,134]
var max = arguments[0];
for (var i = 1; i < arguments.length; i++) {
if (arguments[i] > max) {
max = arguments[i];
}
}
return max;
}
console.log(getMax(2, 125, 123, 74, 114, 134));
rest 和 arguments 对象的区别
- rest 只包含那些没有对应形参的实参,而 arguments 包含了传给函数的所有实参
- rest 是真正的数组,而 arguments 对象不是一个真正的数组
- arguments 对象还有一些附加的属性 (如callee属性)
js
function variousArgs(num1, ...args) {
console.log(num1); // 1
console.log(args); // [2, 3]
console.log(arguments); // [Arguments] {'0': 1, '1': 2, '2': 3}
}
variousArgs(1, 2, 3);
作用域
- 如果一个变量在当前作用域没有定义
- 向上级作用域,一层一层一次寻找,直至被找到
- 如果全局作用域都没有找到,则显示 not defined
- 自由变量查找是在函数定义的地方,向上级作用域查找,而不是执行的地方
js
var x = 5;
function add(a) {
var y = 10;
console.log(y);
return a + x;
}
console.log(add(8)); // 13
x = 20; // 修改全局作用域变量的值
console.log(add(8)); // 28
console.log(y); // 局部作用域的变量无法在外面使用
箭头函数
介绍
- 箭头函数为匿名函数
- 当只有一个参数时,可省略 (),有零个或多个参数是不可省略
- 当箭头函数有返回值且只有一行代码,可省略 {} 和 return
注意点
- 箭头函数没有 Arguments
- 箭头函数没有 this 指向
- 不能用 new 关键字实例化对象
js
let greeting = () => {
console.log('hello');
};
greeting();
this 指向
基本概念
- 普通函数:this 指向调用所在函数的对象
- 箭头函数:没有自己的 this 指向,this 指向包裹其作用域的对象
注意点
- 在构造函数的方法中使用 this,this 指向这个构造函数
- 当对象实例化时,this 指向实例化后的对象
- 一般情况下,箭头函数不作为对象的方法
js
let zhangsan = {
name: '张三'
};
// 使用普通函数定义时,this 指向的是zhangsan
zhangsan.sayHi = function () {
console.log(this.name + '说Hi');
};
// 使用箭头函数定义时,this 指向Window对象(浏览器中)
zhangsan.sayHello = () => {
console.log(this.name + '说Hello');
};
zhangsan.sayHi(); // 张三说Hi
zhangsan.sayHello(); // undefined说Hello
闭包
概念
- 闭包是函数内部再定义函数
- 闭包让开发者可以从内部函数访问外部函数的作用域。
优点
- 私有化数据
- 避免污染全局作用域
缺点
- 变量不会被回收,可能会导致内存泄漏
返回值返回(高阶函数)
js
function create() {
let a = 100;
return function () {
console.log(a);
};
}
let fn = create();
let a = 200;
fn(); // 100
参数传入(回调函数)
js
function print(fn) {
let a = 200;
fn();
}
let a = 100;
function fn() {
console.log(a);
}
print(fn); // 100
柯里化
- 将一个接受多个参数的函数转化为一系列接受一个参数的函数
- 优势:可以将其中几个参数先固定下来,单独修改一个参数
js
function addThreeNums(a, b, c) {
return a + b + c;
}
// 柯里化后
function addThreeNumsCurry(a) {
return function (b) {
return function (c) {
return a + b + c;
};
};
}
console.log(addThreeNums(1, 2, 3)); // 6
console.log(addThreeNumsCurry(1)(2)(3)); // 6
// 优势
let fixedTwo = addThreeNumsCurry(1)(2);
console.log(fixedTwo(3)); // 6
回调函数
js
// 回调函数就是一个被作为参数传递的函数
function request(cb) {
console.log('请求开始');
cb('success');
console.log('请求结束');
}
// 回调函数(可含参)
function callback(result) {
console.log('执行回调');
console.log(result);
}
request(callback);
// 用箭头函数简写(不可复用)
request((result) => {
console.log('执行回调');
console.log(result);
});
预览: