常用语法


# 常用语法

# 1.Es6 常用语法

# 1.1 let 和 const

为什么要有 let 和 const

var 声明的变量存在的问题

  1. 污染全局变量(常见的作用域 window, function, with)
  2. 我们没有声明之前 会预先定义 出现变量提升
  3. var 可以被定义多次
  4. var 不能声明常量
  5. var 默认不会产生作用域
// 变量提升
console.log(a); // undefind
var a = 10;

// for 循环 var 定义
for (var i = 0; i < 10; i++) {
  setTimeout(function() {
    console.log(i); // 打印10次10
  });
}

//es5 解决 打印1-9 利用IIFE(自执行函数),形成一个作用域
for (var i = 0; i < 10; i++) {
  (function(i) {
    setTimeout(function() {
      console.log(i);
    });
  })(i);
}

// es6 使用 let 解决
for (let i = 0; i < 10; i++) {
  console.log(i);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

let 不会污染全局变量 ,不存在变量提升,不能被重复定义(同一个作用域下不能重复定义)

let + {} 可以产生一个作用域,异步循环

# 1.2 解构赋值


// 解构不到值,则返回undefind
let { a, b } = { a: 10 };
console.log(b); //undefind

// 对象解构

let { name, age, address = "回龙观" } = { name: "河大", age: 10 };
console.log(name); //河大
console.log(age); //10
console.log(address); //回龙观

// 对象解构默认值

let { name: name1, age, address = "回龙观" } = { name: "河大", age: 10 };
console.log(name1); // 河大

// 数组解构

let [n, a] = ["海峰", 10];
console.log(n, a); // 海峰 10

// 数组解构, 剩余运算值, ... 剩余运算符,只能放到最后一项

let [a, ...args] = ["海峰", "10", "回龙观"];
console.log(a); // 海峰
console.log(args); //[ '10', '回龙观' ]

// 对象解构, 剩余运算值

let { name, ...obj } = { name: "海峰", age: 10, address: "回龙观" };
console.log(obj); // { age: 10, address: '回龙观' }
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

# 1.3 深浅克隆

Javascript 有六种基本数据类型(也就是简单数据类型) 它们分别是:Undefined,Null,Boolean,Symbol,Number 和 String。

还含有一种复杂数据类型,就是 对象 ( function, array, object, regexp, date )

Undefined 和 Null 的区别

Undefined 类型只有一个值,就是 undefined,Null 类型也只有一个值,也就是 null, Undefined 其实就是已声明未赋值的变量输出的结果 Null 其实就是一个不存在的对象的结果, 是一个空指针

简单的数据类型和复杂的数据类型有以下重要的区别

对于简单数据类型

它们值在占据了内存中固定大小的空间,并被保存在栈内存中。

当一个变量向另一个变量复制基本类型的值会创建这个值的一个副本

还有就是不能给基本数据类型的值添加属性

var a = 1;
var b = a;
a.attr = "wsscat";
console.log(a.attr); //undefined
1
2
3
4

上面代码中 a 就是简单数据类型(Number),b 就是 a 的副本,它们两者都占有不同位置但相等的内存空间

对于复杂的数据类型

复杂的数据类型即引用类型,它的值是对象,保存在堆内存中,

包含引用类型值的变量实际上包含的并不是对象本身,而是一个指向该对象的指针。

从一个变量向另一个变量复制引用类型的值,复制的其实是指针,因此两个变量最终都指向同一个对象。

var obj = {
  name: "wsscat",
  age: 0
};
var obj2 = obj;
obj2["c"] = 5;
console.log(obj); //{name: "wsscat", age: 0, c: 5}
console.log(obj2); //{name: "wsscat", age: 0, c: 5}
1
2
3
4
5
6
7
8

更改其中一个对象的属性值,两个对象都发生了改变,究其原因是因为 obj 和 obj2 这两个变量都指向同一个指针 赋值只是复制了指针,所以当我们改变其中一个的值就会影响另外一个变量的值

实现浅拷贝的方法

Object.assign()

// 一层对象深拷贝, 从二层就是浅拷贝
let a = {
  name: "阳光"
};
let b = Object.assign({}, a);
b.name = "中学";
console.log(b); // { name: '中学' }
console.log(a); //{ name: '阳光' }
1
2
3
4
5
6
7
8

...剩余运算符

// 一层对象深拷贝,从二层就是浅拷贝
let a = {
  name: "阳光"
};
let b = { ...a };
b.name = "中学";
console.log(a); //{ name: '阳光' }
console.log(b); //{ name: '中学' }
1
2
3
4
5
6
7
8

二层拷贝

let a = {
  name: "阳光",
  age: {
    id: 1
  }
};
let b = Object.assign({}, a);
b.name = "中学";
b.age.id = 12;
console.log(b); //{ name: '中学', age: { id: 12 } }
console.log(a); //{ name: '阳光', age: { id: 12 } }
1
2
3
4
5
6
7
8
9
10
11

赋值拷贝

let a = {
  name: "阳光"
};
let b = a;
b.name = "河南";
console.log(b); // { name: '河南' }
1
2
3
4
5
6

深拷贝 - JSON.stringify()

let a = {
  name: "阳光"
};
let b = JSON.stringify(a);

b.name = "河南";

console.log(b);
console.log(a);
1
2
3
4
5
6
7
8
9

JSON.stringify() 不支持函数 日期 正则 undefined, 深拷贝不到值

let a = {
  name: "阳光",
  some: null,
  ady: function() {
    console.log(20);
  }
};
let b = JSON.stringify(a);

b.name = "河南";

console.log(b); // {"name":"阳光","some":null}
console.log(a); // { name: '阳光', some: null, ady: [Function: ady] }
1
2
3
4
5
6
7
8
9
10
11
12
13

# 1.4 手写克隆函数


// obj 参数不支持函数的拷贝
function deepClone(obj) {
  // null 和 undefined 在 == 号的情况下相等
  if (obj === undefined) return obj;
  // 数据类型 string number boolean symbol
  if (typeof obj !== "object") return obj;
  // 正则
  if (obj instanceof RegExp) return new RegExp(obj);
  //  日期
  if (obj instanceof Date) return new Date(obj);
  // 剩下的就是对象
  let cloneObj = new obj.constructor(); // 去当前传入对象的构造函数

  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      cloneObj[key] = deepClone(obj[key]); // 如果key的值 还是一个对象, 需要递归处理
    }
  }
  return cloneObj;
}

