第十一节:ES6解构赋值、var/let/const详解(作用域提升、windows关系、暂时性死区、使用场景等)

一. ES6解构赋值

1. 含义

 允许按照一定模式,从数组和对象中提取值,对变量进行赋值的过程,叫做解构

2. 套路

      (1). 符号:[]

      (2). 别名:xx:别名

      (3). 默认值: xx='默认值'  或 xx:别名='默认值'

      (4). 剩余参数: ...xxx, 对象解构中是对象,数组解构中是数组

3. 数组的解构

 全部解构、解构某个元素、剩余参数的使用、解构+默认值

代码分享:
{
  let nameArrays = ["ypf1", "ypf2", "ypf3"];
  //全部解构
  let [name1, name2, name3] = nameArrays;
  console.log(name1, name2, name3); //ypf1 ypf2 ypf3
  //解构某一个元素
  let [, , myName3] = nameArrays; //ypf3
  console.log(myName3);
  //解构出来一个元素,剩下的放到数组里(形如剩余参数)
  let [item1, ...itemList] = nameArrays;
  console.log(item1, itemList); //ypf1 [ 'ypf2', 'ypf3' ]
  //解构的同时赋默认值
  let [l1, l2, l3, l4 = "ypf4"] = nameArrays;
  console.log(l1, l2, l3, l4); //ypf1 ypf2 ypf3 ypf4
}

4. 对象的解构

 全部解构、解构起别名、结构+默认值、解构+别名+默认值、剩余参数、复杂对象解构、函数中使用。

代码分享:

{
    let obj = {
        name: "ypf",
        age: 18,
        height: 1.82,
    };
    // 解构
    let { name, age, height } = obj;
    console.log(name, age, height);

    // 解构并起别名
    let { name: name1, age: age1, height: height1 } = obj;
    console.log(name1, age1, height1);

    // 解构+默认值,解构+别名+默认值
    let { name2 = "ypf2", name: name3 = "ypf3" } = obj;
    console.log(name2, name3); //ypf2 ypf

    // rest剩余参数的使用
    let { name: name4, ...restObj } = obj;
    console.log(name, restObj); //ypf { age: 18, height: 1.82 }

    // 复杂对象的解构
    let options = {
        size: {
            width: 100,
            myheight: 200,
        },
        items: ["Cake", "Donut"],
        extra: true, // something extra that we will not destruct
    };
    let {
        size: { width, myheight },
        items: [item1, item2], // assign items here
        title = "Menu", // not present in the object (default value is used)
    } = options;

    console.log(title); // Menu
    console.log(width); // 100
    console.log(myheight); // 200
    console.log(item1); // Cake
    console.log(item2); // Donut

    //函数中使用
    function foo({ name, age }) {
        console.log(name, age);
    }
    foo(obj); //ypf 18
}
View Code

5. 字符串的解构

 和数组解构类似

 代码分享:

{
  let str = "ypf001";
  let [a, b, c, d, e] = str;
  console.log(a, b, c, d, e); //y p f 0 0
}

 

二. var/let/const详解

1. let 基本使用

(1). let 不允许声明同名变量(var可以)

(2). 其它声明变量方面和var没有太大区别

代码分享:

    // 1.1 let 不允许声明同名变量 (下面代码报错)
    {
        let msg1 = "ypf1";
        let msg1 = "ypf2";
    }
    //1.2 var可以
    {
        var msg2 = "ypf1";
        var msg2 = "ypf2";
    }

2. const基本使用

(1).const不允许声明重复变量

(2).const用来声明常量,保存的数据一旦被赋值,就不能被修改

 特别注意:对于number、string、bool等数值类型不能直接修改,但是如果赋值的是引用类型,那么可以通过引用找到对应的对象,修改对象的内容。

【基本数据类型存储在 栈内存 中,引用数据类型存储在 堆内存 中然后在栈内存中保存 引用地址,对于引用类型而言,const 实际上保证的并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。】

 

(3). const声明变量的同时,必须赋值。

代码分享:

{
    //2.1 const不允许声明同名变量(下面代码报错)
    {
        const msg1 = "ypf1";
        const msg1 = "ypf2";
    }
    // 2.2 声明的常量,对于基本数据类型不允许修改(下面代码报错)
    {
        const name1 = "ypf1";
        name1 = "ypf2";
        console.log(name1); //报错 Assignment to constant variable.
    }
    // 2.3 声明的常量,如果是引用类型,修改引用类型的地址,不允许;但是修改引用类型的值,是可以的
    {
        const obj = {
            name: "ypf1",
            age: 20,
        };
        obj.age = "30"; // 允许的,改的堆内存中的值,栈中的存放的地址没有修改
        console.log(obj.age);
        obj = {
            name: "ypf2",
        }; // 不允许修改栈内存中存放的引用地址!! Assignment to constant variable.
    }
    //2.4 声明变量的同时必须赋值
    {
        //   const msg;  //必须初始化 "const" 声明
    }
}

3. var、let/const到底有没有作用域提升呢?(也叫变量提升)

(1). 什么是作用域提升?

【答:在声明变量的作用域中,如果这个变量可以在声明之前被访问,那么我们可以称之为作用域提升】

(2). var是具有作用域提升的,如下例子,输出undefined,并没有报错,说明能访问。

