ES6新特性(入门)

本文章只列举了一些,具体了解请查看:ES6入门教程

一、let和const命令

​ JavaScript 是大家所了解的语言名称,今天学习的 ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言 的下一代标准。它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。

​ 那么,ECMAScript 和 JavaScript 到底是什么关系? ECMAScript 和 JavaScript 的关系是,前者是后者 的规格,后者是前者的一种实现。日常场合,这两个词是可以互换的。其中 ES6 是 ECMAScript 的一个标准,2015 年由 ECMAScript 标准委员会在 2015 年 6 月正式发布,并且从 ES6 开始,每年发布一个版本,版本号比年份最 后一位大 1。

我们为什么要学习 ES6?

  • ES6 的版本变动内容最多,具有里程碑意义;
  • ES6 加入许多新的语法特性,编程实现更简单、高效;
  • ES6 是前端发展趋势,就业必备技能。

1、let命令的基本用法

let 关键字用来声明变量,作用和 var 类似,不过使用 let 声明的变量有几个特点

  1. 不允许重复声明。
  2. 块级作用域。
  3. 不存在变量提升。

特性1:不允许重复声明变量

  • 任务1:使用 let 声明变量,观察与 var声明变量的区别。

    <script type="text/javascript">
        var a = 1;
        var a = 2;
        // 不报错,结果为:2
        console.log(a);
    
        //let声明的变量不允许重复声明
        let b = 3;
        let b = 4;
        //控制台报错,报错信息:Uncaught SyntaxError: Identifier 'b' has already been declared
        console.log(b);
    
    </script>
    
    • 结果显示,变量b已经声明,不能多次声明。

    • var 可以声明多次,let 只能声明一次。

特性2: 块级作用域

​ let 声明的变量有严格局部作用域,只能块级作用域中使用

  • 任务 2:观察块级作用域的效果

    <script type="text/javascript">
        {
            var a = 1;
            let b = 2;
        }
        // 结果为:1
        console.log(a);
        // 报错
        console.log(b);
    
    
    </script>
    
    • 结果:

      在这里插入图片描述

    从上面的示例,可以总结出:var 声明的变量可以全局操作,而 let 声明的变量虽然用法类似于 var,但是 所声明的变量,只在 let 命令所在的代码块内有效。包括 if、else、while、for 使用 let 都也只能在代码快中 有效。

  • 例如:for 循环的计数器,就很合适使用 let 命令。

    <script type="text/javascript">
        for (var i = 0; i<=10; i++) {
    
        }
        // 结果为:11
        console.log(i);
    
        for (let x = 0; x<=10; x++) {
    
        }
        // 报错
        console.log(x);
    
    </script>
    
    • 结果:

      在这里插入图片描述

计数器 i 只在 for 循环体内有效,在循环体外引用就会报错。

特性3:不存在变量提升

​ var 命令会发生"变量提升"现象,即变量可以在声明之前使用,值为 undefined。按照编程逻辑思维,变量 只应该在声明语句之后才可以使用。

为了纠正这种现象,let 命令改变了语法行为,它所声明的变量一定要在声明后使用,否则报错。

  • 示例如下:

    <script type="text/javascript">
    			
        //没有定义变量a的时候报错:Uncaught ReferenceError: a is not defined
        //定义变量a以后,打印undefined。
        console.log(a);
        var a = 1;
    
        //不管有没有定义变量b,都会报错:Uncaught ReferenceError: b is not defined
        console.log(b);
        let b = 2;
    
    </script>
    

对于 let 的特性,就先讲到这里,当然 let 还有很多特性,例如暂存性死区、不影响作用域链等,大家可以 下去自学。总之,以后声明变量多使用 let 就对了。

2、const命令的基本用法

const 关键字用来声明一个只读的常量,一旦声明,常量的地址就不能改变。什么是常量那?常量就是值(内存地址)不能变化的量。const 声明有以下特点:

  1. 块级作用域
  2. 标识符一般为大写(规范)
  3. 声明必须赋初始值
  4. 值不允许修改
  5. 不允许重复声明

特性1:块级作用域

  • 示例代码;

    <script type="text/javascript">
    
        {
            const a = 1;
    	}
    
        //报错:Uncaught ReferenceError: a is not defined
        console.log(a);
    
    </script>
    
  • 任务3:请问运行下列代码后,三行代码的运行结果是什么。

    <script type="text/javascript">
        {
            const A = 1;
            {
                const A = 2;
                // 先打印,结果:2
                console.log(A);
            }
            // 后打印,结果:1
            console.log(A);
        }
        //报错:Uncaught ReferenceError: A is not defined
        console.log(A);
    </script>
    
    • 结果:

      在这里插入图片描述