// 测试数组
let obj = [1, 2, 3];
let copyValue = deepClone(obj);

console.log(obj); // [ 1, 2, 3 ]
copyValue[1] = 10;
console.log(copyValue); //[ 1, 10, 3 ]

// 测试对象
let obj1 = {
  a: 1,
  b: {
    id: 12
  }
};
let copyValue1 = deepClone(obj1);
copyValue1.a = 20;
copyValue1.b.id = 120;
console.log(obj1); // { a: 1, b: { id: 12 } }
console.log(copyValue1); //{ a: 20, b: { id: 120 } }
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
34
35
36
37
38
39
40
41

# 1.5 Set 集合

set 代表的就是集合,它类似于数组,但是成员的值都是唯一的,没有重复的值。 Set 本身是一个构造函数,用来生成 Set 数据结构。 集合数据可以使用 forEach 遍历

Set 实例的属性和方法:

Set 结构的实例有以下属性

  • Set.prototype.constructor:构造函数,默认就是 Set 函数。

  • Set.prototype.size:返回 Set 实例的成员总数。


Set 实例的方法分为两大类:操作方法(用于操作数据)和遍历方法(用于遍历成员),四个操作方法。

  • Set.prototype.add(value):添加某个值,返回 Set 结构本身。
  • Set.prototype.delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
  • Set.prototype.has(value):返回一个布尔值,表示该值是否为 Set 的成员。
  • Set.prototype.clear():清除所有成员,没有返回值。

遍历操作

Set 结构的实例有四个遍历方法,可以用于遍历成员。

  • Set.prototype.keys():返回键名的遍历器
  • Set.prototype.values():返回键值的遍历器
  • Set.prototype.entries():返回键值对的遍历器
  • Set.prototype.forEach():使用回调函数遍历每个成员

Array.from 方法可以将 Set 结构转为数组

const items = new Set([1, 2, 3, 4, 5]);
const array = Array.from(items);
console.log(array); // [1, 2, 3, 4, 5]
1
2
3

通过 add()方法向 Set 结构加入成员,结果表明 Set 结构不会添加重复的值。

const s = new Set();
let arr = [2, 3, 5, 4, 5, 2, 2];
arr.forEach(x => s.add(x));

for (let i of s) {
  console.log(i); // 2 3 5 4
}
1
2
3
4
5
6
7

去重 和 求长度

let set = new Set([1, 2, 3, 3, 2, 1]);
console.log([...set]); //[ 1, 2, 3 ]
console.log(set.size); // 3
1
2
3

并集 | 交集 | 交集

let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);

// 并集
let union = new Set([...a, ...b]);
// Set {1, 2, 3, 4}

// 交集
let intersect = new Set([...a].filter(x => b.has(x)));
// set {2, 3}

// 差集
let difference = new Set([...a].filter(x => !b.has(x)));
1
2
3
4
5
6
7
8
9
10
11
12
13

# WeakSet


WeakSet 结构与 Set 类似,也是不重复的值的集合。 但是,它与 Set 有两个区别。 首先,WeakSet 的成员只能是对象,而不能是其他类型的值。

WeakSet 结构有以下三个方法:

  • WeakSet.prototype.add(value):向 WeakSet 实例添加一个新成员。
  • WeakSet.prototype.delete(value):清除 WeakSet 实例的指定成员。
  • WeakSet.prototype.has(value):返回一个布尔值,表示某个值是否在 WeakSet 实例之中。
const ws = new WeakSet();
const obj = {};
const foo = {};

