# 基础语法

# let

  1. 变量不能重复声明
  2. let 是块级作用域,位于代码块中,代码块外没有定义
  3. 不存在变量提升,初始不会对 let 定义的变量进行收集,赋予 undefined 初值
  4. 不影响作用域链,如果在代码块中调用了函数,如果在该函数中找不到变量定义,则会到上一级,也就是块级作用域中找到 let 定义的变量

# const

  1. const 用来声明常量,一定要赋初值
  2. 值一旦被确定就不能修改,且一般变量名使用大写
  3. 也是一个块级作用域
  4. 对于数组和对象的元素修改,不算做对常量的修改,不会报错,是因为常量所指向的起始地址没有发生改变。
    javascript
    1
    2
    const TEAM = ['a','b','c','d'];
    TEAM.push('e');

    上面的代码不会报错,但下面的代码就会报错:
    javascript
    1
    TEAM = 100;

# 解构赋值

解构赋值:允许按照一定模式从数组和对象中提取值,对变量进行赋值。

  1. 数组的解构
    javascript
    1
    2
    const F4 = ['a','b','c','d'];
    let [a, b, c, d] = F4;
  2. 对象的解构
    javascript
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    const a = {
    name: 'osakana',
    age: '20',
    click: function(){
    console.log('hello');
    }
    };

    let {name, age, click} = a;
    click();

    执行如上代码就会在控制台输出 hello

# 模板字符串

ES6 引入新的声明字符串的方式:反单引号 (``)

  1. 内容中可以直接出现换行符,而普通的引号不能
  2. 变量拼接
    javascript
    1
    2
    let love = 'czj';
    let out = `${love}是我最爱的女孩!`;

# 箭头函数

ES6 允许使用箭头 (=>) 定义函数

javascript
1
2
3
4
5
6
7
8
//声明一个函数
// let fn = function(){
// }
let fn = (a, b) => {
......
}
//调用函数
fn(1,2);

箭头函数的特性:

  1. this 是静态的,this 始终指向函数声明时所在作用域下的 this 的值
  2. 不能作为构造函数实例化对象
  3. 不能使用 arguments 变量(用来保存实参)
  4. 箭头函数的简写
    1. 省略小括号,当形参有且只有一个时
      javascript
      1
      2
      3
      let add = n => {
      return n + n;
      }
    2. 省略花括号,当代码体只有一条语句时,此时 return 必须省略,语句的执行结果就是函数的返回值
      javascript
      1
      let pow = (n) => n*n;
  5. 箭头函数适合与 this 无关的回调、定时器,数组的方法回调;不适合与 this 有关的回调、事件回调、对象的方法

# rest 参数

ES6 引入 rest 参数,用于获取函数的实参,用来代替 arguments

javascript
1
2
3
4
function date(...args) {
console.log(args);
}
date('a','b','c');

上面的代码会返回一个数组 ['a','b','c'],rest 参数必须要放到参数最后
javascript
1
2
3
function date(a,b,...args){

}

# 扩展运算符

[...] 扩展运算符能将数组转换为逗号分隔的参数序列

javascript
1
2
3
4
5
6
7
//声明一个数组
const array = ['1','2','3']; //=> '1','2','3'
//声明一个函数
function hanshu(){
console.log(arguments);
}
hanshu(...array); //hanshu('1','2','3')

# Symbol

Symbol 是一种 ES6 新引入的原始数据类型,表示独一无二的值。类似于字符串的数据类型。

Symbol 特点:

  1. 值唯一,用来解决命名冲突的问题
  2. 值不能与其他数据进行运算
  3. 定义的对象属性不能使用 for...in 循环遍历,但是可以使用 Reflect.ownKeys 来获取对象的所有键名

javascript
1
2
3
4
5
6
7
8
let s = Symbol();
let s1 = Symbol('a');
let s2 = Symbol('a');
console.log(s1 === s2) // false

let s3 = Symbol.for('a');
let s4 = Symbol.for('a');
console.log(s3 === s4) // true

Symbol 可以用来向对象中添加方法

javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//假设我们不知道对象中有哪些属性
let game = {···}

//声明一个对象
let methods = {
up: Symbol(),
down: Symbol()
};
game[methods.up] = function(){
console.log("up");
}