特性 2:地址不允许修改

  • 示例代码

    <script type="text/javascript">
        const a = 1;
        // 报错:Uncaught SyntaxError: Identifier 'a' has already been declared
        const a =2;
    </script>
    
    • 结果:

      在这里插入图片描述

    上面代码表明改变常量的值会报错,const 声明的变量不得改变值。这意味着,const 一旦声明变量,就必 须立即初始化,不能留到以后赋值。

特性 3:声明必须赋初始

  • 示例代码

    <script type="text/javascript">
        // 报错:Uncaught SyntaxError: Missing initializer in const declaration
        const a;
    </script>
    
    • 结果:

      在这里插入图片描述

const本质:

​ const 实际上保证的并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。对于简单类型的数 据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据 (主要是对象和数组),变量指向的内存地址,保存的只是一个指针,const 只能保证这个指针是固定的,至于 它指向的数据结构是不是可变的,就完全不能控制了。

​ 因此,将一个对象或数组声明为常量必须非常小心。

  • 任务4:使用 const 声明常量对象,观察常量对象的可变与不可变。

    <script type="text/javascript">
        const stu = {
            name:"张三",
            age:20
        }
        //结果:{name: "李四", age: 20}
        console.log(stu);
    
        stu.name = "李四";
        //结果:{name: "李四", age: 20}
        console.log(stu);
    
        //报错:es6.html:21 Uncaught TypeError: Assignment to constant variable.
        stu = {};
    
    </script>
    
    • 结果:

      在这里插入图片描述

    上面代码中,常量 obj 储存的是一个地址,这个地址指向一个对象。不可变的只是对象指定的这个地址,也 就是不能把 foo 指向另一个地址,但对象本身是可变的,可以为其添加新属性或修改属性的值。

  • 任务5:使用 const 声明常量对象,观察常量数组的可变与不可变。

<script type="text/javascript">
    const TV = ["熊出没","喜羊羊与灰太狼","猪猪侠"];
    TV.push("电击小子"); //可执行
    TV.length = 0; //可执行

    //报错:Uncaught TypeError: Assignment to constant variable.
    TV = ["小猪佩奇"];

</script>

上面代码中,常量 TVS 是一个数组,这个数组本身是可读可写的可操作的,但是如果将另一个数组赋值 给 TVS,就会报错。

3、let、const 和 var 命令的区别

  1. 使用 var 声明的变量,其作用域为该语句所在的函数内,且存在变量提升现象。
  2. 使用 let 声明的变量,其作用域为该语句所在的代码块内,不存在变量提升。
  3. 使用 const 声明的是常量,在后面出现的代码中不能再修改该常量的值。

在这里插入图片描述

二、变量的解构赋值

​ ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构赋值。可以帮助我们更加简便 的从数组和对象中提取值。

1、数组解构赋值

  • 之前,要从数组取值为变量赋值,只能直接指定值。
<script type="text/javascript">
    const arr = [1,2,3]
    let a = arr[0];
    let b = arr[1];
    let c = arr[2];
</script>
  • 现在ES6允许写成下面这样:
<script type="text/javascript">
    const arr = [1,2,3]
    let [a,b,c] = arr;

    console.log(a);
    console.log(b);
    console.log(c);
</script>

上面代码表示,可以从数组中提取值,按照对应位置,对变量赋值。本质上,这种写法属于“模式匹配”, 只要等号两边的模式相同,左边的变量就会被赋予对应的值。在解构中,有下面两部分参与:

  • 解构的源,解构赋值表达式的右边部分。
  • 解构的目标,解构赋值表达式的左边部分。

当解构的目标和解构的源匹配模式相同时,就可以将源中的数据赋值给变量。需要注意的是:数组的解构和 顺序有关,顺序的调整会改变变量的值。

  • 在数组模型的解构中,还有以下操作,大家可以了解一下:

    //基本操作
    let [a, b, c] = [1, 2, 3]; //结果:a = 1 b = 2 c = 3
    //可嵌套操作
    let [a, [[b], c]] = [1, [[2], 3]]; //结果:a = 1 b = 2 c = 3
    //可忽略操作
    let [a, , b] = [1, 2, 3]; //结果:a = 1 b = 3
    //剩余运算符
    let [a, ...b] = [1, 2, 3]; //结果:a = 1,b = [2, 3]
    //不完全解构
    let [a = 1, b] = []; //结果:a = 1, b = undefined
    let [x, y, z] = [1, 2]; //结果:x = 1, y 
    

    这里需要注意的是如果解构不成功,变量则未赋值,值等于 undefined。