ws.add(window);
ws.add(obj);

ws.has(window); // true
ws.has(foo); // false

ws.delete(window);
ws.has(window); // false
1
2
3
4
5
6
7
8
9
10
11
12

WeakSet 没有 size 属性,没有办法遍历它的成员

WeakSet 不能遍历,是因为成员都是弱引用,随时可能消失,遍历机制无法保证成员的存在,很可能刚刚遍历结束,成员就取不到了。WeakSet 的一个用处,是储存 DOM 节点,而不用担心这些节点从文档移除时,会引发内存泄漏。

ws.size; // undefined
ws.forEach; // undefined

ws.forEach(function(item) {
  console.log("WeakSet has " + item);
});
1
2
3
4
5
6

# 2. 手写 Promise

promise 承诺 (promise 有三个状态 等待态、成功态、失败态)

默认现在高版本浏览器哦 默认支持 promise (es6-promise) polyfill

Promise 是一个类 ,默认 new 的时候 是等待态 我调用 resolve 就是成功态 调用了 reject 就是失败态 返回的是一个 promise 的实例,每个实例都有一个 then 方法

# 2.1 基础版同步-Promise

同步 promise

// 1. 三个状态常量
const PENDING = "PENDING"; // 初始态
const FULFILLED = "FULFILLED"; // 成功态
const REJECTED = "REJECTED"; // 失败态

class Promise {
  constructor(executor) {
    //executor 会立即执行
    this.status = PENDING; // 初始化初始状态
    this.value = undefined; // 成功的值
    this.reason = undefined; // 失败的原因
    // 成功执行的函数
    let resolve = value => {
      if (this.status === PENDING) {
        this.value = value; // 将传入的成功的函数的参数进行赋值
        this.status = FULFILLED; // 改变初始态为 成功
      }
    };
    // 失败执行的函数
    let reject = reason => {
      if (this.status === PENDING) {
        this.reason = reason;
        this.status = REJECTED;
      }
    };
    try {
      executor(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }
  // then 方法, 接收两个函数作为参数, 一个返回成功状态下的值, 一个失败状态下的值
  then(onfulfilled, onrejected) {
    if (this.status === FULFILLED) {
      return onfulfilled(this.value);
    }
    if (this.status === REJECTED) {
      return onrejected(this.reason);
    }
  }
}

// 实例化Promise
let p = new Promise((resolve, reject) => {
  resolve("success");
});

// 调用 then 方法
p.then(value => {
  console.log(value); // success
});
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

# 2.2 中级版异步-Promise

异步 Promise












 
 
 

























 
 
 
 
 
 











 

















 





























// 1. 三个状态常量
const PENDING = "PENDING"; // 初始态
const FULFILLED = "FULFILLED"; // 成功态
const REJECTED = "REJECTED"; // 失败态

class Promise {
  constructor(executor) {
    //executor 会立即执行
    this.status = PENDING; // 初始化初始状态
    this.value = undefined; // 成功的值
    this.reason = undefined; // 失败的原因
    // 新增代码, 发布订阅模式, 存储所有的异步回调
    this.resolveCallbacks = []; // 存放所有异步的成功调用函数
    this.rejectCallbacks = []; // 存放所有异步的失败调用函数
    // 成功执行的函数
    let resolve = value => {
      if (this.status === PENDING) {
        this.value = value; // 将传入的成功的函数的参数进行赋值
        this.status = FULFILLED; // 改变初始态为 成功
        this.resolveCallbacks.forEach(fn => fn());
      }
    };
    // 失败执行的函数
    let reject = reason => {
      if (this.status === PENDING) {
        this.reason = reason;
        this.status = REJECTED;
        this.rejectCallbacks.forEach(fn => fn());
      }
    };
    try {
      executor(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }
  // then 方法, 接收两个函数作为参数, 全部改为异步的
  then(onfulfilled, onrejected) {
    if (this.status === FULFILLED) {
      setTimeout(() => {
        try {
          return onfulfilled(this.value);
        } catch (e) {
          reject(e);
        }
      });
    }
    if (this.status === REJECTED) {
      setTimeout(() => {
        try {
          return onrejected(this.reason);
        } catch (e) {
          reject(e);
        }
      });
    }
    // 新增的代码
    if (this.status === PENDING) {
      this.rejectCallbacks.push(() =>
        setTimeout(() => {
          try {
            onfulfilled(this.value);
          } catch (e) {
            reject(e);
          }
        })
      );
      this.rejectCallbacks.push(() =>
        setTimeout(() => {
          try {
            onrejected(this.reason);
          } catch (e) {
            reject(e);
          }
        })
      );
    }
  }
}

let p = new Promise((resolve, reject) => {
  //   resolve("success")
  reject("失败");
});

// 第一次调用
p.then(
  value => {
    console.log(value);
  },
  reason => {
    console.log(reason);
  }
);
// 第二次调用
p.then(
  value => {
    console.log(value);
  },
  reason => {
    console.log(reason);
  }
);
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103