game[methods.down] = function(){
console.log("down");
}

//另一种方式
let game = {
name: "overwatch",
[Symbol('say')]: function(){
console.log("hello");
}
}

Symbol 还有 11 个内置属性,这里仅举个例子:

javascript
1
2
3
4
5
6
7
8
9
class Person {
static [Symbol.hasInstance](param){
console.log(param);
return false;
}
}

let test = {};
console.log(test instanceof Person); //会返回false

# 迭代器

ES6 创造了一种新的遍历命令 for...of 循环,Iterator 接口主要供 for...of 消费。

javascript
1
2
3
4
5
6
//声明一个数组
const array = ['a','b','c','d'];

for(let v of array) {
console.log(v);
}

会输出 array 数组中的每个元素的值。

# 迭代器的工作原理

  1. 创建一个指针对象,指向当前数据结构的起始位置
  2. 第一次调用对象的 next 方法,指针自动指向数据结构的第一个成员
  3. 接下来不断调用 next 方法,指针一直往后移动,直到指向最后一个成员
  4. 每调用 next 方法返回一个包含 value 和 done 属性的对象

javascript
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
27
28
//自定义遍历数据
const osakana = {
name: "osakana373",
stu: [
'xiaoming',
'xiaowang',
'xiaozhang'
],
[Symbol.iterator](){
let index = 0;
let _this = this;
return {
next: function(){
if(index < _this.stu.length) {
const result = {value: _this.stu[index], done: false};
index++;
return result;
}else {
return {value: undefined, done: true};
}
}
};
}
}

for (let v of osakana) {
console.log(v);
}

上面的代码会输出 stu 数组中的每一个元素。

# 生成器

生成器是一个特殊函数,用来进行异步编程,格式如下:

javascript
1
2
3
4
5
6
7
8
9
10
//加一个*号
function * gen(){
console.log("hello");
}

// let iterator = gen();
// iterator.next();
for(let v of gen()){
console.log(v);
}

只有调用迭代器的 next 方法才会打印 hello,或者使用 for...of 语句遍历。生成器也可以传参。

# Promise

Promise 是 ES6 引入的异步编程的新解决方案,语法上是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果。

javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//实例化Promise
const p = new Promise(function(resolve, reject){
setTimeout(function(){
let data = '数据库数据',
//resolve
resolve(data);
//reject(err)
}, 1000)
});

//调用promise对象的then方法
//value 成功 ;reason 失败
//只要上面调用了resolve(成功),就执行then方法中的第一个回调函数;调用reject(失败),就执行第二个回调函数
p.then(function(value){

}, function(reason){

})

# Promise 读取文件

这里用到了一些 node.js 的知识(详情可以看菜鸟教程 Node.js),不过不会问题也不大。

javascript
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
//引入fs模块(node.js中的知识)
const fs = require('fs');
//调用方法读取文件
fs.readFile('./xxx/xxx', (err, data) => {
//如果失败,则抛出错误
if(err) throw err;
//没有错误,则输出内容
console.log(data.toString());
//data就是文件中的内容
});

//使用Promise封装
const p = new Promise(function(resolve, reject){
fs.readFile('./xxx/xxx', (err, data) => {
//如果失败,则抛出错误
if(err) reject(err);
//如果成功,则输出内容
resolve(data);
});
});

p.then(function(value){
console.log(value.toString());
}, function(reason){
console.log("读取失败");
});

# Promise 封装 AJAX 请求

我们假设接口地址为:https://api.apiopen.top/getJoke

javascript
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
27
28
29
30
31
32
33
//封装Promise
const p = new Promise((resolve, reject) => {
//1. 创建对象
const xhr = new XMLHttpRequest();
//2. 初始化
xhr.open("GET", "https://api.apiopen.top/getJoke");
//3. 发送
xhr.send();
//4. 绑定事件,处理相应结果
xhr.onreadystatechange = function(){
//判断
if(xhr.onreadystatechange === 4) {
//判断响应状态码 200-299 表示成功
if(xhr.status >= 200 && xhr.status < 300) {
//表示成功
// console.log(xhr.response);
resolve(xhr.response);
}else {
//表示失败
// console.error(xhr.status);
reject(xhr.status);
}
}
}
});