2、对象解构赋值

​ 对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的 属性没有次序,变量必须与属性同名,才能取到正确的值。对象解构允许我们使用变量的名字匹配对象的属性, 将对象的属性赋给变量。

  • 任务 3-1:使用解构赋值操作对象,观察如果属性和变量不一致时会出现什么问题。

    <script type="text/javascript">
        const STU = {
            name:"张三",
            age:20
        };
    
        let {name,age} = STU;
    
        //张三
        console.log(name);
        //20
        console.log(age);
    
    </script>
    

    上面代码的例子,等号左边的两个变量的次序,与等号右边两个同名属性的次序不一致,但是对取值完全没 有影响。反而最后一行代码,因为变量名没有对应的同名属性,导致取不到值,导致变量值是 undefined。

  • 如果变量名与属性名不一致,必须写成下面这样:

<script type="text/javascript">
    const STU = {
        name:"张三",
        age:20
    };

    let {name: name2,age: age2} = STU;

    //张三
    console.log(name2);
    //20
    console.log(age2);

</script>

3、特殊解构赋值

3.1、字符串解构赋值

字符串也可以解构赋值。这是因为此时,字符串被转换成了一个类似数组的对象。字符串解构类似数组解构, 可以按照顺序将字符赋值给变量。

<script type="text/javascript">
    const str = "hello!";
    let [a,b,c,d,e,f] = str;
	//h
    console.log(a);
	//e
    console.log(b);
	//l
    console.log(c);
	//l
    console.log(d);
	//o
    console.log(e);
	//!
    console.log(f);
</script>
  • 类数组的对象都有一个 length 属性,因此还可以对这个属性解构取值赋值。

    <script type="text/javascript">
        const str = "hello!";
        let {length: len} = str;
    
        //6
        console.log(len);
    
    </script>
    
3.2、函数参数解构赋值

​ 我们常用的函数中的参数函数也可以使用解构赋值,形参可以重复利用数组、对象、字符串等解构的方式,将传 入的参数解析并赋值给变量。

  • 示例如下:

    function add([a, b]){
        //将传入的数组的值赋值给 a 和 b 变量
        return a + b;
    }
    add([1, 2]); //结果:3
    

    当频繁使用对象方法、数组元素,就可以使用解构赋值形式。对于解构赋值我们暂时先讲到这里,解构中还有很 多的用法需要大家去探索哦。

三、字符串扩展

1、模板字符串

​ 在之前要声明一个字符串,有单引号’'和双引号""两种写法,ES6 引入了模板字符串``。模板字符串(template string)是增强版的字符串, 用反引号(`)标识。

特点:

  • 字符串中可以出现换行符;

  • 可以使用${xxx}作为占位符,形式输出大括号中变量或表达式;

  • 任务 4:使用引号和模板字符串实现换

    //使用双引号实现换行
    let comedy1 = "<ul>"
        + "<li>沈腾</li>" 
        + "<li>马丽</li>"
        + "<li>艾伦</li>" 
    +"</ul>";
    
    //使用模板字符串实现换行
    let comedy2 = `<ul>
            <li>沈腾</li>
            <li>马丽</li>
            <li>艾伦</li>
        </ul>`;
    

    ​ 根据示例效果我们可以发现,模板字符串编写的字符串不存在因为换行造成的代码无法运行的问题,并且使用起来非常方便。

  • 任务5:使用 ${xxx} 完成变量输出。

let wl = "庄强";
let fc = "大聪明";
let str = `卧龙${wl}和凤雏${fc},得一可得天下!!!`;

//结果:卧龙庄强凤雏大聪明,得一可得天下!!!
console.log(str); 

注意:${}是固定写法,千万不要写错哦!!!

2、字符串拓展函数

