惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V
V2EX
C
Check Point Blog
博客园_首页
B
Blog
D
Docker
U
Unit 42
量子位
I
InfoQ
有赞技术团队
有赞技术团队
Martin Fowler
Martin Fowler
GbyAI
GbyAI
L
LangChain Blog
云风的 BLOG
云风的 BLOG
博客园 - Franky
美团技术团队
T
The Blog of Author Tim Ferriss
阮一峰的网络日志
阮一峰的网络日志
月光博客
月光博客
Vercel News
Vercel News
Recent Announcements
Recent Announcements
雷峰网
雷峰网
大猫的无限游戏
大猫的无限游戏
小众软件
小众软件
Google DeepMind News
Google DeepMind News

博客园 - 小笨笨

C# 面试题整理 《ASP.NET Core 6 框架揭秘》第五章读书笔记 - 配置选项(上) 《ASP.NET Core 6 框架揭秘》第四章读书笔记 - 文件系统 Entity Framework Core 笔记 - 入门 《ASP.NET Core 6 框架揭秘》第三章读书笔记 - 依赖注入(下) React 学习笔记之三 - 基于 Redux 状态管理 React 学习笔记之二 - React 详解 《Kafka 权威指南》之二 - 安装 Kafka 《Kafka 权威指南》读书笔记之一 - 初识Kafka 微服务实战读书笔记-第一章 缠论 jQuery 实战读书笔记之第六章:事件本质 jQuery 实战读书笔记之第五章:使用 jQuery 操作页面 jQuery 实战读书笔记之第四章:使用特性、属性和数据 jQuery 实战读书笔记之第三章:操作 jQuery 集合 jQuery 实战读书笔记之第二章:选择元素 TCP/IP详解读书笔记:ARP-地址解析协议 TCP/IP详解读书笔记:链路层 TCP/IP详解读书笔记:概述
React 学习笔记之一 - ES6 基础
小笨笨 · 2022-11-22 · via 博客园 - 小笨笨

1.1 let 及 const

1.1.1 let 命令

用 var 声明变量有变量提升的情况。

1     console.log(a);
2     var a = 1;

如果没有第二行的声明,那么会看到“a is not defined”的错误。但有了第二行,打印结果就是“undefined”。

出现这种结果的原因就是 var 声明变量时的变量提升机制,等同于:

1     var a;
2     console.log(a);
3     a = 1;

针对上面的情况,ES6 引入 let。let 不仅没有变量提升的问题,同时还把作用于局限在代码块中,也就是声明作用域之外无法访问。

第一种是函数内部块级作用域。

1     function test(){
2         let a;
3     }
4 
5     test();
6     console.log(a);  // Uncaught ReferenceError: a is not defined

第二种是“{}”之间的区域。

    {
        let a;
    }

    console.log(a);  // Uncaught ReferenceError: a is not defined

同时,let 禁止重复声明。

1.1.2 const 命令

 声明常量,声明时必须进行初始化。

如果声明的是对象,仅可以修改对象的属性值。

 1     const obj = {
 2         name: "张三",
 3         age: 20
 4     };
 5 
 6     obj.name = "李四";
 7 
 8     console.log(obj);
 9 
10     obj = {};  // Uncaught TypeError: Assignment to constant variable.

通过 Object.freeze 函数冻结对象,可以确保对象属性不被修改,但仅限直接属性。

1     const obj = {
2         name: "张三",
3         age: 20
4     };
5 
6     Object.freeze(obj);  // 冻结对象
7 
8     obj.name = "李四";
9     console.log(obj);   // name 还是张三,并未改变

深度冻结对象示例:

 1     const obj = {
 2         name: "张三",
 3         age: 20,
 4         family: {
 5             name: "张安",
 6             age: 48
 7         }
 8     };
 9 