//指定回调
p.then(function(value){
console.log(value);
}, function(reason){
console.error(reason);
});

# Promise.prototype.then 方法

javascript
1
2
3
4
5
6
7
8
9
10
11
12
const p = new Promise((resolve, reject) =>{
setTimeout(()=>{
resolve('用户数据');
}, 1000)
});

//then方法返回结果也是一个promise对象,对象状态由回调函数执行的结果决定
const result = p.then(value => {
console.log(value.toString());
}, reason => {
console.log("读取失败");
});

# catch 方法

catch 方法用来指定失败的回调

javascript
1
2
3
4
5
6
7
8
9
10
const p = new Promise((resolve, reject) =>{
setTimeout(()=>{
reject('出错啦');
}, 1000)
});

//不指定第一个参数,then和catch一样
p.catch(function(reason){
console.warn(reason);
});

# Set

ES6 提供了新的数据结构 Set(集合)。类似于数组,但成员的值都是唯一的,集合实现了 iterator 接口,故可使用扩展运算符合 for...of 进行遍历。

Set 的属性和方法:

  1. size 返回集合的元素个数
  2. add 增加一个新元素,返回当前集合
  3. delete 删除元素,返回 boolean 值
  4. has 检测集合中是否包含某个元素,返回 boolean 值

javascript
1
2
3
4
5
6
7
8
9
let s = new Set();
let s1 = new Set(['a','b','c','a']);
//s1中的元素会自动去重
s1.add('d');
s1.delete('d');
s1.has('d');
for(let v of s1) {
console.log(v); //会打印s1中所有元素的值
}

# Map

ES6 提供了 Map 数据结构,类似于对象,也是键值对的集合。但是 “键” 的范围不限于字符串,各种类型的值(包括对象)都可以当做键。Map 也实现了 iterator 接口,故可使用扩展运算符合 for...of 进行遍历。

Map 的属性和方法:

  1. size 返回 Map 的元素个数
  2. set 增加一个新元素,返回当前 Map
  3. get 返回键名对象的键值
  4. has 检测 Map 中是否包含某个元素,返回 boolean 值
  5. clear 清空集合,返回 undefined

javascript
1
2
3
4
5
6
7
//声明 Map
let m = new Map();
//添加元素
m.set('name', 'osakana');
m.set('change', function(){
console.log("Hello");
});

# Class 类

javascript
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
27
28
29
//ES5传统构造方法创建对象
function Phone(brand, price) {
this.brand = brand;
this.price = price;
}

Phone.prototype.call = function(){
console.log("call");
}

let Huawei = new Phone('华为', 4999);
Huawei.call();
console.log(Huawei);

//class
class Phone {
//构造方法,名字固定不能修改
constructor(brand, price){
this.brand = brand;
this.price = price;
}

//方法必须使用该语法,不能使用ES5的对象完整形式
call(){
console.log("call");
}
}

let Huawei = new Phone('华为', 4999);

# class 类的静态成员

类的静态成员属于这个类,不属于这个类的实例

javascript
1
2
3
4
5
6
7
8
9
10
11
class Phone = {
//静态属性
static name = '手机';
static change(){
console.log("xxx");
}
}

let Huawei = new Phone();
console.log(Huawei.name); //undefined
console.log(Phone.name); //手机

# 构造函数继承

javascript
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
27
//父级构造函数:手机
function Phone(brand, price) {
this.brand = brand;
this.price = price;
}

Phone.prototype.call = function(){
console.log("call");
}

//子级构造函数:智能手机
function SmartPhone(brand, price, color, size) {
Phone.call(this, brand, price); //继承了父类的brand和price
this.color = color;
this.size = size;
}
//设置子级构造函数的原型(子级构造函数就会有父级构造函数的方法)
SmartPhone.prototype = new Phone;
SmartPhone.prototype.constructor = SmartPhone; //加不加都可

//声明子级构造函数的方法
SmartPhone.prototype.photo = function(){
console.log();
}

//实例
const Huawei = new SmartPhone('Huawei', 4999, '星河银', '6.7inch');

# 类继承

