目录
简介
- ECMAScript 6是什么
- ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准,之前的JavaScript 标准是ES5,ES6在2015年6月正式发布了。它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。
- ECMAScript 和 JavaScript 的关系
- ECMAScript是JavaScript的标准, JavaScript是ECMAScript的实现。换句话说,如果说JavaScript是一门语言的话,那么ECMAScript就是这门语言遵循的语法书。
创建项目
新建文件夹 ES6,在VScode中打开(VScode安装插件 Chinese (Simplified)、vscode-icons、Live Server)
新建两个文件夹 src用于存放es6语法的代码,dist用于存放编译后的代码
打开VScode终端,使用命令
//意思是使用默认配置初始化项目
npm init -y
使用babel
安装 babel 终端使用命令(babel的作用是把es6的代码转译成es5)
// npm安装 非全局安装 babel客户端,babel的规则集,使用es2015(es6转换代码)
npm i --save-dev babel-cli babel-preset-es2015
测试 babel是否生效
配置babel,在根目录下新建.babelrc文件,编写转译规则集
在src目录下新建一个test.js文件,使用es6语法写一行代码
使用babel编译test.js,命令如下
// 非全局的babel所以要在目录中使用babel命令 es6文件 输出 输出位置
.\node_modules\.bin\babel .\src\test.js --out-file .\dist\test.js
发现报错,具体如下
解决办法,使用管理员运行VScode,执行命令
PS D:\Users\lixx\Desktop\es6> get-ExecutionPolicy
Restricted
PS D:\Users\lixx\Desktop\es6> set-ExecutionPolicy RemoteSigned
PS D:\Users\lixx\Desktop\es6> get-ExecutionPolicy
RemoteSigned
重新执行babel命令,发现在dist目录下生成了一个转译后的文件,转译成功
转译多个文件使用文件夹命令
自动转译
关键字 let、const
var在声明变量时的全局问题
// 在 for中声明的变量,我们在for结束后还可以使用输出的结果是10,造成内存泄漏问题
for (var i = 0; i<10; i++) {}
console.log(i);
var在声明变量时的局部作用域问题
// 会输出 undefined,按照我们原本的设想应该报Uncaught ReferenceError: b is not defined错误
!(function(){
console.log(b);
var b = 10;
})()
// 原因是浏览器在执行的时候会执行js变量提升的操作行为,会把代码转换成以下进行执行,可能造成我们预期之外的结果
!(function(){
var b;
console.log(b);
b = 10;
})()
块级作用域 { }
因为有上面的问题,所以在es6中出现了 块级作用域 { } 在花括号中定义的变量在外部是不能访问的
- let: 用来声明变量
- const: 用来声明一个只读的变量(值可以变,保证内存地址不可变)
- 变量声明的同时必须⽴即赋值
- 如声明的是简单类型的数据,变量的值不可改变
- 简单类型如字符串、数字和布尔值,值就保存在变量指向的内存地址。⽽复杂类型的数据如 对象、数组和函数,保证变量的引用地址是不变的,但是对象的堆内存地址的数据是会改变的
特点:只在声明的代码块中生效、暂时性死区、没有变量提升、不能重复声明
symbol数据类型
ES6之前JavaScript的数据类型
- Number(数字)
- String(字符串串)
- Boolean(布尔值)
- Object(对象)
- Null(空对象指针)
- Undefined(声明的变量量未被初始化时)
出现原因
- 对象的属性名容易产生命名冲突,为保证键名的唯一性,故es6引入Symbol这种新的原始数据类型,确保创建的每个变量都是独一无二的
用法
- Symbol类型的数据是类似字符串的数据类型,由于Symbol函数返回的值是原始类型的数据,不是对象,故Symbol函数前不能使用new命令,否则会报错。
let a1 = "a";
let a2 = "a";
console.log(a1 === a2); // true
let b1 = Symbol("b");
let b2 = Symbol("b");
console.log(b1 === b2); // false
let c1 = Symbol.for("c");
let c2 = Symbol.for("c");
console.log(c1 === c2); // true
// 一
let c1 = Symbol.for("c");
let obj = {
[c1]: 123, // 这里使用了 c1 变量,c1变量是Symbol
c1: 123,
c2: 123
}
console.log(obj); // {c1: 123, c2: 123, Symbol(c): 123}
// 二
let obj = {}
obj[c1] = 3;
obj.c2 = 4;
console.log(obj); // {c2: 4, Symbol(c): 3}
console.log(obj[c1]); // 3
// 三
let b1 = Symbol("b");
let obj = {}
Object.defineProperty(obj, b1, { value: 123 });
console.log(obj); // {Symbol(b): 123}
// 四
const user = Symbol("zhangsan");
console.log(user); // Symbol(zhangsan)
解构赋值
介绍
- 解构赋值可以理解为赋值操作的语法糖,它是针对数组或者对象进行模式匹配,然后对其中的变量进行赋值。代码书写上言简意赅,语义明确,也方便了对象数据的读取操作。
实质
- ES6中只要某种数据有Iterator接口(也就是可以循环迭代),都可以进行数组的解构赋值。
{
let a, b, c;
[a, b, c] = [1, 2]; // [] 类似模板语法
console.log(a, b, c); // 1 2 undefined
} // {} 是块级作用域
{
let a, b, c;
[a, b, c = 6] = [1, 2]; // c赋值的话就是赋值后的值,没有赋值就输出默认值6
console.log(a, b, c); // 1 2 6
}
{
let a, other;
[a, ...other] = [1, 2, 3, 4, 5, 6]; // a = 1, other 解构成了数组
console.log(a, other); // 1 [2, 3, 4, 5, 6]
}
{
let a, b;
[a,,,b] = [1,2,3,4]; // , 占位符
console.log(a,b); // 1 4
}
{
let a, b;
({ a, b } = { a: 1, b: 2 }) // 解构对象,使用{} ,外面要用 () 括起来
console.log(a, b); // 1 2
}
{
let num, total;
({ a: num, b: total } = { a: 3, b: 4 }); // 替换变量名
console.log(num, total); // 3 4
}
解构后端返回给我们的json数据,有可能后端的变量名不是我们想要的
{
// 假如这个是后端返回的数据
function fn() {
return {
"user_id": 1,
"nickname": "桑心得难人",
"mobile": 10086,
"photo_url": "https://123.jpg",
"info": {
"real_name": "张三"
},
"orderList": [
{
"order_id": 1
},
{
"order_id": 2
}
]
}
};
// 前端解构
let a = fn();
let { user_id: userId, photo_url: photoUrl, info: { real_name: realName }, orderList: [{ order_id: orderId }] } = a;
console.log(userId, photoUrl, realName, orderId); // 1 'https://123.jpg' '张三' 1
}
字符串的扩展方法以及模板字符串
在es6中使用{}来处理超过范围的Unicode字符
{
const str1 = "\u20bb7";
console.log(str1); // ₻7 es5乱码
const str2 = "\u{20bb7}";
console.log(str2); // 𠮷 es6正确输出
}
在字符串for循环中,es6使用for-of处理Unicode字符
{
const str = "\u{20bb7}";
for (let i = 0; i< str.length; i ++) {
console.log(str[i]); // � � es5 乱码
}
for (let s of str) {
console.log(s); // 𠮷 es6正确输出
}
}
字符串的一些判断操作
{
let str = "123zhangsan456";
// 判断字符串中是否包含指定的字符串
console.log(str.includes("zhangsan")); // true
// 判断字符串是否是以指定的字符串开头
console.log(str.startsWith("zhangsan")); // false
// 判断字符串第几位是否是以指定的字符串开头
console.log(str.startsWith("zhangsan", 3)); // true
// 判断字符串是否是以指定的字符串结尾
console.log(str.endsWith("456")); // true
// 将字符串重复拼接指定的次数
str = str.repeat(2);
console.log(str); // 123zhangsan456123zhangsan456
}
{
// 字符串头部补充指定的字符串
let str1 = "00";
str1 = str1.padStart(8, "*");
console.log(str1); // ******00
// 字符串尾部补充指定的字符串
let str2 = "11";
str2 = str2.padEnd(8, "*");
console.log(str2); // 11******
}
{
// 拼接字符串
const username = "zhangsan";
const age = 18;
const es5 = "我叫"+username+", 今年"+age;
const es6 = `我叫${username}, 今年${age}`;
console.log(es5);
console.log(es6);
}
数组扩展与运算符扩展
扩展运算符 "..." 三个点
{
// 复制list数组,到一个新的数组, // 改变原来的数组,新的数组也不会改变
const list = [1, 2, 3, 4];
let list2 = [...list];
list.push(5); // 改变原来的数组
console.log(list); // [1, 2, 3, 4, 5]
console.log(list2); // [1, 2, 3, 4]
}
{
// 分隔数组
const totalList = [1, "a", "b", "c"];
let [, ...strList] = totalList; // , 占位表示丢弃数组的第一个下标
console.log(strList); // ['a', 'b', 'c']
}
{
function add(x, y) {
return x + y;
}
let addList = [1, 2];
console.log(add(...addList)); // 3
}
{
// fill 给数组里面填充数据
const list = [1, 2, 3, 4, 5];
let list2 = [...list].fill(3); // fill 替换数据为3
console.log(list2); // [3, 3, 3, 3, 3]
let list3 = [...list].fill('a', 1, 3); // fill("替换成的数据", "替换开始的下标", "替换下标从0开始到指定的替换数")
console.log(list3); // [1, 'a', 'a', 4, 5]
}
{
// find findIndex 搜索数组数据
const list = [{ title: "es6" }, { title: "webpack", id: 2 }, { title: "webpack", id: 3 }];
let result = list.find(function (item) {
return item.title === 'webpack'; // 找到第一个符合条件的数据就返回
});
console.log(result); // {title: 'webpack', id: 2}
let resultIndex = list.findIndex(function (item) {
return item.title === 'webpack'; // 找到第一个符合条件的数据返回数据所在的下标
});
console.log(resultIndex); // 1
}
{
// includes
const list = [1, 2, 3, 4, 5, 6];
let result = list.includes(2); // 判断数组中是否包含指定的数据
console.log(result); // true
}
{
// flat 展开数组操作
const list = [1, 2, 3, [4, 5, 6, [7, 8, 9]]];
let flatList = [].concat(...list); // 多层嵌套需要使用循环
console.log(flatList); // [1, 2, 3, 4, 5, 6, Array(3)]
let flatList2 = list.flat(2); // 指定展开的层数,不传默认只展开一层
console.log(flatList2); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
}
{
// 复制对象 // 适用于基本类型对象,扩展运算符复制的对象中有对象的话,里面的对象复制的是对象的指针(原对象中的对象改变,复制后的对象也会改变)
const obj = { name: "zhangsan", age: 18 };
let newObj = { ...obj }; // ...复制对象, es8的语法,如果报错,需要安装transform-object-rest-spread插件
console.log(newObj); // {name: "zhangsan", age: 18}
}
{
// 复制对象时,改变对象的值
const obj1 = { name: "zhangsan", age: 18 };
let obj2 = { ...obj1, name: "lisi" }
console.log(obj2); // {name: 'lisi', age: 18}
}
{
// 合并对象
const obj1 = { name: "zhangsan", age: 18 };
const obj2 = { sex: 1 }
let obj3 = { ...obj1, ...obj2 };
console.log(obj3); // {name: 'zhangsan', age: 18, sex: 1}
}
复制对象如果报错Unexpected token
需要安装插件transform-object-rest-spread,使用命令
npm i babel-plugin-transform-object-rest-spread
在.babelrc文件中配置
map、reduce
map
{
// json数组
const json = [{ title: "es6", status: 1 }, { title: "es7", status: 0 }, { title: "es8", status: 1 }];
let map = json.map(function (item) { // 使用 map
return { statusTxt: item.status ? "正常" : "禁用" } // 这种每个变量都要重新定义,不然新对象中没有这个变量
});
console.log(json); // {title: 'es6', status: 1} {title: 'es7', status: 0} {title: 'es8', status: 1}
console.log(map); // {statusTxt: '正常'} {statusTxt: '禁用'} {statusTxt: '正常'}
}
{
// json数组
const json = [{ title: "es6", status: 1 }, { title: "es7", status: 0 }, { title: "es8", status: 1 }];
let map = json.map(function (item) { // 使用 map
let obj = {};
Object.assign(obj, item); // 使用Object.assign方法,把item的数据复制给一个新的对象
obj.status = item.status ? "正常":"禁用";
return obj;
});
console.log(json); // {title: 'es6', status: 1} {title: 'es7', status: 0} {title: 'es8', status: 1}
console.log(map); // {title: 'es6', status: '正常'} {title: 'es6', status: '禁用'} {title: 'es6', status: '正常'}
}
reduce
{
/**
* 统计每个字符在字符串中出现的次数
*
* reduce 对数组中的每个元素进行一次回调,升序执行然后将回调值汇总一个返回值
* @params fn(acc, currentValue, currentIndex, Array), initalValue
*/
const letterList = 'aaba3ceseee';
const result = letterList.split('').reduce(function (acc, cur) {
acc[cur] ? acc[cur]++ : acc[cur] = 1;
return acc;
}, {});
console.log(result); // {3: 1, a: 3, b: 1, c: 1, e: 4, s: 1}
}
{
//展开多层数组
const list = [1, ['2nd', 2, 3, ['3rd', 4, 5]], ['2nd', 6, 7]];
const deepFlat = function (list) {
return list.reduce(function (acc, cur) {
return acc.concat(Array.isArray(cur) ? deepFlat(cur) : cur);
}, []);
}
let flatList = deepFlat(list);
console.log(flatList); // [1, '2nd', 2, 3, '3rd', 4, 5, '2nd', 6, 7]
}
语法糖
{
let name = 'zhangsan';
let age = 18;
// es5定义json对象的方式
var es5Obj = {
name: name,
age: age,
sayHello: function () {
console.log('this is es5Obj')
}
}
// es6简化版的方式
let es6Obj = {
name,
age,
sayHello() {
console.log('this is es6Obj')
}
}
}
{
let key = 'name';
// es5语法
let es5Obj = {}
es5Obj[key] = 'zhangsan'
console.log(es5Obj); // {name: 'zhangsan'}
// es6 语法
let es6Obj = {
[key]: 'lisi'
}
console.log(es6Obj); // {name: 'lisi'}
}
{
// Object.is() 判断两个对象是否相等,和'==='类似,唯一的区别是判断NaN的不同
console.log(Object.is(NaN, NaN)); // true
console.log(NaN === NaN); // false
}
{
// Object.assign() 复制对象,外层对象深拷贝,内层对象浅拷贝:原对象改变后新对象也会改变
const sourceObj = { name: '小米', age: 18, info: { height: 180 } };
let targetObj = {};
Object.assign(targetObj, sourceObj);
sourceObj.info.height = 160;
sourceObj.age = 16;
console.log(targetObj); // {name: '小米', age: 18, info: { height: 160 }}
}
{
// Object.keys() 遍历对象的 key
const json = { name: "zhangsan", age: 18 };
let obj = {};
for (const key of Object.keys(json)) {
console.log(key); // 1 name 2 age
obj[key] = json[key];
}
console.log(obj); // {name: 'zhangsan', age: 18}
}
{
// Object.values() 遍历对象的value
const json = { name: "zhangsan", age: 18 };
for (const value of Object.values(json)) {
console.log(value); // 1 zhangsan 2 18
}
}
{
// Object.entries() 遍历对象的 键和值
const json = { name: "zhangsan", age: 18 };
for (const entrie of Object.entries(json)) {
console.log(entrie); // 1 ['name', 'zhangsan'] 2 ['age', 18]
}
}
Map、WeakMap
- Map
- JavaScript中的对象,实质就是键值对的集合(Hash结构),但是在对象里却只能用字符串作为键名。在一些特殊的场景里就满足不了我们的需求了,正因为此, Map这一数据提出了,它是JavaScript中的一种更完善Hash结构。
{
let map1 = new Map();
map1.set("name", "zhangsan"); // 添加元素
map1.set("name", "zhangsan").set("age", 18) // 链式
let map2 = new Map([["name", "zhangsan"], ["age", 18]]); // 在创建对象时初始化元素
console.log(map2.size); // 查询map中有多少键值对
console.log(map2.get("name")); // 读取key的value
console.log(map2.has("sex")); // 判断map中是否存在指定的key, true:存在,false:不存在
map2.delete("age"); // 删除map中指定的元素
map2.clear(); // 移除map中的所有元素
}
{
// 遍历map
let map = new Map([
["name", "zhangsan"],
["age", 18]
]);
for (const key of map.keys()) { // keys() 遍历map的 key
console.log(key);
}
for (const value of map.values()) { // values() 遍历map的value
console.log(value);
}
for (const entrie of map.entries()) { // entries() 遍历map的键值,可以省略entries()
console.log(entrie);
}
}
- WeakMap
-
是一组键/值对的集合,其中的键是弱引用的。其键必须是对象,而值可以是任意的。
-
因为键名所指的对象不触发垃圾回收机制,当document(使用场景一般是绑定document)不存在时这个map也就销毁了
-
没有clear, 没有size, 无法遍历(因为是弱引用)
<!-- html代码 -->
<div id="test"></div> // 当 test 存在的时候,下面的js代码正常执行。当 test 不存在的时候,下面的js代码报错 TypeError: Invalid value used as weak map key,因为 test的document对象已经不存在了
// js代码
{
let ulObj = document.getElementById('test')
let weakmap = new WeakMap([
[ulObj, 10]
])
console.log(weakmap)
}
Set、WeakSet
- set
- 是ES6给开发者提供的一种类似数组的数据结构,可以理解为值的集合。可以按照插入的顺序迭代它的元素。它和数组的最大的区别就在于: 它的值不会有重复项。
{
let set1 = new Set();
set1.add(1); // 添加元素
set1.add(1).add(2) // 链式
let set2 = new Set(['a', 'b', 'c']); // 在创建对象时初始化元素
console.log(set2.size); // 查询set中有多少个元素
console.log(set2.has('a')); // 判断set中是否存在指定的元素, true:存在,false:不存在
set2.delete('a'); // 删除set中指定的元素
set2.clear(); // 移除set内的所有元素
}
{
let set = new Set(['a', 'b', { 'username': 'zhangsan' }]);
for (const item of set) { // 遍历set中的元素, 同 values()、keys()相等
console.log(item); // 按插入顺序输出
}
for (const entrie of set.entries()) {
console.log(entrie); // 输出 键值相同的数据
}
}
- WeakSet
- 数组成员必须是对象
- WeakSet结构也提供了add()方法,delete()方法,has()方法给开发者使用,作用与用法跟Set结构完全一致。
- WeakSet结构不可遍历。因为它的成员都是对象的弱引用,随时被回收机制回收,成员消失。所以WeakSet 结构不会有keys(),values(),entries(),forEach()等方法和size属性。
Map、Set、Array、Object 的区别
{
let array = []
let obj = {}
let map = new Map()
let set = new Set()
const user = { username: 'zhangsan' }
// 增加
array.push(user)
obj['username'] = 'zhangsan'
map.set('username', 'zhangsan')
set.add(user)
console.log('add', array, obj, map, set)
// 查询判断是否存在
const resultArray = array.includes(user)
const resultObj = 'username' in obj
const resultMap = map.has('username')
const resultSet = set.has(user)
console.log('search', resultArray, resultObj, resultMap, resultSet)
// 修改
array.forEach(function (item) {
item.username = item.username ? 'lisi' : ''
})
obj['username'] = 'lisi'
map.set('username', 'lisi')
set.forEach(function (item) {
item.username = item.username ? 'lisi' : ''
})
console.log('update', array, obj, map, set)
// 删除
const index = array.findIndex(function (item) {
return item.username
})
array.splice(index, 1)
delete obj.username
map.delete('username')
set.delete(user)
console.log('delete', array, obj, map, set)
}
{
// 类型转换
// 定义一个对象
let obj = { 'username': 'zhangsan', 'age': 18 };
// 对象转map
let map = new Map(Object.entries(obj));
// map转对象
let obj2 = Object.fromEntries(map);
// 数组转set
let array = [1, 2, 3];
let set = new Set(array);
// set转数组
let array2 = Array.from(set);
}
代理Proxy、反射Reflect
Proxy
{
// 定义一个对象,例如是后端返回给我们的对象
let user = { id: 1, username: "admin", age: 18, createTime: 2021, _password: '123456' };
// 代理对象
let userProxy = new Proxy(user, {
/**
* 读取操作
* @param {目标对象} target
* @param {目标对象的键} key
*/
get: function (target, key) {
switch (key) {
case "username":
return target[key] + "_123"; // 读取,如果是指定是字段‘username’ 就在后面加上指定的字符
default:
return target[key];
}
},
/**
* 设置操作
* @param {目标对象} target
* @param {目标对象的键} key
* @param {目标对象的值} value
*/
set: function (target, key, value) {
if (Object.is(key, "id")) { // 设置,如果是指定的字段,就跳过设置,原样输出
return target[key]; // 原样输出
} else {
return target[key] = value; // 输出设置后的值
}
},
/**
* 判断 in 操作
* 判断对象中的属性是否存在
*
* @param {目标对象} target
* @param {目标对象的键} key
* @returns true:存在,false:不存在
*/
has: function (target, key) {
if (key in target) {
console.log(`${key}:`, target[key])
return true
} else {
console.log('并无此属性')
return false
}
},
/**
* 删除操作
* @param {目标对象} target
* @param {目标对象的键} key
* @returns true:删除成功,false:删除失败
*/
deleteProperty: function (target, key) {
if (key.indexOf('_') === 0) {
console.warn('私有属性不能被删除')
return false
} else {
delete target[key]
return true
}
},
/**
* 拦截Object.keys()
* @param {目标对象} target
* @returns
*/
ownKeys(target) {
return Object.keys(target).filter(function (item) { // 遍历 对象target中的所有键
return item !== 'id' && item.indexOf('_') !== 0 // 过滤掉id和_开头的属性
})
}
});
// 调用读取
console.log(userProxy.username); // admin_123 通过[对象.变量]的方式读取,不要通过[对象]的方式
// 设置调用
userProxy.id = 2;
userProxy.age = 30
console.log(userProxy.id, userProxy.age); // 1 30
// 判断 in 操作
console.log('username' in userProxy); // true // 判断 userProxy 对象中是否存在属性 username
// 删除操作
console.log(delete userProxy['_password']); // false // Uncaught TypeError: 'deleteProperty' on proxy 这个错误在es5会出现,es6中不会出现
// 拦截Object.keys()操作
console.log(Object.keys(userProxy)); // ['username', 'age', 'createTime']
}
Reflect
{
// Reflect 所有Object的方法和Proxy的方法,Reflect中都有,
// 编程的过程中推荐使用 Reflect函数式编程的方式 操作对象
// 创建一个对象
let user = { id: 1, username: "admin", age: 18, createTime: 2021 };
// get
console.log(Reflect.get(user, 'username')); // admin // 字段需要使用 引号'' 引起来
// set
Reflect.set(user, 'username', 'zhangsan');
console.log(user.username);
// 判断 has
console.log(Reflect.has(user, 'username')); // true // Object中的语法 'username' in user
}
使用Proxy和Reflect实现双向数据绑定,
ps:请教,我没看懂txtObje 和 objProxy是怎么代理起来的,它们好像没有关系?有看懂的同学评论区解答一下,谢了
<!-- html -->
<body>
<input type="text" id="input"> // input输入内容后, i 中的内容会跟着改变
<h2>输入的值:<i id="txt"></i></h2> // i 的值改变后,input的内容也会改变
<script src="src/js.js"></script>
</body>
// js
{
// 获取dom元素
const inputObj = document.getElementById("input");
const txtObj = document.getElementById("txt");
// 初始化对象
const obj = {};
// 代理选项
const handler = {
get: function (target, key) {
return Reflect.get(target, key);
},
set: function (target, key, value) {
if (key === 'text') {
inputObj.value = inputObj.value === value ? inputObj.value : value;
txtObj.innerHTML = value;
}
return Reflect.set(target, key, value);
}
}
// 代理对象
const objProxy = new Proxy(obj, handler);
// 给 input元素 添加键盘键入事件
inputObj.addEventListener('keyup', function(e) {
objProxy.text = e.target.value;
});
}
函数参数默认值
{
function es6(x, y = 'world') {
console.log('es6', x + y);
}
es6('hello', ''); // es6 hello
}
函数参数可变长度
{
// 使用扩展运算符 ...
function add(...rest) {
let sum = 0;
for (const value of rest) {
sum += value;
}
console.log(sum);
}
add(1, 2, 3, 4, 5);
}
函数尾调用(替换递归)
{
function step2 (x) {
console.log('尾调用', x);
}
// 一个函数的返回结果是另一个函数
function step1 (x) {
return step2(x);
}
step1(1);
}
箭头函数
{
function arrow (x) {
console.log('es5 函数 ', x);
}
arrow('5');
}
{
// 声明函数,箭头函数是匿名函数
const arrow = (x) => {
console.log('es6 箭头函数 ', x);
}
arrow(6); // 调用
}
箭头函数,只有一个参数的时候,参数的 () 可以省略。函数体 只有一行代码的时候 {} 可以省略
{
// 未省略
const arrow = (x) => {
return x * 2;
}
const result = arrow(6); // 调用
console.log(result);
// 省略
const arrow2 = x => x * 2;
console.log(arrow2(6));
}
箭头函数不绑定 this,或者绑定的对象不会变换
setTimeout 是 windows的函数,会改变this的指向,所以没得指 /摊手
使用箭头函数,this 依旧指向对象本身
类
es5的方式
{
// es5 的方式
// 类构造函数
function Person(name, age) {
this.name = name;
this.age = age;
}
// 类中的方法
Person.prototype.sayHello = function () {
console.log("hello,我是 es5 方式的类中的方法");
}
const p = new Person('小明', 18);
console.log(p);
p.sayHello();
}
es6的方式
引入了新的关键字class
{
// es6 的方式,引入了新的关键字 class
class Person {
// 构造函数
constructor(name, age) {
this.name = name // 不用加逗号,
this.age = age
} // 不要加逗号,
// 类中的方法
sayHello () {
console.log("hello,我是 es6 方式的类中的方法");
}
}
const p = new Person('小花', 17);
console.log(p);
p.sayHello();
}
类的 get set
{
// 引入关键字 get set
// 类中的 get set 属性,注意是属性,不是方法
class Person {
constructor(name = '小红') {
this.name = name
}
get attrName () { // get set 的属性名称自定义,但是不能和要修改的属性名称相同
return this.name;
}
set attrName(name) {
this.name = name
}
}
const p = new Person();
console.log('get set', p.attrName); // 小红
p.attrName = '小明'
console.log('get set', p.attrName); // 小明
}
类的静态方法
{
// 类的静态方法
class Person {
constructor(name = '小红') {
this.name = name
}
static sayHello () {
console.log('hello, 我是 es6 的静态方法');
}
}
// 静态方法,可以 类名.方法名 进行调用,不需要使用new
Person.sayHello();
}
类的静态属性
es6的方式
{
// 类
class Person {
constructor(name = '小红') {
this.name = name
}
}
// 静态属性
Person.age = 18;
console.log(Person.age);
}
es7的方式
需要使用插件
npm i babel-preset-es2017
{
// 类
class Person {
constructor(name = '小红') {
this.name = name
}
static age = 18
}
console.log(Person.age);
}
类的继承
引入了关键字 extends
{
// 父类
class Person {
constructor(name = '小红') {
this.name = name
}
}
// 子类
class Child extends Person {
}
console.log(new Child()); //{name: '小红'}
}
{
// 父类
class Person {
constructor(name = '小红') {
this.name = name
}
}
console.log(new Person()); // {name: '小红'}
// 子类
class Child extends Person {
constructor(name, age) {
super(name) // 子类的构造方法中必须调用父类的构造
this.age = age
}
}
console.log(new Child('小黄', 16)); // {name: '小黄', age: 16}
}
遍历对象
{
// 应用场景
const obj = {
color: 'red',
price: 18,
size: 'small',
[Symbol.iterator]() {
let index = 0
const values = Object.values(this)
return {
next() {
if (index < values.length) {
return {
value: values[index++],
done: false
}
} else {
return {
done: true
}
}
}
}
}
}
for (const value of obj) {
console.log(value) // red 18 small
}
}
js 模块化( import 、export )
A 写了一些 js 代码,B 想调用 A 的代码
A 的 js 代码 首先要 export 导出
B 要 import 导入 A 的代码
方式一
export
// a.js 文件
// 导出一个变量
export let a = 3;
// 导出 一个方法
export function sayHello() {
console.log('我是方法');
}
// 导出一个类
export class Test {
sayTest () {
console.log('我是类方法');
}
}
import
<script src="src/b.js" type="module"></script> <!-- 使用模块化,需要加 type="module" -->
// b.js 文件
import { a, sayHello , Test} from "./a.js";
console.log(a);
sayHello();
const test = new Test();
test.sayTest();
使用 * 星号通配符,引入所有模块
import * as test from "./a.js";
console.log(test.a)
方式二
export default
// a.js 文件
let a = 3;
function sayHello() {
console.log('我是方法');
}
class Test {
sayTest() {
console.log('我是类方法');
}
}
// 导出
export default {
a, // 导出 变量
sayHello, // 导出 方法
// Test // 类不导出
}
import
// b.js 文件
import test from "./a.js";
console.log(test.a)
test.sayHello();
异步调用
Promise
示例一
{
// es6 提供了 Promise 异步调用对象
// Promise 对象构造函数接收两个方法:
// 第一个方法:resolve 解决(成功方法)
// 第二个方法:reject 拒绝(失败方法)
const p = new Promise((resolve, reject) => {
// ... 代码省略,先执行这里的代码
resolve(); // 然后再执行 then 里面的代码
// reject();
});
// Promise 对象提供了 then() 回调函数,用于接收 resolve、reject 参数
p.then(() => {
console.log("解决了");
}, () => {
console.log("拒绝了");
})
}
模糊知识点:调用 then 方法后,会先执行 Promise对象里面的代码,执行完成后,才会执行对应的resolve、reject函数
示例二
{
// 创建 Promise
const fun = (num) => {
return new Promise((resolve, reject) => {
if (num == 200) {
// 成功了
resolve();
} else {
// 失败了
const err = new Error('我是异常信息');
reject(err);
}
});
}
// 调用
fun(500).then(() => {
console.log("200 成功了");
}, e => {
console.log('打印异常信息', e.message);
console.log("500 失败了");
});
// 或者可以使用 catch 捕获异常
/* fun(500).then(() => {
console.log("200 成功了");
}).catch(e => {
console.log('打印异常信息', e.message);
console.log("500 失败了");
}); */
}
实例三:请求真实接口
// 封装一个 post 请求方法
// 参数 url:请求地址,例如:http://localhost:8080/test
// 参数 data:请求数据,例如{name:"张三"}
let post = (url, data) => {
return new Promise((resolve, reject) => {
axios({
method: 'post',
url: url,
data: data,
}).then((response) => {
// 接口请求成功
resolve(response.data);
}).catch((error) => {
// 接口请求失败
reject(error);
})
});
}
----------------------------------------------华丽分割线------------------------------------------------
// 调用发起 post 接口请求
let url = "http://localhost:8080/test"
let data ={name:"zhangsan"}
post(url, data).then((res) => {
// 打印返回结果
console.log("res = ", res);
}, (e) => {
// 打印失败信息
console.log(e);
})
Promise.all(数组).then() : 接收多个Promise对象,当所有的Promise对象执行成功后,继续向下执行all的then,参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例,不是的话会转为Promise实例
Promise.race(数组).then() :接收一组Promise对象,当只有一个Promise对象执行成功后,就可继续向下执行race的then
Generator
使用 Generator 需要安装组件,安装命令如下
npm install --save babel-polyfill
学习 Generator 之前要先了解下 iterator 迭代,iterator 是一种接口,目的是为了给不同的数据结构提供统一的循环方式,任何数据结构如果部署了Iterator接口,就能够实现遍历的操作。Array、String、Set、Map、函数的argument对象 具备原生的Iterator接口,可以进行变量。Object如果需要遍历的话,需要自己实现
// iterator 示例代码
{
const arr = [1,2,3];
const obj = arr[Symbol.iterator]();
console.log(obj.next()); // {value: 1, done: false}
console.log(obj.next()); // {value: 2, done: false}
console.log(obj.next()); // {value: 3, done: false}
console.log(obj.next()); // {value: undefined, done: true}
}
开始学习 Generator
{
// 创建一个 Generator 函数
// Generator 的 语法标志 是 function*
// 函数里面 通过关键字 yield 赋值
const say = function* () {
yield 'a'
yield 'b'
yield 'c'
}
// 创建一个 Generator 对象
const obj = say();
// 输出 , 这个结果 和 iterator 结构一致
console.log(obj.next()); // {value: 'a', done: false}
}
Generator 应用场景一:遍历对象
{
// 有一个对象
let obj = {
a:1,
b:2,
c:3
}
// 给对象中添加一个 Generator 方法,这样这个对象就具有了 iterator 迭代功能
obj[Symbol.iterator] = function * () {
for (const key of Object.keys(obj)) {
yield obj[key]
}
}
// 遍历对象结果
for (const value of obj) {
console.log(value); // 1 2 3
}
}
Generator 应用场景二:长轮询
{
// 长轮询
function fn1() {
return new Promise(resolve => {
setTimeout( () => {
console.log('查询中')
resolve({code: 0})
},1000)
})
}
// 创建 Generator 方法
const getStatus = function* () {
yield fn1()
}
function autoGetStatus() {
const gen = getStatus() // 创建 Generator 实例
const status = gen.next() // 调用 Generator 方法,得到 结果
status.value.then(res => {
if(res.code === 0) {
console.log('用户付款成功')
} else {
console.log('暂未付款')
setTimeout( () => autoGetStatus(), 500)
}
})
}
autoGetStatus()
}
Generator 应用场景三:异步同步化
需求:
{
// 需求,我想让 1 先进行打印,然后再打印 2 , 但是实际执行结果是 2 1
function test() {
setTimeout(()=>{
console.log("1");
}, 100);
console.log("2");
}
test();
}
实现:
{
const ajax = function* () {
console.log('1')
yield function (cb) {
setTimeout(() => {
console.log('2')
cb && cb()
}, 1000)
}
console.log('3')
}
const runAjax = ajax()
const first = runAjax.next() // 1
first.value(() => runAjax.next()) // 2 3
}
async await
async
async作用是声明一个异步函数,表示这个函数是异步函数。
被async定义的函数会默认返回一个Promise对象resolve的值。因此对async函数可以直接then,返回值就是then方法传入的函数。
{
async function fn1() {
console.log(1);
return 1;
}
fn1().then(res =>{
console.log(res);
})
}
await
await 也是一个修饰符,只能放在async定义的函数内。可以理解为等待。
await 修饰的如果是Promise对象:可以获取Promise中返回的内容(resolve或reject的参数),且取到值后语句才会往下执行;
如果不是Promise对象:把这个非promise的东西当做await表达式的结果。
需求:有如下代码,我想 1 执行完成后,再执行 2 , 2 执行完成后,再执行 3。但是实际结果是 123 同时执行
{
function fn1() {
setTimeout(() => {
console.log('任务1')
},1000)
}
function fn2() {
setTimeout(() => {
console.log('任务2')
},1000)
}
function fn3() {
setTimeout(() => {
console.log('任务3')
},1000)
}
function init() {
fn1()
fn2()
fn3()
}
init()
}
使用 async await 实现 异步函数达到同步的效果
{
function fn1() {
return new Promise(resolve => {
setTimeout(() => {
console.log('1')
resolve() // 这里调用 resolve 或者 reject,await 才会向下继续执行
},1000)
})
}
function fn2() {
return new Promise(resolve => {
setTimeout(() => {
console.log('2')
resolve()
},1000)
})
}
function fn3() {
return new Promise(resolve => {
setTimeout(() => {
console.log('3')
resolve()
},1000)
})
}
async function init(fn1,fn2,fn3) {
await fn1()
await fn2()
await fn3()
}
init(fn1, fn2, fn3)
}