10     // 深度冻结函数
11     function deepFreeze(obj){
12         Object.freeze(obj);
13 
14         for(let key in obj){
15         // hasOwnProperty() 方法会返回一个布尔值,指示对象自身属性中(非继承属性)是否具有指定的属性,
16             // 如果 object 具有带指定名称的属性,则 hasOwnProperty 方法返回 true,否则返回 false。此方法不会检查对象原型链中的属性;
17        // 该属性必须是对象本身的一个成员。
18        // 如果要判断继承属性,通过原型链prototype判断,Object.prototype.hasOwnProperty(''toString'')
19         if(obj.hasOwnProperty(key) & typeof obj[key] === "object"){
20                Object.freeze(obj[key]);
21              }
22      }
23     }
24 
25     deepFreeze(obj);
26     obj.family.age = 50; // 并未修改成功,值还是 48
27     console.log(obj);

1.1.3 临时死区(temporal dead zone)

通过 let 与 const 声明的常量,不会存在变量提升的情况,会放在临时死区。

1 // 如果没有第二行,那么会打印 undefined
2 // 第二行存在的情况下,打印 Uncaught ReferenceError: Cannot access 'a' before initialization
3 console.log(typeof a); 
4 let a;

1.1.4 循环中的 let 及 const

var 作用域。

 1     let funArr = [];
 2     for(var i = 0; i < 5;i++){
 3         funArr.push(function(){
 4             console.log(i);
 5         });
 6     }
 7 
 8     // 这里会打印出 5 个 5
 9     funArr.forEach(item => {
10         item();
11     });

在上面的程序中,i 是保存在全局作用域中。

要解决这样的问题,可以利用闭包创建独立作用域。

 1     let funArr = [];
 2 
 3     for(var i = 0; i < 5; i++){
 4         (function(i){
 5             funArr.push(function(){
 6                 console.log(i);
 7             })
 8           })(i)
 9     };
10 
11     funArr.forEach(item => {
12         item();
13     });

利用 let 及 const 提供的块级作用域可以简化并达到同样的效果。

 1     let funArr = [];
 2         
 3     for(let i = 0; i < 5; i++){
 4         funArr.push(function(){
 5             console.log(i);
 6         });
 7     }
 8 
 9     // 这里能正常打印出 0 - 5
10     funArr.forEach(item => {
11         item();
12     });

由于 const 不能被重复赋值,因此这里的 let 不能换成 const。但 const 可以和 for-in 和 for-of 循环中使用。

 1     let obj = {
 2         name: '张三',
 3         age: 20
 4     };
 5 
 6     // 打印出 name, age
 7     for(const i in obj){
 8         console.log(i);
 9     }
10 
11     let arr = ['张三', '李四', '狗蛋'];
12     // 依次打印出数组中的值
13     for(const value of arr){
14         console.log(value);
15     }

 1.2 解构赋值

1.2.1 数组的结构

 1   // 运行后 a = 10, b = 20, c = 30
 2   // 等号右边的值按照顺序依次赋给等号左边的值
 3   let [a, b, c] = [10, 20, 30];
 4 
 5   // 左右不对应的情况,d = 10, e = 20
 6   let [d, e] =  [10, 20, 30];
 7 
 8   // 通过逗号隔开省略的元素,f = 30
 9   let [,, f] =  [10, 20, 30];
10 
11   // i 为 undefined
12   let [g, h, i] = [10, 20];
13 
14 
15   // 通过“...”把特定的元素放在变量里,k 为 [20, 30]
16   let [j,...k] = [40, 50, 60];
17 
18   let [m, n] = [1, 2];
19   // 通过解构赋值简化引入中间变量互换值
20   [n, m] = [m, n];
21   console.log('m = ' + m + ', n = ' + n);

1.2.2 对象的解构

1     let obj = {
2         name: '张三',
3         age: 20
4     };
5 
6     // 结构中的名称必须和对象里的下标保持一致,不然值就是 undefined
7     let {name, age} = obj;
8     console.log('name = ' + name + ', age = ' + age);

上面的例子中,name 和 age 的顺序无关,只要名字相同就可以。

多层对象的处理方式如下:

 1   let obj = {
 2      name: '张三',
 3       age: 20,
 4       family:{
 5         father: '张一',
 6          mother: '李二'
 7       }
 8    };
 9 