​ 传统上,JavaScript 只有 indexOf 方法,可以用来确定一个字符串是否包含在另一个字符串中。ES6 又提供 了三种新方法。

  • includes():返回布尔值,表示是否找到了参数字符串。

  • startsWith():返回布尔值,表示参数字符串是否在源字符串的头部。

  • endsWith():返回布尔值,表示参数字符串是否在源字符串的尾部。

  • 任务6:使用字符串拓展函数,并观察结果。

    <script type="text/javascript">
        let f4 = `东北F4:莱昂纳多·小沈阳、约翰尼·宋小宝、克里斯蒂安·刘能和尼古拉斯·赵四!!!`;
        //判断是否以"东北"开头
        console.log(f4.startsWith("东北"))//true
        //判断是否以"四"结尾
        console.log(f4.endsWith("四"))//false
        //判断是否包含"尼古拉斯"
        console.log(f4.includes("尼古拉斯"))//true
    
    </script>
    

    这三个方法都支持第二个参数 n,表示开始搜索的位置,n 从 0 开始。但是使用第二个参数 n 时,endsWith 的行为与其他两个方法有所不同。它针对前 n 个字符,而其他两个方法针对从第 n 个位置直到字符串结束。

  • 任务7:观察字符串拓展函数,增加第二个参数 n,并观察结果。

<script type="text/javascript">
    let f4 = `东北F4:莱昂纳多·小沈阳、约翰尼·宋小宝、克里斯蒂安·刘能和尼古拉斯·赵四!!!`;
    //索引都是从0开始的!!!
    //判断从第四个索引开始,是否开头为"true"
    console.log(f4.startsWith(":",4))//true
    //判断前4个字符是否以4结尾
    console.log(f4.endsWith("4",4))//true
    //判断从第9个开始,是否包含"小"字
    console.log(f4.includes("小",9))//true
    //判断从第20个开始,是否包含"小"字
    console.log(f4.includes("小",20))//false

</script>

四、函数和对象优化

1、函数优化

1.1、函数参数默认值
  • 在 ES6 以前,我们无法给一个函数参数设置默认值,只能采用变通写法:
<script type="text/javascript">
    function add(a,b){
        b = b || 1
        return a+b;
    }

    //3
    console.log(add(2))

</script>
  • 在 ES 新特征中,允许这么写:直接给参数写上默认值,没传就会自动使用默认值

    <script type="text/javascript">
        function add(a,b=1){
            return a+b;
        }
    
        //3
        console.log(add(2))
    
    </script>
    

    这样做的好处在于,首先对于阅读代码的人,可以立刻意识到哪些参数是可以省略的,不用查看函数体或文 档;其次,有利于将来的代码优化,即使未来的版本在对外接口中,彻底拿掉这个参数,也不会导致以前的代码 无法运行。

需要注意的是:

  • 使用函数默认参数时,不允许有同名参数。
  • 只有在未传递参数,或者参数为 undefined 时,才会使用默认参数,null值被认为是有效的值传递。

五、扩展运算符(…)

1、函数参数

rest 参数也叫做不定参数,用来表示不确定参数个数。语法是:...变量名。由...加上一个具名参数标识符组成。

基本用法: 函数名(…不定参数名)。

  • 示例如下:

    <script type="text/javascript">
            function fn(...vals){
            console.log(vals);
            console.log("长度:" + vals.length);
        }
        //结果:(3) [1, 2, 3]	长度:3
        fn(1,2,3);
    
        //结果:(6) [1, 2, 3, 5, 4, 3]	长度:6
        fn(1,2,3,5,4,3);
    
    </script>
    

    通过上面的运行发现,rest 不定参数拿到是一个数组,这里就可以使用到数组的一些 Api,例如 some、map 方法或者 length 属性等,提高了对参数处理的灵活程度。但是需要注意的是不定参数只能放在参数组的最后, 并且一个函数有且仅有一个不定参数。

2、输出数组

​ 扩展运算符(spread)是三个点(…)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数 序列。

  • 在之前,如果需要将一个数组的值输出,需要这么做:

    <script type="text/javascript">
        let arr = [1,2,3];
        //结果:1 2 3
        console.log(arr[0] + " " + arr[1] + " " + arr[2]);
    
    </script>
    
  • 现在有了扩展运算符,你可以这么写:

    <script type="text/javascript">
        let arr = [1,2,3];
        //结果:1 2 3
        console.log(...arr);
    </script>
    

