usuiforhe пре 4 година
родитељ
комит
57b4a871c3
1 измењених фајлова са 518 додато и 3 уклоњено
  1. 518 3
      TS/JaveScript.md

+ 518 - 3
TS/JaveScript.md

@@ -2,8 +2,8 @@
  * @Version: 
  * @Autor: LC
  * @Date: 2022-01-20 10:45:55
- * @LastEditors: LC
- * @LastEditTime: 2022-01-27 12:28:31
+ * @LastEditors: Please set LastEditors
+ * @LastEditTime: 2022-02-02 02:54:01
  * @Description: file content
 -->
 # JavaScipt语法
@@ -1811,7 +1811,7 @@ console.log(s3.description);
 
 const obj = {
     [s1] : "abv",
-    [s2] : "qwer 
+    [s2] : "qwer"
 };
 
 obj[s3] = "asd";
@@ -1841,3 +1841,518 @@ console.log(Symbol.keyFor("aa"));
 
 使用Symbol作为key的属性名,在遍历/Object.key等方法是遍历不到这些Symbol值的  
 
+### Set与WeakMap(ES6)
+
+**Set**新增数据结构,可以用来保存数据,类似数组,但是**元素不能重复**  
+
+```javascript
+const s = new Set();
+
+// 添加
+s.add(10);
+s.add(10);
+s.add(20);
+s.add(30);
+
+s.add({});  // 字面量A
+s.add({});  // 字面量B A、B地址不同 并不是同一个东西
+
+console.log(s);
+
+// 去重
+const arr = [10, 10, 20, 30, 40];
+const arrSet = new Set(arr);
+const newArr = [...arrSet];     // 支持展开运算符
+console.log(newArr);    
+
+// 常用方法 属性
+console.log(arrSet.size);   // Set内元素个数
+arrSet.delete(10);          // 删除Set内的元素
+arrSet.has(10);             // 判断Set内是否存在某个元素
+arrSet.clear();             // 清除Set内所有元素
+
+// 遍历Set
+arrSet.forEach(item => {
+    console.log(item);
+})
+
+for(const item of arrSet){
+    console.log(item);
+}
+
+```
+
+**WeakSet**类似*Set*,也是内部元素不能重复的数据结构  
+
+- 与Set的区别
+  - **WeakSet**只能存放对象类型,不能存放基本数据类型
+  - **WeakSet**对元素的引用是**弱引用**,如果没有其他引用对某个对象进行运行,那么GC可以对该对象进行回收
+
+```javascript
+const weakSet = new WeakSet();
+
+// 不能存放基本数据类型
+weakSet.add(10);        // TypeError
+
+weakSet.add(value);     // 返回weakSet本身
+weakSet.delete(value);  // 返回boolean类型
+weakSet.has(value);     // 返回boolean类型
+```
+
+WeakSet不能遍历,因为WeakSet只是对对象的弱引用,如果遍历获取其中的元素,有可能造成对象的不正常销毁  
+
+> WeakSet在开发中很少用到
+
+**WeakSet的应用**
+
+```javascript
+const personSet = new WeakSet();
+class Person(){
+    constructor() {
+        personSet.add(this);
+    }
+    running() {
+        if(!personSet.has(this)){
+            throw new Error("不能通过非构造方法创建出来的对象调用running方法");
+        }
+        console.log("running ", this);
+    }
+}
+
+const p = new Person();
+p.running();
+p.running.call({name, "why"});
+```
+
+> 使用WeakSet是因为当Person对象需要被销毁时,不会因为Set的强引用而不会被GC
+
+### Map与WeakMap(ES6)
+
+**Map**新增数据结构,可以用来保存映射关系  
+
+对象映射关系只能用**字符串**和**Symbol**作为属性名,如果使用对象,会自动将对象转换成字符串来作为key  
+
+```javascript
+// 对象字面量只能用字符串和Symbol作为key的测试用例
+const obj1 = {};
+const obj2 = {};
+const info = {
+    [obj1] : "a",
+    [obj2] : "b";
+};
+console.log(info);      // { [Object object] : "b" }
+
+const map = new Map();
+map.set(obj1, "a");     // object作为key,插入到map中
+map.set(obj2, "b");
+console.log(map);
+
+// 常用方法
+const map2 = new Map([[obj1, "a"], [obj2, "b"]]);   // 用数组作为构造参数
+
+map2.set("key", "value");
+console.log(map2.get("key"));
+console.log(map2.has("key"));
+console.log(map2.delete("key"));
+map2.clear();
+
+// 遍历map
+map2.forEach((value, key) => {
+    console.log(value, " ", key);
+});
+
+for(const item of map2) {
+    // 这里item是一个数组 index 0 是key index 1 是value
+    console.log(item[0], item[1]);
+}
+
+// 直接对数组进行解构
+for(const [key, value] of map2) {
+    console.log(key, " ", value);
+}
+```
+
+**WeakMap**也是键值对容器,与**Map**的区别  
+1. WeakMap的key只能使用对象,不接受其他的类型作为key
+2. WeakMap的key对对象的引用是**弱引用**,如果没有其他对象引用该对象,那么可以GC回收对象
+
+```javascript
+const obj1 = {};
+const obj2 = {};
+
+const weakMap = new WeakMap();
+weakMap.set(1, "b");  // TypeError
+weakMap.set(obj1, "a");
+
+console.log(weakMap.get(obj1));
+console.log(weakMap.has(obj1));
+console.log(weakMap.delete(obj1));
+```
+
+> 不支持forEach,也不能用其他方式遍历
+
+**WeakMap的应用**
+
+> vue3响应式原理
+
+```javascript
+const obj1 = {
+    name : "x",
+    age : 10
+};
+
+const obj2 = {
+    name : "z",
+    height : 190,
+    address : "BJ"
+}
+
+function obj1NameFn1(){
+    console.log("Obj1 Name Fn1 被执行");
+}
+function obj1NameFn2(){
+    console.log("Obj1 Name Fn2 被执行");
+}
+function obj2HeightFn1(){
+    console.log("Obj2 height Fn1 被执行");
+}
+function obj2HeightFn2(){
+    console.log("Obj2 height Fn2 被执行");
+}
+
+const weakMap = new WeakMap();
+const obj1Map = new Map();
+const obj2Map = new Map();
+
+obj1Map.set("name", [obj1NameFn1, obj1NameFn2]);
+weakMap.set(obj1, obj1Map);
+
+obj2Map.set("height", [obj2HeightFn1, obj2HeightFn2]);
+weakMap.set(obj2, obj2Map);
+
+// Proxy / Object.defineProperty 监听obj1或obj2的属性发生改变
+obj1.name = "qwer";
+
+// 监听到数据变化后
+const targetMap = weakMap.get(obj1);
+const fns = targetMap.get("name");
+fns.forEach(item => item());
+
+```
+
+> 使用WeakMap是因为当obj对象需要被销毁时,不会因为Map的强引用而不会被GC
+
+### Array Includes(ES7)
+
+- `includes`与`indexOf`的区别:对`NaN`的判断
+
+
+```javascript
+const names = ['a', 'b', 'c', NaN];
+if(names.indexOf('a') !== -1){
+    // 就版本判断是否包含指定值
+}
+
+// Array.includes(searchElement : string, formIndex ?: number): boolean
+if(names.includes('a')){
+    console.log("包含 a")
+}
+console.log(names.includes('a', 0));    // true
+console.log(names.includes('a', 1));    // false
+console.log(names.includes('a', 2));    // false
+
+console.log(names.includes(NaN));       // true
+console.log(names.indexOf(NaN));        // -1
+```
+
+> fromIndex参数表示从第几个开始查找  
+> `indexOf`无法找到NaN
+
+### 指数运算符(ES7)
+
+```javascript
+const result = Math.pow(3, 3);
+const result2 = 3 ** 3;
+console.log(result, result2);
+```
+
+### Object Values(ES8)
+
+之前通过`Object.keys`获取一个对象所有的key,ES8提供`Object.values`来获取所有的value值  
+
+```javascript
+const obj = {
+    name : "x",
+    age : 10
+}
+console.log(Object.keys(obj));                  // ['name', 'age']
+console.log(Object.values(obj));                // ['x', 10]
+
+// 用的少
+console.log(Object.values(["q", "w", "r"]));    // ['q', 'w', 'r']
+console.log(Object.values("qwer"));             // ['q', 'w', 'e', 'r']
+
+```
+
+### Obejct Entries(ES8)
+
+通过`Object.entries`可以获取一个数组,数组中会存放可枚举属性的键值对数组
+
+
+```javascript
+const obj = {
+    name : "x",
+    age : 10
+}
+
+console.log(Object.entries(obj));   // [ [ 'name', 'x' ], [ 'age', 10 ] ]
+
+console.log(Object.entries(["a", "b", "c"]));   // [ [ '0', 'a' ], [ '1', 'b' ], [ '2', 'c' ] ]
+console.log(Object.entries("qwer"));    // [ [ '0', 'q' ], [ '1', 'w' ], [ '2', 'e' ], [ '3', 'r' ] ]
+```
+
+> 本质就是一个二维数组,第一个维度是item,第二个维度是键值对
+
+### String Padding(ES8)
+
+> 字符串填充
+
+某些字符串我们需要对其进行前后的填充,去实现某种格式化的效果,ES8中增加了`padStart`和`padEnd`方法,分别是队字符串和首位进行填充的
+
+```javascript
+const message = "Hello World";
+// string.padStart(maxLength : number, fillString ?: string) : string
+const newMessage1 = message.padStart(15);
+console.log(newMessage1);           //     Hello World
+const newMessage2 = message.padStart(15, "*");
+console.log(newMessage2);           // ****Hello World
+const newMessage3 = message.padStart(15, "*").padEnd(20, "-");
+console.log(newMessage3);           // ****Hello World-----
+```
+
+**案例**
+
+```javascript
+// 身份证号、银行卡号只显示最后四位
+const cardNumber = "4532132468321312135433";
+const lastFourCardNumber = cardNumber.slice(-4);
+const finalCard = lastFourCardNumber.padStart(cardNumber.length, "*");
+console.log(finaleCard);    // ******************5433
+```
+
+### Trailing-Commas(ES8)
+
+支持函数调用额函数声明中参数列表最后多个逗号  
+
+> 为了有些人习惯、有些语言而添加
+
+```javascript
+function foo(m, n,){    // ES8之前报错
+
+}
+foo(1, 2,);
+```
+
+### Object Descriptors(ES8)
+
+`Object.getOwnPropertyDescriptors`获取对象的所有属性描述符
+
+> 前面有写
+
+### Flat FlatMap(ES10)
+
+`flat()`方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回  
+
+```javascript
+// Array.flat(depth? : 1) : (number | number[]) []
+const nums = [1, 2, [3, [4, 5], 6], 7, 8, [9, 10]];
+const newNums = nums.flat();    // 默认做一次降维
+console.log(newNums);
+
+const newNums2 = nums.flat(2);  
+console.log(newNums2);
+```
+
+`flatMap()`方法首先使用映射函数映射每个元素,然后将结果压缩成一个新数组
+1. `flatMap()`是先进行`map`操作,在做`flat`操作
+2. `flatMap()`中的`flat`深度为1
+
+```javascript
+// Array.flatMap(callback : (this : undefined, value : number | number[] | number[][], index : number, array : (number | number[] | number[][])[]) => any, thisArg ?: undefined) : any[]
+
+const nums = [1, 2, 3];
+const newNum = nums.flatMap(item => {
+    return item * 2;
+});
+console.log(newNum);    // [2, 4, 6]
+
+const messages = ["Hello World", "Hello Java", "Hello Cpp"];
+const words = messages.flatMap(item => {
+    return item.split(" ");
+});
+console.log(wrods); // [ "Hello", "World","Hello","Java","Hello","Cpp"]
+```
+
+1. 这里的messages先通过`map()`转换为\[\["Hello", "World"\],\["Hello", "Java"\],\["Hello", "Cpp"\]\]
+2. 再通过`flat()`将二维数组转换成一维数组
+
+### Object fromEntries(ES10)
+
+通过`entries`创建对象
+
+```javascript
+const obj = {
+    name : "x",
+    age : 10
+};
+const entries = Object.entries(obj);
+console.log(entries);
+
+const newObj = {};
+for(const entry of entries){
+    newObj[entry[0]] = entry[1];
+}
+console.log(newObj);
+
+const newObj2 = Object.fromEntries(entries);
+console.log(newObj2);
+
+const queryString = "name=w&age=10&height=190";
+const queryParams = new URLSearchParams(queryString);
+const paramObj = Object.fromEntries(queryParams);
+console.log(paramObj);  // {"name": "w","age": "10","height": "190"}
+```
+
+### trimStart trimEnd(ES10)
+
+去除前、后的空白字符
+
+```javascript
+const message = "    hello wworld    ";
+console.log(message.trim());        // 
+console.log(message.trimStart());   // 
+console.log(message.trimEnd());     // 
+```
+
+### BigInt(ES11)
+
+早期JS**不能正确**的表示过大的数字,大于`Number.MAX_SAFE_INTEGER`的数值的表示**可能是**不正确的  
+
+```javascript
+console.log(Number.MAX_SAFE_INTEGER);   // 9007199254740991
+```
+
+ES11引入BigInt来表示大数字,就是在数字后加上`n`  
+
+```javascript
+const bigint = 90071992547409910n;
+console.log(bigint);
+bigint + 10;            // TypeError: Cannot mix BigInt and other type
+bigint + BigInt(10);    // 90071992547409920n
+const num = Number(bigint); // 不一定正确 不安全
+```
+
+### Nullish Coalescing Operator(ES11)
+
+> 空值合并操作、空值合并运算
+
+```javascript
+// 空值合并运算 ??
+
+let foo;
+let foo1 = 0;
+let bar1 = foo1 || "default value";
+let bar = foo ?? "default value";
+
+console.log(bar);   // default value
+console.log(bar1);  // default value
+```
+
+> 针对**bar1**的值,我们目标肯定是0,但是由于`||`运算符将0、false和空字符串认为是false,所以这里使用`??`是最好的选择
+
+### Optional Chaining(ES11)
+
+> 可选链
+
+可选链是ES11新增的一个特性,主要作用是在代码中进行`null`和`undefined`判断时更加清晰和简介  
+
+```javascript
+const info = {
+    name : "x",
+    friend : {
+        name : "a",
+        friend : {
+            name : "q"
+        }
+    }
+};
+
+console.log(info.friend.friend.name);
+
+// 为了代码健壮性 需要判断info中是否存在friend,info.friend中是否存在friend
+if(info && info.friend && info.friend.friend){
+    console.log(info.friend.friend.name);
+}
+
+// 可选链的使用
+console.log(info?.friend?.friend?.name);
+```
+
+> 注意代码的健壮性
+
+### Global This(ES11)
+
+> 获取全局对象的GlobalThis
+
+```javascript
+// 获取某个环境下的全局对象
+
+// 浏览器下正确,但是node环境下错误
+console.log(window);
+
+// node下
+console.log(global);
+
+let myGlobalObj = undefined;
+if(window !== undefined){
+    myGlobalObj = window;
+}
+else{
+    myGlobalObj = global;
+}
+
+// ES11后
+console.log(globalThis);    // 浏览器中等于window,node中等于global
+```
+
+> 不用写复杂的`if...else`去给`myGlobalObj`赋值,可以直接用`globalThis`替代
+
+### 其他ES11
+
+1. Dynamic Import:动态导入
+2. Promise.allSettled
+3. import meta
+4. ...
+
+### FinalizationRegistry(ES12)
+
+- `FinalizationRegistry`对象可以让你在对象被垃圾回收时请求一个回调
+  - `FinalizationRegistry`提供:当一个在注册表中注册的对象被回收时,请求在某个时间点上调用一个清理回调(清理回调有时被称为finalizer)
+  - 可以通过调用`register`方法,注册任何你想要清理回调的对象,传入该对象和所含的值
+
+```javascript
+// FinalizationRegistry
+const finalRegistry = new FinalizationRegistry((value) => {
+    console.log("注册对象被销毁", value);
+});
+
+let obj = { name: "w" };
+let info = {};
+finalRegistry.register(obj, "obj_1");
+finalRegistry.register(info, "info_1");
+
+obj = null;
+```
+
+> 这个用浏览器测试比较方便,而且JS垃圾回收不是实时的可能得等一会  
+> 最后应该是输出obj_1和info_1