10    // 如果想要打印 family,会报 Uncaught ReferenceError: family is not defined
11    let {name, age, family: {father, mother}} = obj;
12    onsole.log('name = ' + name + ', father = ' + father);

别名处理如下:

1   let obj = {
2      name: '张三',
3       age: 20
4     };
5 
6     // name 和 age 是 Uncaught ReferenceError: age is not defined
7     let {name : myName, age : myAge} = obj;
8     console.log('myName = ' + myName + ', myAge = ' + myAge);

1.2.3 解构的默认值及参数的结构

设了默认值,如果解构时没有值,那就取默认值。

1   let obj = {
2       name: '张三',
3        age: 20
4     };
5         
6     // height 设置了默认值,后面没有对应值的情况下,就取默认值
7     let {name, age, height = 175} = obj;
8     console.log('name = ' + name + ', age = ' + age + ', height = ' + height);    

函数参数解构的例子。

 1   let obj = {
 2      name: '张三',
 3       age: 20
 4    };
 5         
 6    // 注意这里参数的写法,名称还是得相同,顺序无关
 7    function fn({name, age} = {}){
 8      console.log('name = ' + name + ', age = ' + age)
 9    }
10 
11    fn(obj);
12 
13    for(const i in obj){
14      console.log(i);
15    }

1.3 字符串扩展

1.3.1 Unicode 支持

超过 uFFFF 会打印成两部分/前4位+后四位(首先将0x20BB7-10000计算超出的部分,将超出的部分使用20个2进制表示,将前十位与0xD800 相加转成16进制,后10位与0xDC00 相加),来自 https://blog.csdn.net/weixin_47295886/article/details/127076787。

 加了个大括号后,对于超过 uFFFF 的,就不会打印出两个字符,而是一个字符。

console.log('\u{20BB7}');

1.3.2 新增字符串方法

ES5 中有一个 indexOf 方法判断一个字符串是否包含在另一个字符串中,找到返回索引位置,没有找到返回 -1.

ES6 新增了以下方法。

includes():返回布尔值,是否找到了字符串。

startWith():返回布尔值,被检测字符串是否在源字符串的头部。

endsWith():返回布尔值,被检测字符串是否在源字符串的结尾。

repeat():返回新的字符串将源字符串循环指定次数。

1   let res = 'a'.repeat(5);
2     // 打印出 aaaaa
3     console.log(res);

1.3.3 模板字符串

 1   let persons = [{
 2       name: '张三',
 3         age: 23
 4      }, {
 5         name: '李四',
 6         age: 25
 7        }, {
 8         name: '王五',
 9         age: 27
10     }];
11 
12     let str = '';
13     for(let i = 0; i < persons.length; i++){
14       // 模板字符串使用反引号来表示,嵌入的变量通过“${}”表示
15        str += `姓名是:${persons[i].name},年龄是:${persons[i].age}\n`;
16     }

“${}”支持三目运算符。

1.4 Symbol

ES5 中提供的 6 种数据类型:Undefined,NULL,布尔值(Boolean),字符串(String),数值(Number),对象(Object)。

ES6 提供了 Symbol 来表示唯一的值,每一个创建的 Symbol 都是唯一的。

1.7 异步编程

1.7.1 ES5 中的异步

JavaScript 引擎是基于事件循环的概念实现的。引擎会把任务放在一个任务队列里,通过事件循环机制执行任务队列里的任务。

异步任务不进入主线程。在 ES5 标准中是通过回调来解决执行顺序问题。

1.7.2 Promise 基本语法

实例化后可以得到 Promise 对象。Promise 对象有 3 种状态:pending、resolved(fulfilled)、rejected。

 1   let p1 = new Promise(function(){
 2 
 3    });
 4    console.log(p1); // pending
 5 
 6    let p2 = new Promise(function(resolve, reject){
 7      resolve('success...');
 8    });
 9    console.log(p2);  // fulfilled       
