主要内容:函数与闭包(类放在下一天)
- 函数默认值(原生及es6的简洁实现 - 技巧类)
- 传统函数表达式/回调的缺点与解决(this不好用)
- 通过箭头函数来解决(=>),但有些时候抽象了也不好理解
- 箭头函数语法(无参数、单参数、多参数)
- …rest/spread 剩余及扩展参数
- 对象字面量的扩展(或者叫简化)
- 闭包:可以访问自由变量的函数。可以用来:
- 访问函数中的私有变量;
- 快速创建多个全局可用的API。
1. 函数默认值
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>函数默认值</title>
</head>
<body>
<script>
// 原生方式
function delay(timeout, callback) {
timeout = typeof timeout !== undefined ? timeout : 3000;
callback = typeof callback !== undefined ? timeout : function () {};
setTimeout(callback, timeout);
}
delay();
// es6
function delay(timeout = 3000, callback = function () {}) {
setTimeout(callback, timeout);
}
delay();
</script>
</body>
</html>
2. 传统函数表达式/回调的缺点与解决
- 直接用this会出现错误,需要通过临时变量来转化
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>传统函数表达式/回调的缺点与解决方案</title>
</head>
<body>
<script>
let user = {
// 属性
name: "peter-zhu",
// 方法
getName: function () {
// console.log(this);
// 将当前this(user)放在一个临时变量中保存起来
let that = this;
setTimeout(function () {
// 回调的作用域是全局window
// console.log(this);
// console.log("My name is ", this.name);
console.log("My name is ", that.name);
}, 1000);
},
};
user.getName();
</script>
</body>
</html>
3. 通过箭头函数来解决
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>箭头函数的一个小案例</title>
</head>
<body>
<script>
let user = {
// 属性
name: "peter-zhu",
// 方法
getName: function () {
setTimeout(() => {
console.log(this);
console.log("My name is ", this.name);
}, 1000);
},
};
user.getName();
// 箭头函数主是要替代之前的函数表达式
// 箭头函数没有自己的this,支持词法作用域/块级作用域
</script>
</body>
</html>
4. 箭头函数语法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>箭头函数语法</title>
</head>
<body>
<script>
// 1. 无参数
let f1 = function () {
console.log("f1()");
};
f1();
// 箭头函数,没有参数,圆括号不能省
f1 = () => console.log("->f1()");
f1();
// 进一步使用IIFE:立即执行函数简化。但越简化会越抽象
// (() => console.log("->f1()"))();
console.log("---------------");
// 2. 单个参数
let f2 = function (item) {
console.log(`f2(${item})`);
};
f2("item");
// 箭头函数
f2 = (item) => console.log(`->f2(${item})`);
f2("item");
console.log("---------------");
// 3. 多个参数
let f3 = function (...items) {
console.log("f3()", items);
};
f3(1, 2, 3, 4);
// 箭头函数
f3 = (...items) => {
console.log(this);
console.log("->f3()", items);
};
f3(5, 6, 7, 8);
</script>
</body>
</html>
5. …rest/spread 剩余及扩展参数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>...rest/spread</title>
</head>
<body>
<script>
// 1. ...rest(剩余参数),将多个参数归并到一个数组中, 用在函数声明的"参数列表中"
let sum = (a, b) => a + b;
console.log(sum(4, 5));
// 累加
sum = (...args) => args.reduce((prev, next) => prev + next, 0);
console.log(sum(4, 5, 6, 7));
// 遍历
let f = (...items) => items.map((item) => console.log(item));
f("apple", "pear", "orange");
// 2. ...spread(扩展参数), 从一个集合中解析出一个个独立的元素,用在函数的调用的实际参数列表中
f = (a, b, c) => console.log(a + b + c);
f(3, 4, 5);
f(...[3, 4, 5]);
// 混合在一起
// ...rest
f = (a, ...items) => console.log(a, items);
f(4, 5, 6, 7);
// ...spread
f(...[4, 5, 6], 7); // 这个方式跟前面是一样的。自己分散开了。
</script>
</body>
</html>
6. 对象字面量的扩展
- 所谓的对象字面量、数组字面量等其实就是对象、数组实例化的一种方法。
例如实例化一般分两种(https://www.cnblogs.com/zhuzhenwei918/p/6013363.html)
- 构造函数法,例如
var person=new Object();
person.name="zhuzhenwei";
person.age=20;
- 对象字面量,例如
var person={
name:"zhuzhenwei",
age:18
};
对象字面量有以下一点需要注意的地方:
- 在末尾需加分号;表示结束。
- 在一个属性定义之后用逗号分隔,最后一个属性不需要
- 属性名可以加双引号,好处是我们可以包含错误的字符。并使用方括号表示法来访问对象的属性。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>对象字面量的扩展</title>
</head>
<body>
<script>
// 1. 属性简化
let createUser = (id, name, email) => ({
id: id,
name: name,
email: email,
});
let user = createUser(1, "admin", "admin@php.cn");
console.dir(user);
// 当对象中的属性与属性值变量同名时,可省去变量值
createUser = (id, name, email) => ({ id, name, email });
user = createUser(2, "peter", "peter@php.cn");
console.dir(user);
// 2. 方法简化
let site = {
siteName: "php中文网",
getSite: function () {
return this.siteName;
},
};
console.log(site);
// es6方法简化
let prop = "site Name";
// 属性变量化处理(本来sitename是对象字面化中的一个属性名,现在通过一个变量来传递)
site = {
// siteName: "php.cn",
prop: "php.cn",
// : function 去掉
getSite() {
return this.siteName;
},
};
console.log(site);
let prefix = "user-";
user = {
[prefix + "name"]: "Trump",
[`${prefix}email`]: "trump@php.cn",
};
console.log(user);
</script>
</body>
</html>
7. 闭包原理
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>闭包原理</title>
</head>
<body>
<script>
// mdn: 闭包是可以访问自由变量的函数
// c: 自由变量,相对于foo()中的变量而言
let c = 50;
function foo(a) {
// 函数中有二类变量是自己的
// 1. 函数参数: a
// 2. 私有变量: b
// 除此之外都是自由变量
let b = 10;
console.log(a, b);
console.log(c);
}
console.log(foo(30));
// <JavaScript权威指南>说过,从技术角度看,任何一个函数都 闭包
// 实际开发中, 闭包需要满足二个条件
// 1. 即使创建它的上下文已经销毁,它仍然存在(例如,内部函数从父函数中返回)
// 2. 在代码中引用了自由变量
// 简化一下
// 1. 函数中必须要有一个子函数,且返回这个函数
// 2. 在这个子函数中, 引用了父函数中的变量
// o:从这个角度讲,闭包也可以理解为“自成系统”的函数
</script>
</body>
</html>
8. 闭包的应用场景
- 总算到了自己一直关心的,怎么去访问函数中的私有变量。之前在分页那块这方面留了一个尾巴。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>闭包的应用场景</title>
</head>
<body>
<script>
// 1. 访问函数中的私有变量
let foo = () => {
// 私有变量
let username = "peter-zhu";
// 子函数(闭包), 当前的username相对于闭包(子函数get)来说是自由变量
let get = () => username;
// 必须返回这个闭包函数
return get;
};
// console.log(username);
let get = foo();
console.log(get);
console.log(get());
console.log("---------------------");
// 2. 快速创建多个全局可用的API
// 要求这个脚本返回四个接口api
let set, inc, dec, print;
let manager = (n) => {
let num = n;
// 设置器
set = (val) => (num = val);
// 递增器
inc = () => num++;
// 递减器
dec = () => num--;
// 打印
print = () => console.log(num);
};
// 初始化
manager(10);
print();
inc();
print();
set(20);
print();
dec();
print();
// 例如点赞就是递增,取消关注可以用递减等。
</script>
</body>
</html>