美文网首页
ES6基础(一)

ES6基础(一)

作者: 喵呜Yuri | 来源:发表于2019-10-05 09:40 被阅读0次

    ECMAScript(简称ECMA或ES)和JavaScript(简称JS)的关系是什么呢?

    ES是标准,JS是实现。

    变量声明

    ES5中var有什么缺陷呢?

    var可以重复声明变量,带来的问题是会覆盖掉别人已经声明的变量。
    无法限制var声明变量的修改
    变量作用域

    JS的作用域只有全局作用域global scope和函数作用域function scope两种,并没有块级作用域。所以,在JS中的代码块中使用var声明的变量的作用域都是隶属于全局作用域的。

    var price = 100;
    var count = 5;
    if(count > 3){
        var discount = price * 0.5;
        console.log(discount);//50
    }
    console.log(discount);//50
    函数内声明的局部变量的作用域只在函数内有效
    
    function fn(){
        var cnt = 5;
        console.log(cnt);
    }
    fn();//5
    console.log(cnt);//Uncaught ReferenceError: cnt is not defined
    

    ES6中有两种定义变量的方式,分别是const和let关键字。

    使用关键字let声明变量

    let声明的变量是可以被修改的
    let声明的变量不能被重复声明
    let discount = 10;//作用域在全局
    let discount = 100;//Uncaught SyntaxError: Identifier 'discount' has already been declared
    let声明的变量拥有块级作用域block scope
    let count = 5;
    let discount = 10;//作用域在全局
    if(count > 3){
    let discount = 100;//作用域在块级
    }
    console.log(discount);//10
    使用关键字const声明常量

    const声明的常量是不可以被修改的
    const pi = 3.14;
    pi = 3.1415;//Uncaught TypeError: Assignment to constant variable.
    如果使用const声明的是对象(引用),那么是可以改变对象的成员属性的,不变的是引用指针所指向的地址。

    const obj = {id:1, name:"alice"};
    obj.id = 2;
    console.log(obj);//{id: 2, name: "alice"}
    

    如果不允许改变对象的属性值,可使用Object.freeze(object)方法来冻结。

    const obj = {id:1, name:"alpha"};
    
    const o = Object.freeze(obj);
    console.log(o);//{id: 1, name: "alpha"}
    
    obj.id = 2;
    console.log(obj);//{id: 1, name: "alpha"}
    
    o.id = 3;//Attempt to assign to const or readonly variable
    console.log(o);//{id: 1, name: "alpha"}
    

    const声明的常量不能重复声明
    const声明的常量拥有块级作用域
    变量与常量

    ES6中使用let声明变量的值是允许修改的,使用const声明的常量的值是禁止修改的。

    let i = 1;
    i = 10;
    console.log(i);//10
    
    const PI = 3.14;
    PI = 3.14159;//Uncaught TypeError: Assignment to constant variable.
    变量重复声明
    

    ES5中使用var声明的变量是可以被重复赋值的

    var i = 1;
    console.log(i);//1
    var i = 10;
    console.log(i);//10
    

    ES6中使用let声明的变量禁止重复声明

    let i = 1;
    let i = 10;//Uncaught SyntaxError: Identifier 'i' has already been declared
    

    ES6中使用const声明的常量禁止重复声明

    const PI = 3.14;
    const PI = 3.14;//es6.js:2 Uncaught SyntaxError: Identifier 'PI' has already been declared
    

    块级作用域

    ES5中没有块级作用域,只有函数作用域。

    使用ES5中的var声明变量,由于缺乏块级作用域,带来了问题。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>js</title>
    </head>
    <body>
        <button>按钮1</button>
        <button>按钮2</button>
        <button>按钮3</button>
        <script>
            window.onload = function(){
                var btns = document.getElementsByTagName("button");
                for(var i=0; i<btns.length; i++){
                    btns[i].onclick = function(){
                        alert(i);
                    }
                }
            }
        </script>
    </body>
    </html>
    

    问题:每次都会输出3

    使用函数闭包的方式解决块级作用域问题

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>js</title>
    </head>
    <body>
        <button>按钮1</button>
        <button>按钮2</button>
        <button>按钮3</button>
        <script>
            window.onload = function(){
                var btns = document.getElementsByTagName("button");
                for(var i=0; i<btns.length; i++){
                    (function(i){
                        btns[i].onclick = function(){
                            alert(i);
                        }
                    })(i);
                }
            }
        </script>
    </body>
    </html>
    

    使用ES6中的let声明的变量具有块级作用域

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>es6</title>
    </head>
    <body>
        <button>按钮1</button>
        <button>按钮2</button>
        <button>按钮3</button>
        <script>
            window.onload = function(){
                let btns = document.getElementsByTagName("button");
                for(let i=0; i<btns.length; i++){
                    btns[i].onclick = function(){
                        alert(i);
                    }
                }
            }
        </script>
    </body>
    </html>
    

    IIFE 立即执行函数表达式

    使用let和const替代立即执行函数表达式(IIFE, Immediately-Invoked Function Expression),立即执行函数表达式用于生成一个私有变量。

    由于ES5中使用var声明的变量,很容易就会污染全局作用域。例如JS的window对象具有一个name的属性,如果直接使用var定义一个全局变量name则会直接覆盖window对象的name属性。

    console.log(name, window.name);
    var name = "junchow";
    console.log(name, window.name);//junchow junchow
    

    使用立即执行函数表达式可以有效地避免污染全局变量,使变量私有化。

    console.log(name, window.name);//"" ""
    (function(){
        var name = "junchow";
        console.log(name, window.name);//junchow ""
    })();
    console.log(name, window.name);//"" ""
    使用立即执行函数表达式,虽然使变量私有化了,但并不利用代码的可读性。
    
    const name = "junchow";
    console.log(name, window.name);//junchow ""
    let name = "junchow";
    console.log(name, window.name);//junchow ""
    {
        const name = "junchow";
        console.log(name, window.name);//junchow ""
    }
    

    怪异的for循环

    例如:在for循环中经常会遇到的问题

    for(var i=0; i<3; i++){
        console.log(i);// 0 1 2
    }
    console.log(i);//3
    for(var i=0; i<3; i++){
        console.log(i);//0 1 2
        setTimeout(function(){
            console.log(i);// 3 3 3
        },1000);
    }
    console.log(i);//3
    

    使用ES6的let声明循环变量

    for(let i=0; i<3; i++){
        console.log(i);//0 1 2
        setTimeout(function(){
            console.log(i);// 0 1 2
        },1000);
    }
    console.log(i);//Uncaught ReferenceError: i is not defined
    

    临时性死区

    ES6中,在代码块内使用let或const声明变量前,变量都是不可用的。在语法上,称之为临时性死区(TDZ, Temporal Dead Zone)。

    使用var声明的变量时存在着变量的提升

    console.log(color);//undefined
    var color = "red";
    

    为什么是undefined呢?实际上上述代码相当于:

    var color;
    console.log(color);//undefined
    color = "red";
    

    JS中函数以及变量的声明都将被提升到函数的最顶部,JS中变量可以在使用后声明,也就是变量可以先使用再声明。

    console.log(color);//Uncaught ReferenceError: Cannot access 'color' before initialization
    let color = "red";
    

    变量赋值

    解构赋值

    表达式左右两边结构必须一模一样
    表达式右侧必须是值
    声明和赋值不能分开
    数组解构赋值

    //ES5解构赋值

    var arr = [1,2,3];
    var x = arr[0];
    var y = arr[1];
    var z = arr[2];
    console.log(x, y, z);//1 2 3
    

    //ES6数组解构赋值

    let [x, y, z] = [1, 3, 5];
    console.log(x, y, z);//1 3 5
    

    JSON解构赋值

    let {id, name} = {id:1, name:"alpha"};
    console.log(id, name);//1 "alpha"
    

    字符串

    ES6新增方法startsWith和endsWith
    ES6新增字符串模板
    String.startsWith

    startsWith表示参数字符串是否在原字符串的头部,返回布尔值。

    string.startsWith(substring[, searchPos]):boolean
    例如:验证URL地址的合法性

    let url = "http://www.baidu.com";
    let bool = url.startsWith("http://");
    console.log(bool);//true
    

    startsWith第二个参数为起始位置
    例如:验证身份证号码

    const identify = "51030019800730366x";
    //判断是否为四川省
    console.log(identify.startsWith("51"));//true
    //判断是否为1980年出生的,startsWith第二个参数为从第几个位置开始
    console.log(identify.startsWith("1980", 6));//true
    startsWith对大小写敏感
    const str = "jack love mary";
    console.log(str.startsWith("jack"));//true
    console.log(str.startsWith("Jack"));//false
    String.endsWith
    
    endsWith表示参数字符串是否在原字符串的尾部,返回布尔值。
    
    let url = "yourname@hotmail.com";
    let bool = url.endsWith("@hotmail.com");
    console.log(bool);//true
    String.repeat 重复
    
    repeat(count)方法会返回一个新字符串表示将原字符串重复count次后的结果
    repeat(count)方法的参数count如果是小数则会被向下取整
    repeat(count)方法的参数count如果是负数或Infinity则会报错
    repeat(count)方法的参数count如果是NaN则等于0
    repeat(count)方法的参数count如果是字符串则会先转换为数字
    console.log(`${'='.repeat(10)}`);//==========
    console.log(`${'='.repeat(10.9)}`);//==========
    console.log(`${'='.repeat(0)}`);//
    console.log(`${'='.repeat(NaN)}`);//
    console.log(`${'='.repeat("10")}`);//==========
    console.log(`${'='.repeat(-10)}`);//Uncaught RangeError: Invalid count value
    console.log(`${'='.repeat(Infinity)}`);//Uncaught RangeError: Invalid count value
    

    例如:实现字符串右对齐

    //右对齐

    const pad = (string, length=0)=> `${' '.repeat(Math.max(length - string.length, 0))}${string}`;
    
    console.log(pad("hello", 20));//               hello
    console.log(pad("world", 20));//               world
    

    模板字符串

    传统字符串拼接无法正常换行
    传统字符串拼接无法友好的插入变量
    传统字符串拼接无法友好的处理单引号和双引号相互嵌套的问题
    ES6模板字符串使用一对反引号来定义一个模板字符串

    let obj = {id:1, title:"test"};
    console.log(`id = ${id} title = ${title}`);
    

    模板字符串的应用

    const User = {
        id:1,
        name:"junchow",
        date:"2019-08-28",
        todos:[
            {todo:"go to store", status:false},
            {todo:"watch movie", status:true},
            {todo:"running", status:true}
        ]
    };
    

    //模板字符串可以进行嵌套

    const template = `<ul>${User.todos.map(item=>`<li>${item.todo}</li>`).join("")}</ul>`;
    console.log(template);
    
    document.querySelector("#app").innerHTML = template;
    const User = {
        id:1,
        name:"junchow",
        date:"2019-08-28",
        todos:[
            {todo:"go to store", status:false},
            {todo:"watch movie", status:true},
            {todo:"running", status:true}
        ]
    };
    

    //模板字符串可以进行嵌套

    const template = `
    <ul>
    ${User.todos.map(item=>`
        <li>
            ${item.todo}
            ${item.status===true?"done":"undo"}
        </li>
    `).join("")}
    </ul>`;
    console.log(template);
    
    document.querySelector("#app").innerHTML = template;
    

    标签模板字符串

    function tag(template, ...args){
        const html = args.map(arg=>`<strong>${arg}</strong>`);
        let ret = "";
        template.forEach((item, index) => ret +=`${item} ${html[index]}`);
        return ret;
    }
    
    const id = 1;
    const name = "junchow";
    const sentence = tag`${id} ${name}`;
    console.log(sentence);
    

    数组

    数组解构Array Destructuring

    ES6允许按照一定模式,从数组中提取值,同时定义变量并对其赋值,这也就是解构(Destructureing)。

    const result = [1, "success"];
    
    //const [code, message, data] = result;
    //console.log(code, message, data);//1 "success" undefined
    
    //const [code, ,data] = result;
    //console.log(code, data);//1 undefined
    
    //const [code, ...args] = result;
    //console.log(code, args);//1 ["success"]
    
    //const [code=0, message="", data={}] = result;
    //console.log(code, message, data);//1 "success" {}
    

    例如:交换变量值

    let x = 10, y = 20;
    [x, y] = [y, x];
    console.log(x, y);//20 10
    Array.protype.includes() 包含
    

    ES5数组提供Array.indexOf()方法用来查找某个元素在数组中的索引位置,若不存在则返回-1。

    indexOf方法判断数组中是否包含元素是存在两点不足之处

    返回-1和元素的索引位置表示是否包含不够语义化
    不能判断是否含有NaN元素

    const arr = ["alice", "card", 1, 2, NaN];
    console.log("%s", arr.indexOf("alice"));//0
    console.log("%s", arr.indexOf(0));//-1
    console.log("%s", arr.indexOf(NaN));//-1
    

    ES6提供Array.includes()方法返回布尔值可用于判断数组中是否包含指定元素,对NaN同样有效,其不足之处就是无法进行定位。

    includes方法第1个参数表示查询的目标字符串
    includes方法第2个参数表示判断的起始索引位置
    includes方法第2个参数可以是负数,表示从右倒数第几个,但不会改变搜索方向,搜索方向仍然会从左至右。

    const arr = ["alice", "card", 1, 2, NaN, "alice"];
    console.log("%s", arr.indexOf("alice"));//0
    console.log("%s", arr.indexOf(0));//-1
    console.log("%s", arr.indexOf(NaN));//-1
    
    console.log("%s", arr.includes("junchow"));//false
    console.log("%s", arr.includes("alice"));//true
    console.log("%s", arr.includes("alice", 4));//true
    console.log("%s", arr.includes("alice", -5));//true
    

    ES6中新增了四个函数,分别是映射map、汇总reduce、过滤filter、循环forEach

    Array.map 映射

    // 将数组中所有元素增加2倍

    let arr = [11, 23, 43, 90];
    let result = arr.map((x)=>x*2);
    console.log(result);//[22, 46, 86, 180]
    let arr = [89, 23, 31, 78];
    let result = arr.map((x)=>{
        if(x>=90){
            return "A"
        }else if(x>=80){
            return "B";
        }else if(x>=60){
            return "C";
        }else{
            return "D";
        }
    });
    console.log(result);//["B", "D", "D", "C"]
    Array.prototype.reduce() 汇总
    

    reduce方法接受一个函数作为累加器并指定初始值(可选),数组中的每个值(从左至右)开始缩减,最终计算为一个值。

    array.reduce(callback, [initialValue])
    reduce方法的累加器函数callback接受四个参数

    function callback(preValue, curValue, index, array)
    preValue初始值或上一次回调函数的返回值
    curValue当前元素值
    index当前索引
    array调用reduce方法的数组
    例如:数组求和与平均数

    let arr = [100, 200, 300];
    
    //求和 tmp为中间结果 item为每次迭代的对象 index为索引
    let total = arr.reduce((tmp, item)=>{
        //100 200 1
        //300 300 2
        //console.log(tmp, item, index);
        return tmp + item;
    });
    //console.log(total);//600
    

    例如:求平均值

    let arr = [100, 200, 300];
    //求平均数
    let average = arr.reduce((tmp, item, index, arr)=>{
        if(arr.length === index+1){
            return (tmp+item)/index;
        }else{
            return tmp + item;
        }
    
    });
    console.log(average);//300
    

    例如:求阶乘

    let arr = [100, 200, 300];
    //求阶乘
    let result = arr.reduce((preval, curval)=>{
        return preval * curval;
    });
    console.log(result);//6000000
    

    例如:二维数组转化为一维数组

    let arr = [
        [1, 3, 5],
        [2, 4, 6],
        [10, 20, 30]
    ];
    let result = arr.reduce((retval, current, index, arr)=>{
        return retval.concat(current);
    });
    console.log(result);//[1, 3, 5, 2, 4, 6, 10, 20, 30]
    

    例如:计算数组中元素出现的次数

    let arr = ["alice", "bob", "carl", "bob", "carl", "bob"];
    
    let result = arr.reduce((retval, current, index, arr)=>{
       if(current in retval){
           retval[current]++;
       } else{
           retval[current] = 1;
       }
       return retval;
    },{});
    
    console.log(result);//{alice: 1, bob: 3, carl: 2}
    

    例如:计算数组中某个元素出现的次数

    let arr = ["alice", "bob", "carl", "bob", "carl", "bob"];
    
    let times = (arr, val)=>arr.reduce((retval, current)=>{
        return current===val ? retval+1 : retval+0;
    },0);
    
    console.log(times(arr, "bob"));//3
    

    例如:获取数组对象中某个指定字段的值

    let arr = [
        {id:11, name:"alpha", status:1},
        {id:20, name:"alice", status:0},
        {id:34, name:"bob", status:1}
    ];
    //获取数组中所有的id
    let getField = (arr, field) => arr.reduce((preval, curval)=>{
        preval.push(curval[field]);
        return preval;
    }, []);
    console.log(getField(arr, "id"));//[11, 20, 34]
    

    例如:数组去重

    let arr = ["alice", "bob", "carl", "bob", "carl", "bob"];
    
    let dedupe = (arr)=>arr.sort().reduce((retval, current)=>{
        if(retval.length===0 || retval[retval.length-1]!==current){
            retval.push(current);
        }
        return retval;
    },[]);
    
    console.log(dedupe(arr));// ["alice", "bob", "carl"]
    

    例如:求某用户的单科成绩根据权重比例汇总后的权重值

    //单科成绩
    let arr = [
        {subject:"math", score:89},
        {subject:"chinese", score:89},
        {subject:"english", score:89},
    ];
    //权重比例
    let percent = {math:0.5, chinese:0.3, english:0.2};
    //求权重值
    let result = arr.reduce((preval, curval)=>{
       return preval + curval.score *percent[curval.subject];
    }, 0);
    console.log(result);//89
    

    相关文章

      网友评论

          本文标题:ES6基础(一)

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