10 
11    let p3 = new Promise(function(resolve, reject){
12      reject('reject...');
13    });        
14    p3.then(res => {
15      console.log(res);
16    }, err => {
17      console.log(err);
18    });
19    console.log(p3); // rejected

1.7.3 Promise 处理异步问题

使用 Promise 对象提供的 then 方法,在每个 then 方法又返回一个 Promise 对象,这样就可以实现 then 的链式调用。

 1   function asyncFn(){
 2      return new Promise((resolve, reject) => {
 3         setTimeout(() => {
 4            console.log('异步逻辑');
 5             resolve('success..');
 6          }, 1000);
 7        });
 8     }
 9 
10     asyncFn().then( res => {
11       console.log(res);
12        return asyncFn();
13     }).then(res => {
14       console.log(res);
15     });

ES7 标准中新增了 async 及 await 关键字,可以让 Promise 更简单易用。

 1   let print = function(delay, msg){
 2      return new Promise(function(resolve, reject){
 3         setTimeout(function(){
 4            console.log(msg);
 5             resolve();
 6          }, delay);                
 7        });
 8     }
 9 
10     async function asyncFunc(){
11       await print(1000, 'Hello1');
12        await print(4000, 'Hello4');
13        await print(3000, 'Hello3');
14     }
15     asyncFunc();

1.7.4 Promise 里的其他用法

可以使用 Promise.all 执行多个 Promise 对象。

 1     let p = new Promise(function(resolve, reject){
 2       resolve('p1...');
 3     });
 4 
 5     let p2 = new Promise(function(resolve, reject){
 6       resolve('p2...');
 7     });
 8 
 9     let p3 = new Promise(function(resolve, reject){
10       resolve('p3...');
11     });
12 
13     Promise.all([p, p2, p3]).then(res => {
14       console.log(res);    // 输出 ['p1...', 'p2...', 'p3...']
15 });

Promise.race 会返回最先执行完的结果。

 1   let p1 = new Promise(function(resolve, reject){
 2      setTimeout(() => {
 3         resolve('p1...');
 4       }, 3000);            
 5     });
 6 
 7     let p2 = new Promise(function(resolve, reject){
 8       setTimeout(() => {
 9          resolve('p2...');
10        }, 1000);
11     });
12 
13     let p3 = new Promise(function(resolve, reject){
14       setTimeout(() => {
15          resolve('p3...');
16         }, 2000);
17     });
18 
19     Promise.race([p1, p2, p3]).then(res => {
20       console.log(res);
21     }).catch(err => {
22       console.log(err);   // 输出 p2...
23     });

1.8 模块化

在 ES5 中采取 AMD、CMD 来实现前段的模块化,比较典型的框架是 sea.js 和 require.js。

ES6 中提供了自己的模块化方式,在 script 标签中声明 type="module" 。

1.8.1 导入导出基本使用

 1 // a.js
 2 console.log('我是 a 模块');
 3 
 4 let obj = {
 5     name: '张三',
 6     age: 20
 7 }
 8 
 9 export let a = 10;
10 export default obj;  // 默认导出只能有一个
11 
12 
13 //1.html
14 <script type="module">
15   import A, {a} from './a.js';   //通过 export 导出的需要通过大括号解构变量;可以使用任何名称导入默认导出, 
16   console.log(A, a);
17 </script>

1.8.2 导入导出变式写法

通过 as 来默认导出,也可以通过 as 起别名。

 1 // a.js
 2 console.log('我是 a 模块');
 3 
 4 let obj = {
 5     name: '张三',
 6     age: 20
 7 }
 8 
 9 export let a = 10;
10 let b = 20;
11 export {b as c};  // 导出时给 b 取别名为 c
12 export { obj as default};  // 默认导出
13 
14 
15 // 1.html
16 <script type="module">
17   import A, {a, c as d} from './a.js';  // 导入时给 c 取别名为 d
18    console.log(A, a, d);
19 </script>

通过通配符导入所有导出。

待理解。

1.8.3 按需导入

延迟导入,当需要加载的时候再加载对应的模块来节省性能。

例子待