javascript
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
27
28
29
30
31
32
33
//父类
class Phone {
//构造方法,名字固定不能修改
constructor(brand, price){
this.brand = brand;
this.price = price;
}

//方法必须使用该语法,不能使用ES5的对象完整形式
call(){
console.log("call");
}
}

//子类
class SmartPhone extends Phone {
//构造方法
constructor(brand, price, color, size){
super(brand, price); //类似Phone.call(this, brand, price);
this.color = color;
this.price = price;
}

photo(){
consolo.log();
}
//子类对父类方法的重写
call(){
console.log("hello");
}
}
//实例
const Huawei = new SmartPhone('Huawei', 4999, '星河银', '6.7inch');

# class 中的 getter 和 setter 设置

get 和 set 通常对对象的动态属性进行封装。

javascript
1
2
3
4
5
6
7
8
9
10
class Phone {
//获取price
get price(){
console.log();
}
//设置price,必须要有一个参数
set price(newVal){
console.log();
}
}

# 数值扩展

  1. Number.EPSILON 是 js 表示的最小精度
  2. 二进制、八进制和十六进制:0bxxx 0oxxx 0xxxx
  3. Number.isFinite () 检测一个数值是否为有限数
  4. Number.isNaN () 检测一个数值是否为 NaN
  5. Number.parseInt () 字符串转整数(会截断)
  6. Number.isInteger () 判断一个数是否为整数
  7. Math.trunc () 将数字的小数部分抹掉
  8. Math.sugn () 判断一个数是否为正数负数还是 0(1,-1,0)

# 对象方法扩展

  1. Object.is (a, b) 判断两个值是否完全相等
  2. Object.assign () 对象的合并,相同属性后面对象会覆盖前面对象
  3. Object.setPrototypeOf (a, b) 设置原型对象,b 是 a 的原型对象
  4. Object.getPrototypeOf 获取一个对象的原型对象
  5. Object.keys () 获取对象所有的键
  6. Object.values () 获取对象所有的值
  7. Object.entries () 返回一个数组,数组中每个成员还是一个数组
  8. Object.getOwnPropertyDescriptors () 获取对象属性的描述对象

# ES8 新特性

# async 和 await

async 和 await 两种语法结合可以让异步代码像同步代码一样

# async 函数

  1. async 函数的返回值为 promise 对象
  2. promise 对象的结果由 async 函数执行的返回值决定

# await 表达式

  1. await 必须写在 async 函数中
  2. await 右侧的表达式一般为 promise 对象
  3. await 返回的是 promise 成功的值
  4. await 的 promise 失败了,就会抛出异常,需要通过 try...catch 捕获处理

javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//创建promise对象
const p = new Promise((resolve, reject) => {
resolve("成功的值!");
})

async function main(){
try {
let result = await p;
console.log(result);
} catch (e) {
console.log(e);
}
}

//调用函数
main();

上面的代码会输出 "成功的值!"

# async 和 await 封装 AJAX 请求

javascript
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
27
28
29
30
31
32
//发送AJAX请求,返回的结果是Promise对象
function senfAJAX(url) {
return new Promise((resolve, reject) => {
//创建对象
const x = new XMLHttpRequest();
//初始化
x.open('GET', url);
//发送
x.send();
//事件绑定
x.onreadystatechange = function(){
if(x.readyState === 4) {
if(x.status >= 200 && x.statue < 300) {
resolve(x.response);
} else {
reject(x.status);
}
}
}
})
}
//promise then 方法测试
sendAJAX("https://api.....").then(value => {
console.log(value);
}, reason => {})

//async 与 await测试
async function main(){
//发送AJAX请求
let result = await sendAJAX("https://api.....");
console.log(result);
}

# ES9 新特性

# 对象展开

ES9 中为对象提供了像数组一样的 rest 参数和扩展运算符

javascript
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
27
28
29
30
//rest参数
function connect({host, port, ...user}) {
console.log(host);
console.log(port);
console.log(user);
}

connect({
host: '127.0.0.1',
port: 3306,
username: 'root',
password: 'root'
});

//扩展运算符
const one = {
q: 'a'
}
const two = {
w: 'b'
}
const three = {
e: 'c'
}
const four = {
r: 'd'
}

//把四个对象的属性放在一起,完成对象的合并
const result = {...one, ...two, ...three, ...four}