美文网首页ES6
谈谈ES6语法(汇总上篇)

谈谈ES6语法(汇总上篇)

作者: Ming_Hu | 来源:发表于2019-07-12 16:37 被阅读0次

    ES6可以说是一个泛指,指5.1版本以后的JavaScript的下一代标准,涵盖了ES2015,ES2016,ES2017等;亦指下一代JavaScript语言

    背景

    嗯~ES6的语法有什么好谈的,无聊了吧?

    确实,语法糖的东西真的是学起来如嚼蜡 -- 淡无味;但是要用别人的东西来开发的,你学还是学呢?

    所以,还是简单谈下吧...

    本次的ES6语法的汇总总共分为上、中、下三篇,本篇文章为上篇。

    var、let和const

    var是之前就有的了,在这里提出来主要是为了比较其和let与const

    区别

    1. 块级作用域

    for(var i = 0; i < 3; i++) {
        setTimeout(() => {
            console.log(i); // 输出3个3
        }, 0)
    }
    

    解析:变量i是var声明的,在全局范围内是都有效,全局只有一个变量i。每次循环,变量的值会发生改变。循环内的i是指向全局的i。

    for(let i = 0; i < 3; i++) {
        setTimeout(() => {
            console.log(i); // 输出0, 1, 2
        }, 0)
    }
    

    解析:变量i是let声明的,当前的i只在本轮循环有效,所以每次循环的i其实都是一个新变量。JavaScript引擎内部会记住上一轮的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。

    2. 不存在变量提升

    console.log(a); // undefined
    var a = 100;
    

    var命令会发生变量提升现象,即变量可以在声明之前使用,值为undefined;而let纠正了这种行为,不能产生变量提升。

    console.log(a); // 报错
    let a = 100;
    

    3. 暂时性死区

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

    如:

    var temp = 123;
    if(true) {
        temp = 'abc'; // 引入错误
        let temp; 
    }
    

    在上面中,if后面的大括号内容就形成了一个区域。而temp此时是找不到外层的,因为内部有个temp且你在内部let temp声明前赋值了。

    在看一个隐晦的例子:

    function bar(x = y, y = 2) {
        return [x, y]
    }
    bar(); // 报错
    

    在上面的例子中bar里面进行赋值操作的时候,就产生了一个封闭的区域了,可以认为x 和 y通过let声明,可是上面的问题是,x = y的引用在y = 2的声明之前。

    可以修正如下:

    function bar(y = 2, x = y) {
        return [x, y];
    }
    bar(); // [2, 2]
    

    4. 不可重复声明

    var a = 100;
    var a = 1000;
    console.log(a); // 1000
    
    let a = 100;
    let a = 1000; // 报重复声明错误
    

    5. ES6声明的变量不会挂在顶层对象

    嗯~ES6变量的声明是指哪些声明呢?

    let, const, import, class声明。

    var, function声明是ES6之前的。

    所以目前JavaScript有六种声明变量的方式了~

    var job = 'teacher';
    console.log(window.job); // teacher
    
    let job = 'teacher';
    console.log(window.job); // undefined
    

    const命令注意点

    1. let可以先声明稍后赋值;而const声明之后必须立马赋值,否则会报错
    let a;
    a = 100; // this is ok
    
    const a; // 报没初始化数据的错误
    
    1. const声明了简单的数据类型就不能更改了;声明了引用类型(数组,对象等),指针指向的地址不能更改,但是内部的数据可以更改的
    const str = 'this is a string';
    str = 'this is another string'; // 报了个“给不变的变量分配值”的错误
    
    const obj = {
        name: 'jia'
    }
    obj.name = 'ming'; // this is ok
    obj = {}; // 报了个“给不变的变量分配值”的错误
    

    let和const的使用场景

    1. let使用场景:变量,用以代替var

    2. const使用场景:常量、声明匿名函数、箭头函数的时候。

    // 常量
    const PI = 3.14;
    
    // 匿名函数
    const fn1 = function() {
        // do something
    }
    
    // 箭头函数
    const fn2 = () => {
        // do something
    }
    

    变量的解构赋值

    解构可以理解就是一个作用:简化你变量赋值的操作。

    数组场景

    let [name, job] = ['jiaming', 'teacher'];
    console.log(name); // jiaming
    

    本质上,这种写法属于模式匹配,只要等号两边的模式相同(重点),左边的变量就会被赋予对应的值。再比如:

    let [ , , third] = ["foo", "bar", "baz"];
    console.log(third); // "baz"
    
    let [head, body, ...tail] = [1, 2, 3, 4, 5];
    console.log(tail); // [3, 4, 5]
    

    也可以使用默认值。但是默认值生效的前提是:ES6内部使用严格相等运算符(===),判断一个位置是否有值。所以,只有当一个数组成员严格等于undefined,默认值才会生效。

    let [x, y = 'b'] = ['a']; // x='a', y='b'
    
    let [z = 1] = [undefined];
    console.log(z); // 1
    
    let [k = 1] = [null];
    console.log(k); // null
    

    对象场景

    const state = {
        name: 'jiaming',
        job: 'teacher'
    };
    let {
        name,
        job
    } = state;
    // 上面的场景很熟悉吧
    console.log(job); // teacher
    

    上面的例子如果写具体的话,是这样的:

    const state = {
        name: 'jiaming',
        job: 'teacher'
    };
    let {
        name: name, // 第一个name是匹配模式,第二个name才是变量,两者同名简化成一个即可
        job: job
    } = state;
    

    我们来改写下:

    const state = {
        name: 'jiaming',
        job: 'teacher'
    };
    let {
        name: job,
        job: name
    } = state;
    console.log(job); // jiaming
    

    对象也可以使用默认值,但是前提是:对象的属性值严格等于undefined

    如下:

    var {x = 3} = {x: undefined};
    console.log(x); // 3
    
    var {y = 3} = {y: null};
    console.log(y); // null
    

    字符串场景

    字符串之所以能够被解构赋值,是因为此时字符串被转换成了一个类似数组的对象。

    const [a, b, ...arr] = 'hello';
    console.log(arr); // ["l", "l", "o"]
    
    let {length: len} = 'hello';
    console.log(len); // 5
    

    数值和布尔值场景

    解构赋值时,如果等号右边是数值和布尔值,则会先转换为对象(分别是基本包装类型Number和基本包装类型Boolean)。不过这种场景用得不多~

    let {toString: s} = 123;
    console.log(s); // function toString() { [native code] }
    console.log(s === Number.prototype.toString); // true
    
    let {toString: s} = true;
    console.log(s === Boolean.prototype.toString); // true
    

    解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。由于undefined和null无法转为对象,所以对它们进行解构赋值,都会报错

    两种使用场景

    1. 交换两变量值

    let [a, b] = ['reng', 'jia'];
    [a, b] = [b, a];
    console.log(b); // 'reng'
    

    2. 将字符串转换为数组

    let [...arr] = 'reng';
    console.log(arr); // ["r", "e", "n", "g"]
    console.log(arr.splice(0, 2)); // ["r", "e"] 返回删除的数组(能使用数组的方法了)
    

    字符串扩展

    针对字符串扩展这个,个人感觉模版字符串使用的频率比较高。模版字符串解放了拼接字符串带来的繁琐操作的体力劳动。

    let name = 'jiaming';
    let str = 'Hello! My name is '+ name + '. Nice to meet you!';
    let strTemp = `Hello! My name is ${ name }. Nice to meet you!`
    

    对于新增的字符串方法,可以记下下面这几个:

    • includes(): 返回布尔值,表示是否找到了参数字符串
    • startWith(): 返回布尔值,表示参数字符串是否在原字符串的头部
    • endWith(): 返回布尔值,表示参数字符串是否在原字符串的尾部
    • trimStart(): 返回字符串,表示消除参数字符串开头的空格
    • trimEnd(): 返回字符串,表示消除参数字符串结尾的空格

    数值扩展

    留意下在Number对象上提供的新方法:

    • Number.isFinite(): 返回布尔值,表示参数值是否有限的
    • Number.isNaN(): 返回布尔值,用来检查一个值是否为NaN
    Number.isNaN(NaN) // true
    Number.isNaN(15) // false
    
    • Number.isInteger(): 返回布尔值,用来判断一个数值是否为整数

    关于Math对象上的方法,遇到要用到时候,查API吧,不然记太多,脑瓜子会疼~

    函数扩展

    rest参数

    ES6引入rest参数(形式是...变量名),用于获取多余的参数,这样就不需要使用arguments对象了。rest参数搭配的变量是一个数组(arguments是一个类数组来的),该变量将多余的参数放入数组中。

    arguments对象是一个类数组,还得通过Array.prototype.slice.call(arguments)将其转换为真数组;而rest参数直接就可以使用数组的方法了。

    function add(...arr) {
        console.log(arr); // [2, 5, 3]
        let sum = 0;
        for(var val of arr) {
            sum += val;
        }
        return sum;
    }
    console.log(add(2, 5, 3)); // 10
    

    箭头函数

    ES6允许使用“箭头”(=>)定义函数。

    const f = v => v; // 注意是有返回值return的啊
    
    // 等同于
    const f = function (v) {
        return v;
    }
    

    如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回结果。

    const sum = (num1, num2) => num1 + num2;
    
    // 等价于,使用了大括号,那箭头函数里面就要使用return了
    const sum = (num1, num2) => { return num1 + num2 }
    
    // 等价于
    const sum = function(num1, num2) {
        return num1 + num2
    }
    

    使用箭头函数注意点:

    1. 函数体内的this对象,就是定义所在的对象,而不是使用时所在的对象。
    2. 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
    3. 不可以使用arguments对象,该对象在函数体内不存在的,如果要用,可以用rest参数代替。
    4. 不可以使用yield命令,因此箭头函数不能用作Generator函数。
    function foo() {
        setTimeout(() => {
            console.log('id:', this.id); // id: 42
        }, 100);
    }
    
    var id = 21;
    
    foo.call({ id: 42 });
    
    // 错误使用箭头函数的例子
    
    const cat = {
        lives: 9,
        jumps: () => { // 箭头函数的错误使用,因为对象不构成单独的作用域
            this.lives--; // this 指向window
        }
    }
    
    var button = document.getElementById('press'); // 一个节点对象
    button.addEventListener('click', () => { // 箭头函数的this指向window
        this.classList.toggle('on');
    });
    
    // 箭头函数改成`function`匿名函数指向就正确了。
    

    箭头函数适合处理简单的计算,如果有复杂的函数体或读写操纵不建议使用,这样可以提高代码的可读性。

    关于尾递归和其优化可以直接看阮先生的文档

    找下茬

    假设有这么一个需求,需要对二维数组的元素进行反转并被1减。我们来看下下面代码,哪个能实现此需求呢?

    // 代码一
    const A = [[0,1,1],[1,0,1],[0,0,0]];
    const flipAndInvertArr = function(A) {
        A.map(item=>{
            item.reverse().map(r=>1-r)
        })
    }
    
    // 代码二
    const A = [[0,1,1],[1,0,1],[0,0,0]];
    const flipAndInvertArr = A=> A.map(res =>res.reverse().map(r => 1 - r));
    

    运行之后,发现代码二是能实现需求的:

    let resultArr = flipAndInvertArr(A);
    console.log(resultArr); // [[0, 0, 1], [0, 1, 0], [1, 1, 1]]
    

    嗯~上面已经提到过,箭头函数体加上大括号后,是需要自己手动return的~

    我们来改写下代码一,以便符合需求:

    const A = [[0,1,1],[1,0,1],[0,0,0]];
    const flipAndInvertArr = function(A) {
        return (A.map(item=>{
            return item.reverse().map(r=>1-r)
        }))
    }
    let result = flipAndInvertArr(A);
    console.log(result); // [[0, 0, 1], [0, 1, 0], [1, 1, 1]]
    

    惊喜不,意外不~

    参考和后话

    本次的ES6语法的汇总总共分为上、中、下三篇,本篇文章为上篇。

    更多的内容,请戳我的博客进行了解,能留个star就更好了💨

    相关文章

      网友评论

        本文标题:谈谈ES6语法(汇总上篇)

        本文链接:https://www.haomeiwen.com/subject/fsdykctx.html