3、合并数组

  • 任务1:使用扩展运算符完成合并数组。

    <script type="text/javascript">
        let arr = [1,2,3];
        let arr2 = [4,5,6];
    
        //旧写法		结果:(6) [1, 2, 3, 4, 5, 6]
        console.log(arr.concat(arr2));
    
        //新写法		结果:(6) [1, 2, 3, 4, 5, 6]
        console.log([...arr,...arr2]);
    </script>
    
  • 还可以这么写:

    <script type="text/javascript">
        let arr = [1,2,3];
        let arr2 = [4,5,6];
    
        arr.push(...arr2);
        //结果:(6) [1, 2, 3, 4, 5, 6]
        console.log(arr);
    
    </script>
    

4、克隆数组

  • 任务2:使用扩展运算符完成克隆数组。

    <script type="text/javascript">
        let arr = [1,2,3];
        let arr2 = [...arr];
    
        //结果:(3) [1, 2, 3]
        console.log(arr2);
    
    </script>
    

5、将伪数组转为数组

  • 任务3:使用扩展运算符将div伪数组转为数组。
<body>
		
    <div>1</div>
    <div>2</div>
    <div>3</div>

    <script type="text/javascript">
        let divs = document.getElementsByTagName("div");

        //结果:HTMLCollection(3) [div, div, div]
        console.log(divs);

        let divarr = [...divs];
        //结果:(3) [div, div, div]
        console.log(divarr);

    </script>
</body>

6、拷贝对象

  • 任务4:使用扩展运算符完成对象拷贝。
<script type="text/javascript">
    let stu = {
        name:"张三",
        age:20
    }

    let stus = {...stu};
    //结果:{name: "张三", age: 20}
    console.log(stus);

</script>

示例中的拷贝其实是深拷贝,有兴趣的同学可以去学习一下深浅拷贝。

  • 浅拷贝(shallowCopy)只是增加了一个指针指向已存在的内存地址。
  • 深拷贝(deepCopy)是增加了一个指针并且申请了一个新的内存,使这个增加的指针指向这个新的内存。

6、对象取值合并

  • 任务5:使用扩展运算符完成对象合并的操作。

    <script type="text/javascript">
        let st1 = {name:"张三",age:20};
        let st2 = {age:21};
        let st3 = {sex:"男"};
    
        st3 = {...st1,...st2,...st3};
    
        //结果:{name: "张三", age: 21, sex: "男"}
        console.log(st3);
    
    </script>
    

六、箭头函数

ES6 允许使用「箭头」(=>)定义函数,提供了一种更加简洁的函数书写方式。基本语法是:参数 => 函数

  • 之前我们声明一个函数是这么写的:

    <script type="text/javascript">
        console.log("-----------------------1参函数----------------------------")
      let fun1 = function(val){
            return val;
        }
        console.log(fun1(1));
    </script>
    
  • ES6写法:

    <script type="text/javascript">
        let fun1 = val => val;
    </script>
    

注意点1:如果函数参数只有一个,可以省略(),除此之外无论是没有参数还是多个参数都不能省略小括号()。

  • 示例如下:

    <script type="text/javascript">
        console.log("-----------------------1参函数----------------------------");
    
        //普通写法
        let fun1 = function(val){
            return val;
        }
        console.log(fun1(1));
    
        //箭头写法
        let fun2 = val => val;
        console.log(fun2(2));
    
    
        console.log("-----------------------无参函数----------------------------");
    
        //普通写法
        let fun3 = function(){
            console.log("3");
        }
        fun3();
    
        //箭头写法
        let fun4 = () => console.log("4");
        fun4();
    
        console.log("-----------------------多参函数----------------------------");
    
        //普通写法
        let fun5 = function(a,b){
            return a+b;
        }
        console.log(fun5(2,3));
    
        //箭头函数
        let fun6 = (a,b) => a+b;
        console.log(fun6(3,3));
    </script>
    
    • 结果:

      在这里插入图片描述

注意点2:当箭头函数函数体有多行语句,用{}大括号,包裹起来,表示代码块。当只有一行语句,可以省略{}大括号。当 只有一行语句并且是 return 返回结果时,可以省略{}和return, 返回值会自动返回。

  • 示例如下:

    <script type="text/javascript">
    	console.log("-----------------------方法体内有多行----------------------------");
    			
        //普通写法
        let fun7 = function(a,b){
            let sum = a+b;
            return sum;
        }
        console.log(fun7(3,4));
    
        //箭头函数
        let fun8 = (a,b) => {
            let sum = a+b;
            return sum;
        }
        console.log(fun8(4,4));
    
        console.log("-----------------------方法体为单行----------------------------");
    
        //普通写法
        let fun9 = function(a,b){
            return a+b;
        }
        console.log(fun9(4,5));
    
        //箭头函数
        let fun10 = (a,b) => a+b;
        console.log(fun10(5,5));
    </script>
    
    • 结果:

      在这里插入图片描述

