ECMAScript6 是 JavaScript 的标准,目前浏览器的 JavaScript 大多数都支持 ES6。ES6 使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。
var,let 和 const
- var 和 let 都可用于定义变量,但是 let 定义的变量只能在一个作用域内使用
{
var demo_1 = "demo_1";
let demo_2 = "demo_2";
}
console.log(demo_1);
// 可以打印出来demo_1
console.log(demo_2);
// 不可以打印出来demo_2
- const 定义不可变量
const demo_3 = "demo_3";
demo_3 = "demo_4";
// 会提示报错
// 如果 const 的是一个对象,对象所包含的值是可以被修改的。
// 抽象一点儿说,就是对象所指向的地址不能改变,而变量成员是可以修改的。
js 在引擎扫描代码时,如果发现变量声明,用 var 声明变量时会将声明提升到函数或全局作用域的顶部。但是 let 或者 const,会将声明关进一个小黑屋也是 TDZ(暂时性死区),只有执行到变量声明这句语句时,变量才会从小黑屋被放出来,才能安全使用这个变量
字符串模板
- 告别用 + 和 \ 拼接字符串
// 在ES5时我们通过反斜杠(\)来做多行字符串或者字符串一行行拼接
// ES6直接通过``拼接
const str = "demo";
console.log(`
test ${str}!!
`);
// 显示test demo!!
- 其他好用的方法
// includes 判断字符串是否包含指定值
const str = "jacksonzhou";
console.log(str.includes("zh")); // true
// repeat 获取字符串重复的次数
const str = "z";
console.log(str.repeat(3)); // 'zzz'
//如果你带入小数, Math.floor(num) 来处理
// s.repeat(3.1) 或者 s.repeat(3.9) 都当做成 s.repeat(3) 来处理
// startsWith 和 endsWith 判断是否以给定文本开始或者结束
const str = "hello world!";
console.log(str.startsWith("hello")); // true
console.log(str.endsWith("!")); // true
// padStart 和 padEnd 填充字符串,应用场景:时分秒
setInterval(() => {
const now = new Date();
const hours = now.getHours().toString();
const minutes = now.getMinutes().toString();
const seconds = now.getSeconds().toString();
console.log(
`${hours.padStart(2, 0)}:${minutes.padStart(2, 0)}:${seconds.padStart(
2,
0
)}`
);
}, 1000);
- 面试题
- 模拟一个模板字符串的实现
- 实现标签化模板(自定义模板规则)
函数扩展
- 函数简写 (箭头函数)
- 不需要 function 关键字来创建函数
- 可省略 return 关键字
- 继承当前上下文的 this 关键字
- 没有 this、super、arguments 和 new.target 绑定,这些值由继承的上下文决定
- 不存在 prototype 这个属性
//例如:
[1, 2, 3]
.map((x) => x + 1)
[
//等同于:
(1, 2, 3)
].map(
function (x) {
return x + 1;
}.bind(this)
);
什么情况下不适用箭头函数? 当要求动态上下文的时候,你就不能使用箭头函数,比如:定义对象方法、定义原型方法、定义构造函数、定义事件回调函数 什么时候你不能用箭头函数
- 带默认参数
// es6之前,定义默认参数的方法是在一个方法内部定义
var link = function (width, height) {
var width = width || 100;
var height = height || 100;
};
// 而在es6中你可以这样写
var link = function (width = 100, height = 100) {};
// 做参数的非空检测
const notNull = () => {
throw new Error("参数不能为空");
};
const func = (id = notNull()) => {
console.log(`${id}`);
};
func(1);
func();
展开运算符 …
// 数组
let arr = [1, 2];
console.log(...arr, 3, 4, 5);
// 打印出1,2,3,4,5
const number = [1, 2, 3, 4, 5];
const [first, ...rest] = number;
console.log(rest); //2,3,4,5
// 对象
const alp = { fist: "a", second: "b" };
const alphabets = { ...alp, third: "c" };
console.log(alphabets); //{ "fist": "a", "second": "b", "third": "c"
// 函数参数的应用
const add_4 = (num1, num2) => num1 + num2;
console.log(add_4(...arr));
// 展开1和2,分别作为num1和num2的参数,打印出3
// 对于对象而言,组合成新的对象时,如果有重复的属性名,后者会覆盖前者
const first = {
a: 1,
b: 2,
c: 6,
};
const second = {
c: 3,
d: 4,
};
const total = { ...first, ...second };
console.log(total); // { a: 1, b: 2, c: 3, d: 4 }
对象扩展
- 对象简写
const demo = "objs";
const obj = {
name: "obj",
func:function() {
return: 1;
},
demo, // 相当于demo: demo,即键值对重名简写
}
console.log(obj)
// 打印出来的对象不含有objs属性,而是demo:"objs"
// 必须在外面定义 obj[demo] ="jack"才能使对象具有objs属性
const demo = "objs";
const obj = {
name: "obj",
func() {
return 1;
},
[demo]: "jack",
[demo+"2"]: "jackson",
}
console.log(obj)
// 打印出来的对象含有objs属性和objs2属性,即objs:"jack"
-
对象方法
- ES6 对象提供了 Object.assign()这个方法来实现浅复制。
const objA = { name: "cc", age: 18 }; const objB = { address: "beijing" }; const objC = {}; // 这个为目标对象 const obj = Object.assign(objC, objA, objB); // 我们将 objA objB objC obj 分别输出看看 console.log(objA); // { name: 'cc', age: 18 } console.log(objB); // { address: 'beijing' } console.log(objC); // { name: 'cc', age: 18, address: 'beijing' } console.log(obj); // { name: 'cc', age: 18, address: 'beijing' } // 是的,目标对象ObjC的值被改变了。 // so,如果objC也是你的一个源对象的话。请在objC前面填在一个目标对象{} Object.assign({}, objC, objA, objB);
-
面试题
// 请使用 ES6 重构一下代码
// 第一题
var jsonParse = require("body-parser").jsonParse;
// 答
import { jsonParse } from "body-parser";
// 第二题
var body = request.body;
var username = body.username;
var password = body.password;
// 答
const {
body,
body: { username, password },
} = request;
// 这个在配合async和await处理异步请求时很好用
解构赋值
- 数组赋值
let arr = [1, 2];
let [num1, num2] = arr;
<!--num1和num2分别赋值为1和2-->
- 对象赋值
let obj = {num: 1, type: "对象"};
let {num, type} = obj;
<!--num和type分别赋值为1和"对象"-->
class 类(对象封装的语法糖)
原理实现可看 从 js 看面向对象的封装、继承和多态
class Person {
constructor(name) { // name为实例传入的参数
this.name = name
}
hello() {
return 'Hello, I am ' + this.name + '.'
}
}
class Actor extends Person {
hello() {
return super.hello() + ' I am an actor.'
}
}
var jacksonzhou = new Actor('jacksonzhou')
jacksonzhou.hello()
// 定义类的变量名
const testClass = class demo{
constructor(){}
}
// 可省略类名
const testClass = class {
constructor(){}
}
// 立即执行的类
const testClass = new class {
constructor(name,age){
console.log(name,age)
}
}("jacksonzhou",24)
- constructor(arguments)
constructor中定义的属性称为实例属性
constructor外声明的属性定义在原型上,即原型属性
使用new创建实例对象时,自动调用这个函数,传入参数,只能有一个constructor
- extends
ES5的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))
ES6的继承,实质是先创造父类的实例对象this(所以必须先调用super方法),然后再用子类的构造函数修改this
- super
1.子类constructor中需要执行super(arguments),要放在头部
2.因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工
3.通过使用super().methods 引用父类方法或属性
4.super作为对象时,指向父类的原型对象(super.getName())
模块化
将模块标准(AMD,CommonJS,RequireJS)化为通用格式
- import, import{}, export, export default
1.当用export default people导出时,就用 import people 导入(不带大括号)
2.当用export name 时,就用import { name }导入(记得带上大括号)
3.一个文件里,有且只能有一个export default,但可以有多个export
4.当一个文件里,既有一个export default people, 又有多个export name 或者 export age时,导入就用 import people, { name, age }
5.当一个文件里出现n多个 export 导出很多模块,导入时除了一个一个导入,也可以用import * as example
- node 还不支持 import,需要用 require 加载文件
Promise
在 promise 之前代码过多的回调或者嵌套,可读性差、耦合度高、扩展性低。通过 Promise 机制,扁平化的代码机构,大大提高了代码可读性;用同步编程的方式来编写异步代码,保存线性的代码逻辑,极大的降低了代码耦合性而提高了程序的可扩展性。
// 其实就是用同步的方式去实现异步
fetch('/api')
.then(res => res.json())
.then(data => ({ data }))
.catch(err => ({ err }))
- 面试题
setTimeout(function () {
console.log(1);
}, 0);
new Promise(function executor(resolve) {
console.log(2);
for (var i = 0; i < 10000; i++) {
i == 9999 && resolve();
}
console.log(3);
}).then(function () {
console.log(4);
});
console.log(5);
/* 在执行定时器、事件、ajax等操作的时候,会把异步操作放到任务队列里等待主线程执行完
* 所以,定时器(setTimeout)会放到任务队列中,代码继续往下走
* 先输出2、3
* promise中的then操作是放在执行栈,也就是在主线程的最后去执行
* 主线程会继续往下走
* 所以输出5、4
* 执行完主线程的任务,才会去执行任务队列中的任务
* 所以最后执行1
*/
Generators 和 yield
生成器( generator)是能返回一个迭代器的函数。生成器函数也是一种函数,最直观的表现就是比普通的 function 多了个星号*,在其函数体内可以使用 yield 关键字,有意思的是函数会在每个 yield 后暂停
// 生成器
function* createIterator() {
yield 1;
yield 2;
yield 3;
}
// 生成器能像正规函数那样被调用,但会返回一个迭代器
let iterator = createIterator();
console.log(iterator.next().value); // 1
console.log(iterator.next().value); // 2
console.log(iterator.next().value); // 3
async 和 await
以顺序、同步代码的编写方式来控制异步流程,需要注意以下几点:
- await 关键字必须位于 async 函数内部
- await 关键字后面需要一个 promise 对象(不是的话就调用 resolve 转换它)
- 串行的执行异步操作,实现并行可以考虑
promise.all
async getUser () {
const res = await Apis.getUser()
// 未返回的情况下会阻塞代码,res为异步请求返回的结果
console.log(res)
}