zoukankan      html  css  js  c++  java
  • ECMA6


    let关键字

    用来替代var 的关键字,不能重复定义一个变量

    举例:
    for(var i=0; i<5; i++){
         setTimeout(function(){
              alert(i); 
         },1000);
    }
    你认为弹出的结果是多少?
    结果: 5,5,5,5,5

    是不是跟你认为的0,1,2,3,4不太一样???
    原因就在于,var i=0,定义的不是局部变量,而是全局变量,这里不会形成闭包。

    再举例:
    for(var i=0; i<5; i++){
         for(var i=0; i<5; i++){
              console.log(i);
         }  
    }
    你认为打印的结果是多少?
    结果: 1 2 3 4 5

    难到不是打印25次么?
    原因还是因为,像for循环的大括号,是不会形成作用域的

    那么如何在一个语句块中定义局部变量呢?
    那就是使用let,不过目前大多数的ES6只允许在严格模式下使用

    "use strict"
    for(let i=0; i<3; i++){
         setTimeout(function(){ alert(i) },1000);
    }

    我们尝试一下if语句的大括号
    "use strict";
    let a = 10;
    if(window) {
         let a = 100;
         console.log(a);
    }
    console.log(a);
    结果: 100  10

    从标准的ES6开始,我们有了块级作用域

    例如:
    function m(){  console.log("111111111") }
    if( flag > 5) {
         function m(){
               console.log("2222222222")
         }
    }
    m();

    在ES5中,函数m会在第二次定义时被覆盖。 结果: 222222222222
    在ES6中,则会出现 111111111111的结果。

    const

    我们终于可以定义常量了!
    const GD_TYPE_NORMAL = 1;

    =>箭头函数 (应用场景:回调函数)

    这个我个人不推荐使用,待会再说原因,先讲优点!

    先看区别吧,原来的写法
    var test = function(x){
         return x+2;
    }
    使用箭头函数:
    var test = x=>x+2; //可以理解为x经过一系列的变化返回x+2,即:return x+2

    若不想有返回值 var test = x =>{ x+2 }

    数组排序 [1,43,54,23].sort((a,b)=>a-b);

    看起很简单吧? 省略了function、return关键字和大括号。  使用方法跟以前一样没区别
    test(5);  结果: 7
    var test = (x+y)=>x+y;

    还有好处就是,自动绑定this,或者说this指向不发生改变
    var obj = {
         left : 200,
         move : function(){
              setTimeout(function(){
                   //this.left = 100;  
                   //以前这里不能写this
              },1000);
         }
    }
    使用了箭头函数:
    var obj = {
         left : 200,
         move : function(){
              setTimeout( ()=>{
                  this.left = 100;  
              },1000);
         }
    }

    当然也有一些缺陷
    第一:
    箭头函数是不能new的,它的设计初衷就跟构造函数不太一样
    第二:
    箭头函数如果要返回一个JSON对象,必须用小括号包起来
    var test = ()=>({id:3, val=20})

    箭头函数现在非常流行,但我个人并不觉得它有那么美好。
    主要是因为,这样的设计对代码的可读性伤害太大了

    引用国际著名OO专家、敏捷开发创始人 马丁.福勒的一句名言:
    任何一个傻瓜都能写出计算器可以理解的代码。惟有写出人类容易理解的代码,才是优秀的程序员。

    ……我们读代码的时间和写代码的时间比率是10:1。这意味着我们大部分时间都在阅读老代码,
    以便于之后新代码的编写。因为读代码占得比重太大了,因此我们希望在阅读代码的时候能够更
    加轻松,即便在编写代码的时候需要费点劲。
    这一段好像也是他老人家说的,我觉得很有道理。

    省略掉一个function单词,并不能给开发效率提高多少,但牺牲的却是最基本的代码可读性
    除了数学运算,我们几乎从来不用符号表示一些复杂的含义。
    甚至以前有人质疑过JQ关于each方法的设计思想,认为它屏蔽了程序最基本的逻辑,那就是循环
    代码的可读性收到了严重伤害。不过我们仔细看看forEach、map、filter这些函数,
    尽管它们屏蔽了for循环,但分析这些单词的含义:
    each 每个
    map 映射(一一对应)
    filter 过滤
    它们其实全部都隐含 的表达了遍历的意思。

    回过头来我们再看,从function到=>,这东西连个象形符号都算不上
    这就是为什么数学有那么多的符号可以用来交流,但我们却从不把它称为语言

    再举个例子,你看下面这段代码:
    (?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[x01-x08x0bx0cx0e-x1fx21x23-x5bx5d-x7f]|\[x01-x09x0bx0cx0e-x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[x01-x08x0bx0cx0e-x1fx21-x5ax53-x7f]|\[x01-x09x0bx0cx0e-x7f])+)])

    给你3分钟,能看懂吗?
    它为什么这么复杂?实际上就是因为它全部都使用了非象形符号表示的
    因此正则表达式才会成为最难交流以及阅读的编程语言

    但是!我们必须承认,它确实给我们的验证工作提升了百倍以上的效率
    权衡利弊,我有理由喜欢它

    但是这个箭头函数,在多数情况下,我不推荐使用
    就像你的代码里不能满屏幕充斥着  
    三目运算符   和  Math.max(xx, Math.min(xx, obj) )这样的东西
    这样真的好吗?

    在写回调的时候,偶尔用用箭头函数,还是不错的
    [2,3,9,14,8,29,93,30].sort( (a,b) => a-b );

    个人观点,仅供参考。


    Destructuring 解构

    以前我们定义多个变量,可以这样写:
    var x=10,y=20,z=30;

    现在我们可以这样写:
    let [x,y,z] = [10,20,30];
    let [x,[a,b],y] = [10,[15,18],20];
    let {id,name,age} = {id:1,name:"jide",age:99};


    这并不是在定义数组,而是通过等号两边的结构匹配,进行赋值。

    当然,如果你写的不够规范:

    let [x,y] = [10,20,30]; //结果x=10,y=20
    let [x,[a,b],y] = [10,[5],20];  //结果x=10,a=5,y=20,b=undefined
    浏览器并不会报错,依然赋值成功。

    甚至可以使用对象来赋值
    var {id,name,age} = {id:43,name:'yt',age:30}

    解构赋值的用途:
    1 交换变量的值   
    [a,b] = [b,a];  //排序可能会常用到

    2 函数返回多个值,这个功能比较有颠覆性
    var [r1,r2,r3] = exec();
    function exec(){
         return ["结果a","结果b","结果c" ];
    }

    3 函数的参数定义方式, 不用再考虑顺序
    function exec({time, speed, pos}){
    }
    执行函数:
    exec({
         pos: {x:20,y:55},
         speed: 50,  
         time: 2000
    })

    默认值也可以顺便带上:
    function exec({ time=2000, speed=50, pos}){
    }

    3 获取数组的指定元素(第一个和最后一个)
    var {0:head,arr.length-1:last} = arr; 


    字符串的一些方法


    字符串模板, 使用反引号`表示,使用${变量|函数}嵌入代码

    var username = "flq";
    var introduce = function(){ +
    return ''haha";
    }
    $(temp).html(
         `你好,我的名字叫${username}
          接下来是我的自我介绍:${introduce()}`//introduce必须有返回值,否则为undefined
    );

    使用`反引号包裹的字符串,可以当场模板来使用。
    利用${ username } 输出变量
    利用${ introduce() } 执行函数

    经过测试,字符串模板默认会被转义,因此写入标签的话,会被原样输出



    写一个自己的模板引擎

    var template = `<ul>
                    <% for(var i=0; i < data.names.length; i++) { %>
                       <li><%= data.names[i] %></li>
                    <% } %>
                   </ul>       
                   `;

    test(template, {names:["张三","李四","tony stark","hulk"]})

    function test(temp, obj){
        //正则处理字符串
        temp = temp.replace(/<%=(.+?)%>/g,"`) print($1) print(`");
        temp = temp.replace(/<%(.+?)%>/g,"`) $1 print(`");
        temp = "print(`"+temp+"`)";

        //经过正则的处理,我们希望模板字符串变成如下形式:
        /******************************************* 
        print(`<ul>`)
        for(var i=0; i<data.names.length; i++){
            print(`<li>`) 
            print(data.names[i]) 
            print(`</li>`)
        }
        print(`</ul>`)
        *********************************************/

        //准备一段代码,动态生成一个函数,把刚才准备好的temp代码嵌入当中
        var funcStr = `(function(data){
            var strhtml = "";
            function print(str){
                strhtml += str;
            }
            ${temp} //把temp字符串放入该函数中,形成一个完整的函数
            return strhtml;
        })`;

        /*************这段代码生成后的样子******************
        (function(data){
            var strhtml = "";
            function print(str){
                 strhtml += str;  
            } 
            print(`<ul>`)
       for(var i=0; i<data.names.length; i++){
            print(`<li>`)
            print(data.names[i])
            print(`</li>`)
       }
            print(`</ul>`)
            return strhtml; 
        })
        *********************************************/

        //使用eval执行代码,生成并返回这个函数
        var func = eval(funcStr);

        //调用这个函数,得到最终的字符串模板
        var res = func(obj);

        return res;
    }


    Math的一些方法

    请自行查阅文档

    数组的一些方法

    将伪数组转为数组:
    var list = Array.from(document.getElementsByTagName("li"));   

    复制指定内容覆盖指定内容(指定数组的下标6、7替换下标2及以后
    var arr = [1,2,3,4,5,6,7,8,9,0];
    arr.copyWithin(2,6,8);
    //[1,2,7,8,5,6,7,8,9,0] 

    //find跟过filter差不多,不过在找到符合条件的元素后,返回元素并停止遍历
    [1, 5, 10, 15].find(function(value, index, arr) {
         return value > 9;
    })
    // 10

    //跟find差不多,不过在找到符合条件的元素后,返回该元素的下标
    [1, 5, 10, 15].findIndex(function(value, index, arr) {
         return value > 9;
    })
    // 2


    Object.assign

    合并对象

    Object.assign({a:1},{b:2},{b:4,c:3});
    //{a:1,b:4,c:3}


    当然合并过程中做的是浅拷贝
    var a1 = {
        name: "aaaa",
        firend: {
            name: "cccc"
        }
    }
    var a2 = {
        age : 30
    }
    var b = Object.assign(a1,a2)
    a1.friend == b.friend

    对于原型属性忽略
    var a = {
        name: "aaaa"
    }
    var b = {
        age : 20
    }
    b.__proto__.title = "ma";
    var c = Object.assign(a,b);


    对于不可枚举属性忽略
    var a = {
        name: "aaaa"
    }
    var b = {
        age : 20
    }
    Object.defineProperty(b, "age", { //ECMA5
        enmuable : false
    })
    对于引用类型,只拷贝引用

    第七种数据类型Symbol

    var s1 = Symbol();
    var s2 = Symbol();
    var s3 = Symbol("abc");
    var s4 = Symbol("abc")

    s1不等于s2    s3不等于s4

    Symbol函数会生成一个唯一的值
    可以理解为Symbol类型跟字符串是接近的
    但每次生成唯一的值,也就是每次都不相等,至于它等于多少,并不重要
    这对于一些字典变量,比较有用

    const TYPE = {
         SMALL: Symbol(),
         MIDDLE: Symbol() ,
         LARGE: Symbol(),
         COM_SMALL: Symbol(),
         COM_MIDDLE: Symbol()
    }
    //以前我们可能会把SMALL、MIDDLE、LARGE赋值为数字或字符串
    //还要确保它们的值不能发生重复,但现在不用担心了

    function Create(type){
         switch(type){
              case TYPE.SMALL : {
                  .....
                  break; 
              }
              case TYPE.MIDDLE : {
                  .....
                  break;
              }
              case TYPE.LARGE : {
                  .....
                  break;
              }
         }
    }

    var s = Create(TYPE.MIDDLE);


    Set和Map集合

    想当初设计JS的时候,由于有SUN公司人员的参与
    再加上当时如日中天的JAVA及其优秀的设计,才使得JS语法及内存设计跟JAVA会如此的接近。

    但JAVA很多优秀的内容,JS不知道为了什么目的并没有引入,例如Set和Map集合

    Set集合,本质上就是对数组的一种包装(与map相同,但其key值与value值相等)
    例如:

    let imgs = new Set();
    imgs.add(1);
    imgs.add(1);
    imgs.add(5);
    imgs.add("5");
    imgs.add(new String("abc"));
    imgs.add(new String("abc"));

    打印的结果:
    1  5  '5'  'abc'  'abc'

    Set集合是默认去重复的,但前提是两个添加的元素严格相等
    所以5和"5"不相等,两个new出来的字符串不相等

    如何删除元素 set.delete(key)
    imgs.delete(5);

    imgs.delete('5');

    关于遍历的方法
    由于Set集合本质上还是一个map,因此会有以下几种奇怪的遍历方法
    var imgs = new Set(['a','b','c']);
    //根据KEY遍历
    for(let item of imgs.keys()){
         console.log(item);
    }
    //a
    //b
    //c

    //根据VALUE遍历
    for(let item of imgs.values()){
         console.log(item);
    }
    //a
    //b
    //c

    //根据KEY-VALUE遍历
    for(let item of imgs.entries()){
         console.log(item);
    }
    //['a','a']
    //['b','b']
    //['c','c']

    //普通for...of循环(for...of跟for-in的区别很明显,就是直接取值,而不再取下标了)
    for(let item of imgs){
         console.log(item);
    }
    //a
    //b
    //c

    SET集合没有提供下标方式的访问,因此只能使用for来遍历。

    // 下面展示了一种极为精巧利用set集合对数组去重的方法
    var newarr = [...new Set(array)];
    =============================================================================

    Map集合,即映射(底层也是数组,但其根据key值进行哈希运算后进行存储,查询效率远大于数组)
    let map = new Map();
    map.set("S230", "张三");
    map.set("S231", "李四");
    map.set("S232", "王五");

    获取某一个元素
    map.get("s232");
    //王五

    //循环遍历,配合解构赋值
    for(let [key,value] of map){
         console.log(key,value);
    }

    map.has(key);  //返回布尔值,判断是否含有键key
    map.delete(key);  //删除
    map.size    //长度
    map.clear()   //清空

    Promise规范

    新推出的Promise函数,是一个构造函数,它实际上是对回调函数的一种封装
    对异步编程的一种改进,当然,如果只是写法上的改进,那就意义不大了。
    下面我们看看它能解决哪些以前难以解决的问题:

    问题1: 当有两个或以上的ajax请求需要按顺序执行,该如何编写?
    //以下是伪代码
    ajax1({
         url: "xxxxx",
         success: function(data1){
              //在这里,当ajax1完成后,才能开启ajax2 
              ajax2({
                   url: "xxxxx",
                   success: function(data2){
                       //第二次完成的回调,可能还有第三个第四个无止境的嵌套....
                   }
              }); 
         },
         error: function(msg){
             console.log("请求出现错误:",msg); 
         }
    });

    而使用promise的写法如下:
    new Promise(function(resolve, reject){
         ajax1({
              url: "xxxxx",
              success: function(data){
                   resolve();
              },
              error: function(msg){
                   reject();
              }
         });
    }).then(function(){
         ajax2({
              url: "xxxx",
              success: function(data){
                    
              } 
         });
    }).catch(function(){
         console.log("请求出现错误:",msg);
    });

    也就是代码被改进成了new Promise(ajax1).then(ajax2).catch()
    使用了链式调用代替了嵌套结构。

    resolve() 和 reject() 则意味着成功回调和失败回调的执行。

    如果有三个或更多的ajax请求呢?
    new Promise(function(resolve, reject){
         ajax1({
              url: "xxxxx",
              success: function(data){
                   resolve();
                   //use data.......
              },
              error: function(msg){
                   reject();
              }
         });
    }).then(function(){
    return new Promise(function(resolve, reject){
         ajax2({
              url: "xxxx",
              success: function(data){
                   resolve();
                   //use data.......
              },
              error: function(msg){
                   reject();
              }
         })
    })
    }).then(function(){
         ajax3({
              url: "xxxx",
              success: function(data){
                     //use data.......
              },
              error: function(msg){
                    //print msg
              }
         })
    });
    为了简化代码,我这里将catch方法省略了
    代码结构大概是这样的: 
    new Promise(ajax1).then(function(){ return new Promise(ajax2) }).then(ajax3);

    如果有第四个请求要顺序执行,则大概写成这样:
    new Promise(ajax1)
    .then(function(){
         return new Promise(ajax2)
    })
    .then(function(){
         return new Promise(ajax3)
    )
    .then(ajax4);

    从这里仔细观察,可以看出,只有被Promise封装过了,回调才能保证顺序。
    也就是Promise(意为承诺)设计的初衷

    但前一个方法必须在它最后,执行resolve(),后一个方法才可以开始。
    如果执行了reject(),则进入catch()方法。 

    其实这里不能单纯的理解为 resolve就是success,reject就是error
    现就职于阿里的大名鼎鼎的阮一峰老师,喜欢管它叫状态机,这是非常恰当的叫法。

    只有理解了它的设计思路,才能明白为什么叫状态机。

    //我们把代码写的再实际一些
    new Promise(function(resolve, reject){     //p1
              ajax1({
                       .........
                       resovle() 
              });
    }).then(function(){
              return new Promise(resolve, reject ){  //p2
              ajax2({
                       .........
                       resovle() 
              });
              }
    }).then(function(){
              return new Promise(resolve, reject ){   //p3
              ajax3({
                       .........
                       resovle() 
              });
              }
    })

    如果,我们把每一个promise对象都看做一个状态机的话,它实际上只有三种状态
    进行中已完成已失败, 它分别代表了所承诺的回调函数的执行状态。

    而resolve方法,只是把这个状态机从Pending(进行中) ,改成了Resolved(已完成)

    由于p2现在依赖了p1,因此,p2会观察p1的状态,直到p1变为Resolved
    p2才会开始, 但这时then方法返回的不再是p1,而是p2,因此p3会依赖p2的状态


    问题2: 如果有三个ajax请求,调用顺序无关,但必须保证三个都成功,才能开始第四个?

    Promise.all(function(){   // p123
         return [p1,p2,p3];
    }).then(function(){
         return p4;
    });

    p4依赖p123的状态,那么理解了状态机,promise是不是如此简单呢?

    问题3: 如果有三个ajax请求,只要任意一个成功,则开始第四个?

    Promise.race(function(){   //p123
         return [p1,p2,p3];
    }).then(function(){
         return p4;
    });

    Class保留字终于成了关键字

    终于,我们在有生之年等到class出现了
    这下,JS看起来更像一个标准的面相对象的语言了

    以前编写一个构造函数(类)
    function Pad(color){
         this.color = color;
    }

    现在的写法跟Java更接近了
    class Iphone{
         constructor(color, size){
                this.color = color; 
                this.size = size;
         }
         playgame(){
                //.............
         }
         toString(){
               return `这台手机的颜色是${this.color} 屏幕大小是${this.size}`;
         }
    }

    我们定义了一个类,名字叫Iphone
    通过类生成一个实例:
    var iphone = new Iphone("白色", 5);

    其中constructor被称之为构造方法,在我们new 一个对象的时候,自动被调用

    不过本质上,JS依然使用了原型来实现,也就是说,这不过是一个新的写法而已
    跟以前的构造函数没有区别。

    要注意的是,使用了class来定义类,必须先定义再使用
    以前这样写没有问题:
    new Person();
    function Person(){
    }
    现在这样写报错:
    new Person();
    class Person{
           constructor(){

           }
    }

    甚至还可以定义一次性的类
    let person = new class{
            constructor(){
           
            }
    }();
    这个类被定义出来,只能实例化一次,跟JAVA的匿名内部类很像


    关于继承
    class Son extends Father{
         constructor(age){
                super(); //这相当于调用了父类的构造方法,类似于传统写法Father.call(this)
                //但是这个方法是必须放在第一行先调用
                this.age = age;
         }
    }




    展开操作符 ..

    .

    es6 中asign 合并两个对象,浅克隆

    Promise 实例

    <!DOCTYPE html>
    <html>
    	<head>
    		<meta charset="UTF-8">
    		<title></title>
    	</head>
    	<body>
    	</body>
    	<script type="text/javascript">
    		function ajaxA(success){
    			console.log("ajaxA start");
    			setTimeout(function(){
    				console.log("ajaxA finished");
    				success();
    			},2000);
    		}
    		function ajaxB(success){
    			console.log("ajaxB start");
    			setTimeout(function(){
    				console.log("ajaxB finished");
    				success();
    			},2000);
    		}
    		function ajaxC(success){
    			console.log("ajaxC start");
    			setTimeout(function(){
    				console.log("ajaxC finished");
    				success();
    			},2000);
    		}
    		function ajaxD(){
    			console.log("ajaxD start");
    			setTimeout(function(){
    				console.log("ajaxD finished");
    			},2000);
    		}
    		new Promise(ajaxA).then(function(){
    			return new Promise(ajaxB);
    		}).then(function(){
    			return new Promise(ajaxC);			
    		}).then(ajaxD);
    	</script>
    </html>
    




















  • 相关阅读:
    关于hql执行带有case when 的语句问题,另:数据表的倒置
    xslt 转换 xml
    xsd校验xml
    java 调用存储过程
    js return无效,表单自动提交
    bat 启动java程序
    Delphi 窗体拖动,无边框,透明的设置
    installshield实例(三)发布,补丁,升级
    Installshield实例(二)创建自定义界面
    InstallShield 实例(一)检测JDK,MYSQL,创建数据库
  • 原文地址:https://www.cnblogs.com/fanlinqiang/p/7741219.html
Copyright © 2011-2022 走看看