注意点3: 当箭头函数要返回对象的时候,为了区分于代码块,要用小括号()将对象包裹起来。

  • 示例如下:

    <script type="text/javascript">
    	console.log("-----------------------方法体为对象----------------------------");
    			
        //普通写法
        let fun11 = function(num1,num2){
            return {
                num1:num1,
                num2:num2
            }
        }
        console.log(fun11(5,6));
    
        //箭头函数
        //let fun12 = (num1,num2) => {num1:num1,num2:num2};//报错
        let fun12 = (num1,num2) => ({num1:num1,num2:num2});//正确
        console.log(fun12(6,6));
    </script>
    
    • 结果:

      在这里插入图片描述

注意点4:对象赋值简写

  • 在ES6之前,我们赋值对象需要这么写:

    <script type="text/javascript">
    	console.log("---------------------ES6之前对象的赋值----------------------------");
    			
        //ES6以前赋值
        var fun13 = function(num1,num2){
            return {
                num1:num1,
                num2:num2
            }
        }
        console.log(fun13(6,7));
    
        //ES6以前箭头函数赋值
        var fun14 = (num1,num2) => ({num1:num1,num2:num2});
        console.log(fun14(7,7));
    </script>
    
    • 结果:

      在这里插入图片描述

  • ES6以后,我们赋值对象只需要这么写:

    <script type="text/javascript">
    	console.log("---------------------ES6之后对象的赋值----------------------------");
    			
        //ES6之后赋值
        let fun15 = function(num1,num2){
            return {
                num1,
                num2
            }
        }
        console.log(fun15(7,8));
    
        //ES^以后箭头函数赋值
        let fun16 = (num1,num2) => ({num1,num2});
        console.log(fun16(8,8));
    </script>
    
    • 结果:

      在这里插入图片描述

箭头函数注意点:

  1. 如果形参只有一个,则小括号可以省略。
  2. 函数体如果只有一条语句,则大括号{}可以省略。
  3. 函数体如果只有一条语句且为返回值,可以省略大括号{}和return。
  4. 箭头函数this始终指向函数声明时所在的作用域下的this的值,不是window。
  5. 箭头函数不能作为构造函数实例化。
  6. 不能使用 arguments 对象。

上面注意中,第四点尤其值得注意。普通函数的 this 对象的指向是可变的,但是在箭头函数中,它是固定 的。箭头函数 this 是静态的,this 始终指向函数声明时所在的作用域下的 this 的值

七、方法简写

  • ES6之前,定义方法:

    <script type="text/javascript">
        var fun17 = {
            say: function(){
                console.log("你好");
            }
        }
    	fun17.say();
    </script>
    
  • ES6之后,定义方法:

    <script type="text/javascript">
        let fun18 = {
            say(){
                console.log("你好");
            }
        }
    	fun18.say();
    </script>
    
  • 结果:

    在这里插入图片描述

八、module模块(重要)

1、概述

​ 历史上,JavaScript 一直没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的 方法拼装起来。其他语言都有这项功能,比如 Ruby 的 require、Python 的 import,甚至就连 CSS 都有@import, 但是 JavaScript 任何这方面的支持都没有,这对开发大型的、复杂的项目形成了巨大障碍。 ES6 引入模块 Module 体系,通过 export 命令显示的指定输出的代码,通过 import 命令输入指定代码。虽然 JavaScript 在 ES6 之前也可以使用 CommonJS 和 AMD 两种方式来实现模块加载。但是 ES6 模块的静态化思想,使 得编译时就可以确定模块的依赖关系,以及输入输出的变量,使用静态分析成为可能,进一步拓宽了 JavaScript 的语法。

​ 简单来说:模块化就是将一个大的程序文件,拆分为许多小的文件,就像乐高积木块一样,然后通过拼接使用, 将小文件组合起来。

模块化的好处:

  • 防止命名冲突
  • 代码复用
  • 高维护性

ES6 模块功能主要由两个命令构成:export 和 import

  • export 命令用于规定模块的对外接口。
  • import 命令用于输入其他模块提供的功能。

2、模块命令初体验

