追求艺术的脚步
Be the change you want to see in the world.Things are always as hard as you think but always as easy as you do.

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 按需导入

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

例子待

 

posted on 2022-11-22 11:56  小笨笨  阅读(118)  评论(0编辑  收藏  举报