(3). let、const没有进行作用域提升,但是会在“解析阶段”被创建出来

代码分享:

{
    // 3.1 var是具有作用域提升的
    {
        console.log(a); //undefined
        var a = "ypf";
    }
    // 3.2 let/const创建的变量不能提前访问,所以不具有作用域提升功能
    {
        console.log(msg1); //Cannot access 'msg1' before initialization
        let msg1 = "ypf";
        console.log(msg2); //Cannot access 'msg2' before initialization
        const msg2 = "ypf";
    }
} 

4. var、let/const和windows的关系 (需要在index.html页面测试)

(1). 全局通过var来声明一个变量,事实上会在window上添加一个属性

(2). let、const是不会给window上添加任何属性的

代码分享: 

    // 4.1 全局通过var来声明一个变量,事实上会在window上添加一个属性
    {
        var msg1 = "ypf1";
        console.log(window.msg1); //ypf1
    }
    // 4.2 let/const不会给window上添加任何属性的
    {
        let msg2 = "ypf2";
        let msg3 = "ypf3";
        console.log(window.msg2); //undefined
        console.log(window.msg3); //undefined
    }

5. 块级作用域

(1). 在之前的ES5中,没有块级作用域,只有函数作用域和全局作用域

(2). var 没有块级作用域,let/const/function/class声明的类型都是具有块级作用域的

注:不同的浏览器有不同实现的(大部分浏览器为了兼容以前的代码, 让function是没有块级作用域)

(3). if/switch/for 都是具有块级作用域的

(4). 块级作用域应用场景

      有多个按钮,想给每个按钮添加一个事件,然后点击按钮依次输出每个按你对应的顺序,想要的结果是 0,1,2,3  如果直接用for循环+var,输出的结果永远是最大值,因为var是全局变量   这里引入两种解决方案,闭包和let块级作用域

代码分享: 

{
    // 5.4 if/switch/for 对应的块级作用域分析
    // if
    {
        if (true) {
            var msg1 = "ypf1";
            let msg2 = "ypf2";
            const msg3 = "ypf3";
        }
        console.log(msg1); //ypf1
        console.log(msg2); //ReferenceError: msg2 is not defined
        console.log(msg3); //ReferenceError: msg3 is not defined
    }
    // switch
    {
        var color = "red";
        switch (color) {
            case "red":
                var foo = "foo";
                let bar = "bar";
        }
        console.log(foo); //foo
        console.log(bar); //bar is not defined
    }
    // for(重点!!)
    {
        for (var i = 0; i < 3; i++) {
            console.log("循环内:" + i); // 0、1、2
        }
        console.log("循环外:" + i); // 3

        for (let i = 0; i < 3; i++) {
            console.log("循环内:" + i); // 0、1、2
        }
        console.log("循环外:" + i); // ReferenceError: i is not defined
    }
}

// 块级作用域应用场景
{
    const btns = document.getElementsByTagName("button");

    // 仔细理解!!!!
    // 参考博客:https://www.cnblogs.com/yaopengfei/p/14456993.html
    for (var i = 0; i < btns.length; i++) {
        btns[i].onclick = function () {
            console.log("第" + i + "个按钮被点击");
        };
    }

    // 方案1:采用闭包的方式解决
    for (var i = 0; i < btns.length; i++) {
        (function (n) {
            btns[i].onclick = function () {
                console.log("第" + n + "个按钮被点击");
            };
        })(i);
    }

    // 方案2:直接使用let即可
    for (let i = 0; i < btns.length; i++) {
        btns[i].onclick = function () {
            console.log("第" + i + "个按钮被点击");
        };
    }
}
View Code

6. let/const具有暂时性死区

(1). 什么是暂时性死区?

   【答:只要块级作用域内存在 let 命令,它所声明的变量就绑定在了这个区域,不再受外部的影响。】

(2). 案例如下

{
    var a = 5;
    if (true) {
        a = 6;
        let a;
    }
    // Uncaught ReferenceError: Cannot access 'a' before initialization
}

7. 补充

    for (let i = 0; i < names.length; i++) 中不可以使用const声明变量

    for (const item of names)  中可以使用const声明变量

8. 总结

(1). 对于var的使用:

    我们需要明白一个事实,var所表现出来的特殊性:比如作用域提升、window全局对象、没有块级作用域等都是一些历史遗留问题;

    其实是JavaScript在设计之初的一种语言缺陷;

    但是在实际工作中,我们可以使用最新的规范来编写,也就是不再使用var来定义变量了;

(2). 对于let、const:

    对于let和const来说,是目前开发中推荐使用的;

    我们会有限推荐使用const,这样可以保证数据的安全性不会被随意的篡改;

    只有当我们明确知道一个变量后续会需要被重新赋值时,这个时候再使用let;

    这种在很多其他语言里面也都是一种约定俗成的规范,尽量我们也遵守这种规范;

 

 

 

 

 

 

 

!

  • 作       者 : Yaopengfei(姚鹏飞)
  • 博客地址 : http://www.cnblogs.com/yaopengfei/
  • 声     明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
  • 声     明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
 
posted @ 2022-03-14 06:54  Yaopengfei  阅读(147)  评论(1编辑  收藏  举报