​ 在学习命令之前,我们需要明白一个概念。在 ES6 模块定义中,一个模块就是一个独立的文件。该文件内部的所 以变量、方法,外部都无法进行获取或操作。如果你希望在外部能够读取到模块内部的某个变量,我们就需要使 用 export 关键字暴露输出内容,import 关键字输入内容。

接下来使用 ES6 模块命令,体验输出输入模块的方便,具体操作如下:

  1. 新建项目,在项目的 js 文件中创建文件 m1.js,路径:./js/m1.js。在 js 文件中,暴露变量和函数,编写,代码如下:

    export let sname = "马云";
    export function say(){
    	console.log("大家好,我叫马云");
    }
    

注意:这里的暴露导出的语法是:export 变量|函数。

  1. 在根目录下新建 test.html,路径:/test.html,在 html 文件中,引入 m1.js 模块,编写代码如下:

    <script type="module">
        //导入模块 并起别名为m1
        import * as m1 from "./js/m1.js";
        //控制台打印导入的模块
        console.log(m1);
    </script>
    
    • 结果:

      在这里插入图片描述

    我们发现通过 export 和 import 两个命令,就可以引入其他文件模块化的内容,操作起来非常方便。

    注意:

    • 这里的输入的语法是;import * as 别名 from 路径。
    • script标签的 type 类型是module。
    • as起别名。
    • from后的模块文件位置,可以是相对路径,可以是绝对路径。

小结:

  • export 语法

    //export 变量声明;例:
    export let 变量名=;
    
    //export 函数声明;例:
    export function 函数名(){
    	//函数体
    }
    
  • import 语法

    <script type="module">
        //导入所有模块 as 是起别名操作
        import * as 别名 from "模块文件的位置";
    </script>
    

3、export 暴露命令

方式1:统一暴露

在上面的学习中,我们了解到可以对数据内容的单独暴露,除此之外还可以对数据进行统一暴露。

  • 语法如下:

    //统一暴露语法
    export {变量 1,变量 2,函数名 1,函数名 2};//这里仅是函数名,不加小括号
    
  • 新建 m2.js,代码示例如下:

    let sname = "马化腾";
    function say(){
    	console.log("大家好,我叫马化腾");
    }
    
    //单个暴露法
    /* export {sname};
    export {say}; */
    
    //统一暴露法
    //export {sname,say};
    
    //使用as关键字起别名
    export {sname as a, say as b};
    
  • 测试引入,代码如下:

    <script type="module">
        //导入模块 并起别名为m2
        import * as m2 from "./js/m2.js";
        //控制台打印导入的模块
        console.log(m2);
    </script>
    
    • 三种测试结果:

      在这里插入图片描述

**注意:**需要特别注意的是,export 命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系。下面是错误示例:

//报错,不能直接导出值
export 1;

//报错,应该加上{},正确为:export {num}
let num = 1;
export num;

//报错,和第二个错一样,没有加{}
function fn(){};
exoport fn;

方式2:默认暴露

  • 创建m3.js文件,示例如下:

    //写法1
    /* let sname = "雷军";
    function say(){
    	return "大家好,我叫雷军";
    }
    
    export default {sname,say}; */
    
    //写法2	
    export default {
    	sname:"雷总",
    	say(){
    		return "大家好,我是雷总";
    	}
    }
    
  • 测试导入如下:

    <script type="module">
        //导入模块 并起别名为m3
        import m3 from "./js/m3.js";
        //控制台打印导入的模块
        console.log(m3);
    </script>
    
    • 两种方法结果如下:

      在这里插入图片描述

注意:

  • 每个模块中,只允许使用一次 export defulat 默认暴露命令。
  • 默认 defulat 比普通暴露方式多了一层 default。

4、import引入命令

​ 使用 export 命令定义了模块的对外接口以后,其他 JS 文件就可以通过 import 命令加载这个模块。前面我们学习 了使用星号(*)引入整个模块暴露的内容,接下来我们学习按需引入。

方式1:按需引入

可以利用解构赋值的形式,通过 import 命令实现对模块的按需引入(导入)。

  • 语法如下:

    import {变量 1,变量 2,函数 1,函数 2} from "模块文件的位置";
    
  • 代码示例如下:

    //m1.js 内容
    export let sname = "马云";
    export function say(){
    	console.log("大家好,我是马云");
    }
    
    //新建页面 test_import.html 页面引入代码
    <script type="module">
        import {sname,say} from "./js/m1.js;
        console.log(sname); //输出结果: 马云
    </script>
    

通过上面的操作,我们可以按需引入所需的变量或者函数,只需要一个大括号,就可以获取指定模块中的内容, 写法非常类似前面学的解构赋值操作。

  • 接下来我们测试一下,同时引入 m1.js 和 m2.js。代码示例如下:

    //新建页面 test_import.html 页面引入 m1 和 m2 代码
    <script type="module">
        import {sname,say} from "./js/m1.js;
        import {sname,say} from "./js/m2.js;
        console.log(sname);
    </script>
    
    • 结果报错如下:

      在这里插入图片描述

  • 这里报错是因为导入的变量 sname 重复了,我们可以使用 as 关键字来重命名变量或函数,如下所示:

    //新建页面 test_import.html 页面引入 m1 和 m2 代码
    <script type="module">
        import {sname,say} from "./js/m1.js";
        import {sname as a ,say as b} from "./js/m2.js";
        console.log(sname);
        console.log(a);
    </script>
    
  • 结果:

    在这里插入图片描述

方式 2:针对默认暴露的引入方式

  • 语法如下:

    import 别名 from "模块文件的位置";
    import {default as 别名} from "模块文件的位置";
    
  • 代码示例如下:

    //新建页面 test_import.html 页面引入 m3.js
    <script type="module">
        import aa from './js/m3.js';
        import {default as bb} from './js/m3.js';
    
        console.log(aa.sname);
        console.log(bb.sname);
    </script>
    
    • 结果:

      在这里插入图片描述

本质上,export default 就是输出一个叫做 default 的变量或方法,然后系统允许你为它取任意名字。

方式3:默认暴露和普通暴露多种结合

  • 当存在默认和普通引入时,语法如下:

    import 默认别名,{普通引入} from "模块文件的位置";
    
    • 需要注意的时,默认引入一定要在普通引入的前面。
  • 新建 m4.js 文件,代码如下:

    let name1 = "帝国指挥官.刘国梁";
    let name2 = "不懂球的胖子.刘国梁";
    
    export default {name1};
    export {name2};
    
  • 新建 test_improt2.html,代码如下:

    <script type="module">
        import df,{name2} from "./js/m4.js";
        console.log("name1-->" + df.name1);
        console.log("name2-->" + name2);
    </script>
    
    • 结果:

      在这里插入图片描述

九、js中的this关键字(扩展)

this关键字大概分为三种情况:

  • 普通函数:谁调用,指向谁。

  • 箭头函数:指向调用者的父类。

  • 匿名函数又分为两类:

    • 普通匿名函数:永远指向window对象。
    • 箭头匿名函数:指向调用者的父类
  • 示例如下:

    <script type="text/javascript">
        console.log("------------------第一种情况:谁调用,this就指向谁-------------------");
        //测试1:打印结果:window对象
        console.log(this);
    
        //测试2
        let fun1 = function(){
            console.log(this);
        }
    
        /* 打印结果:whindow对象,
         * 因为执行方法的时候,默认window.fun(),
         * window可以省略,调用的时候是window对象调用的。
        */
        fun1();
    
        console.log("-----------第二种情况:箭头函数,this指向调用者的父级--------------");
    
        //测试1(用作对比),普通函数,因为是obj调用的方法,所以this指向obj
        let obj = {
            name: "张三",
            say: function(){
                console.log(this);
            }
        }
        //结果为:obj对象
        obj.say();
    
        //测试2:箭头函数
        let obj2 = {
            name: "张三2",
            say: () => console.log(this)
        }
        //结果为:window对象,因为this的调用者为obj2,obj2的父级为window
        obj2.say();
    
        console.log("-----------第三种情况:匿名函数(分两种情况)--------------");
    
        //普通匿名函数:	定时器里的函数为匿名函数,普通函数里的匿名函数永远指向window对象
        let obj3 = {
            name: "张三3",
            say: function(){
                setTimeout(function(){
                    console.log(this);
                },100);
            }
        }
        //结果为:window对象
        obj3.say();
    
        //箭头匿名函数: 定时器里的函数为匿名函数,箭头函数里的匿名函数指向其调用者的父类
        let obj4 = {
            name: "张三3",
            say: function() {
                setTimeout(() => {
                    console.log(this);
                },100);
            }
        }
        //结果为:obj4对象 因为定时器里为箭头匿名函数,指向调用者(say)的父类(obj4)
        obj4.say();
    
    </script>
    
    • 结果:

      在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值