• 三天精通Vue--ES6的常用语法


    详细学习请参考  阮一峰的ECMAScript 6 入门

    letconst的使⽤

    es5中使用var来声明全局变量

      es5中我们学习了使用var来声明变量,但是使用var声明变量,会存在变量提升的问题。即变量可以在声明之前使用,值为undefined

      栗子1:

        console.log(a);  // var 的结果:undefined-->变量提升
        {
            var a = 2;
            console.log(a);  // 2
        }
        console.log(a);  // var 的结果:2-->变量提升
    
    //在js中一个{},称为作用域,使用var声明变量,会存在变量提升的问题。
    //什么是变量提升呢?当解析脚本的时候,当在脚本中遇到var声明的变量,会将 var a;提到最上面去声明。从而导致此问题。由此也会使,a是一个全局的变量
    //上面的在局部作用域声明并赋值给a=2,相当于这样将a声明为一个全局作用域的变量,然后在局部作用域对a赋值为2.
    		
    		var a;  //声明全局变量
    		console.log(a);  // var 的结果:undefined
        {
            a = 2; // 在局部作用域对全局变量的a赋值
            console.log(a);
        }
        console.log(a);  // var 的结果:2
    

      

      栗子2:

    var a = [];
    for (var i = 0; i < 10; i++) {
      a[i] = function () {
        console.log(i);
      };
    }
    a[6](); // 10
    
    //上面代码中,变量i是var命令声明的,在全局范围内都有效,所以全局只有一个变量i。
    //每一次循环,变量i的值都会发生改变,而循环内被赋给数组a的函数内部的console.log(i),
    //里面的i指向的就是全局的i。也就是说,所有数组a的成员里面的i,指向的都是同一个i,
    //导致运行时输出的是最后一轮的i的值,也就是 10。
    

      

    ES6 新增了let命令来声明变量

      ES6 新增了let命令,用来声明变量。它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效。与使用var来声明变量相比, 使用let声明变量有以下几个特点:

      1.不存在变量提升

      2.不允许重复声明

      3.块级作用域

    1.不存在变量提升

    栗子1:

     console.log(a);
     // let 的结果:Uncaught ReferenceError: a is not defined
    {
    		let a = 2;
    		console.log(a);  //2
    }
    console.log(a);
    // let 的结果:Uncaught ReferenceError: a is not defined
    

      

    栗子2:

      使用let,声明的变量仅在块级作用域内有效,最后输出的是 6。

    var a = [];
    for (let i = 0; i < 10; i++) {
      a[i] = function () {
        console.log(i);
      };
    }
    a[6](); // 6
    
    //上面代码中,变量i是let声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,
    //所以最后输出的是6。你可能会问,如果每一轮循环的变量i都是重新声明的,那它怎么知道上一轮循环的值,
    //从而计算出本轮循环的值?这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,
    //就在上一轮循环的基础上进行计算。
    

      另外,for循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。

     for (let i = 0; i < 3; i++) {
       let i = 'abc';
       console.log(i);
     }
     // abc
     // abc
     // abc
     ​
     //上面代码正确运行,输出了 3 次abc。这表明函数内部的变量i与循环变量i不在同一个作用域,有各自单独的作用域。
      
    

      

    2.不允许重复声明

      let不允许在相同作用域内,重复声明同一个变量。

    // 报错
    function func() {
      let a = 10;
      var a = 1;
    }
    
    // 报错
    function func() {
      let a = 10;
      let a = 1;
    }
    

      因此,不能在函数内部重新声明参数。

    function func(arg) {//函数的参数和函数内部是同一个作用域,这点和for循环不同
      let arg;
    }
    func() // 报错
    
    
    function func(arg) {
      {
        let arg;
      }
    }
    func() // 不报错
    

      

    3.块级作用域

    为什么需要块级作用域?

    ES5 只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景。

      第一种场景,内层变量可能会覆盖外层变量。

    var num=1;
    console.log(num);  //1
    {
       	var num = 11;
        console.log(num)  //11
    }
    console.log(num);  //11-->其实还是变量提升
    

      第二种场景,用来计数的循环变量泄露为全局变量。

    var s = 'hello';
    
    for (var i = 0; i < s.length; i++) {
      console.log(s[i]);
    }
    
    console.log(i); // 5
    
    //上面代码中,变量i只用来控制循环,但是循环结束后,它并没有消失,泄露成了全局变量。
    

      

    let实际上为 JavaScript 新增了块级作用域,而let只能出现在当前作用域的顶层。

    栗子

    let num=1;
    console.log(num);  //1
    {
       	let num = 11;  //内层作用域重复定义全局变量,只在当前作用域有效
        console.log(num)  //11
    }
    console.log(num);  //1
    
    
    //注意区分:
    let num=1;
    console.log(num);  //1
    {
       	num = 11;  //在内层作用域内修改全局变量
        console.log(num)  //11
    }
    console.log(num);  //11
    
    
    //ES6 允许块级作用域的任意嵌套。内层作用域可以重复定义、可以覆盖(通过赋值的方式)、可以直接使用
    //外层作用域的同名变量,而外层作用域可以重复定义、可以覆盖(通过赋值的方式)、不能直接使用内层定义的变量。
    {{{{
      {let insane = 'Hello World'}
      console.log(insane); // 报错,外层不能直接使用内层的变量
    }}}};
    //上面代码使用了一个五层的块级作用域,每一层都是一个单独的作用域。第四层作用域无法读取第五层作用域的内部变量。
    
    {{{{
      let insane = 'Hello World';
      {let insane = 'Hello World'} //内层作用域可以定义外层作用域的同名变量
    }}}};
    

      

    小结:

      1.不存在变量提升

      var命令会发生“变量提升”现象,即变量可以在声明之前使用,值为undefined。这种现象多多少少是有些奇怪的,按照一般的逻辑,变量应该在声明语句之后才可以使用。

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

      2.不允许重复声明

      let不允许在相同作用域内,重复声明同一个变量。

      注意: for循环括号中设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。 而函数的形参和函数内部是同一个作用域。

      3.块级作用域

      实际上还是利用了let命令不存在变量提升,从而方便作用域的任意嵌套。

      内层作用域可以重复定义、可以覆盖(通过赋值的方式)、可以直接使用外层作用域的同名变量,而外层作用域可以重复定义、可以覆盖(通过赋值的方式)、不能直接使用内层定义的变量。

    注意:for循环的花括号不是作用域,只有函数的花括号才是作用域.

    const声明一个常量

    const声明的变量跟let类似,同样拥有上面的三个特性,但是const声明的是常量。

    const声明一个只读的常量。一旦声明,常量的值就不能改变。

    const PI = 3.1415;
    PI // 3.1415
    
    PI = 3;
    // TypeError: Assignment to constant variable.  
    
    //上面代码表明改变常量的值会报错。  

    const声明的变量不得改变值,这意味着,const一旦声明变量,就必须立即初始化,不能留到以后赋值。

    const foo;
    // SyntaxError: Missing initializer in const declaration
    
    //上面代码表示,对于const来说,只声明不赋值,就会报错。
    

      

    注意:

    const命令声明的常量也是不提升,只能在声明的位置后面使用,否则报错。

    const声明的常量,也与let一样不可重复声明(包括不能与var/let声明的变量重复)。

    const的作用域与let命令相同:只在声明所在的块级作用域内有效。

    模板字符串

      传统的 JavaScript 语言,输出模板通常是这样写的(下面使用了 jQuery 的方法)。

    $('#result').append(
      'There are <b>' + basket.count + '</b> ' +
      'items in your basket, ' +
      '<em>' + basket.onSale +
      '</em> are on sale!'
    );
    

      上面这种写法相当繁琐不方便,ES6 引入了模板字符串解决这个问题。

    $('#result').append(`
      There are <b>${basket.count}</b> items
       in your basket, <em>${basket.onSale}</em>
      are on sale!
    `);
    

      模板字符串(template string)是增强版的字符串,用反引号(`)标识。它(模板字符串)可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量(插入变量使用${})。

    // 普通字符串
    `In JavaScript '
    ' is a line-feed.`
    
    // 多行字符串
    `In JavaScript this is
     not legal.`
    
    console.log(`string text line 1
    string text line 2`);
    
    // 字符串中嵌入变量
    let name = "Bob", time = "today";
    `Hello ${name}, how are you ${time}?`
    

      

    箭头函数

      es5的普通函数中使用function关键字来声明函数.

    //普通函数:es5需要这样来定义函数
        function add() {
            return 5;
        }
        add()
    // 函数表达式:即匿名函数的函数名声明在等号左边(如果不声明函数名则会报错)
        let add2 = function(){
            return 5;
        };
        add2()
    // 自执行函数:在匿名函数的基础上
        (function(x,y){
            return x+y;
        })(1,2);
    // 闭包函数:这里就不写了,以免你懵逼...
      .......

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

    //语法规则
    //箭头函数            等价于             匿名函数
    (形参)=>{函数体}  ================> function(形参){函数体}
    
    //栗子
    let add3 = (a,b)=>{
        let c = a+b;
        return a+b+c;
    };
    console.log(add3(3,4));
    
    //等价于
    let add3 = function(a,b){
        let c = a+b;
        return a+b+c;
    };
    console.log(add3(3,4));
    

      如果形参只有一个,可以省略圆括号;如果函数体的代码块部分只有一条语句,并且是返回值语句,则可以省略花括号。栗子:

    var f = v => v;
    
    // 等同于
    var f = function (v) {
      return v;
    };
    

      如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。

    var f = () => 5;
    // 等同于
    var f = function () { return 5 };
    
    var sum = (num1, num2) => num1 + num2;
    // 等同于
    var sum = function(num1, num2) {
      return num1 + num2;
    };
    

      如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回。由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。

    // 报错
    let getTempItem = id => { id: id, name: "Temp" };
    
    // 不报错
    let getTempItem = id => ({ id: id, name: "Temp" });
    

      如果箭头函数只有一行语句,且不需要返回值,可以采用下面的写法,就不用写大括号了。

    let fn = () => void doesNotReturn();
    

      

    es6中对象的声明

      对象(object)是 JavaScript 最重要的数据结构,(类似于Python中的对象,有属性和方法这两种成员,写法类似于Python中的字典,花括号中是键值对的形式)。ES6 对它进行了重大升级。

    //es6中类的定义以及调用==>更接近Python等其他高级语言的类的定义方式
        class Student{
            //对象的单体模式:其实就是普通函数的简单写法,所以this的指向和普通函数是一样的,都是指向调用该方法的对象
            constructor(name,age){//构建函数
                this.name = name;
                this.age = age;
            }
            fav(){//单体模式
               console.log(this.name);
            }
        }
    		//实例化
        let s1 = new Student('alex',18);
        s1.fav();
    

      

    属性的简洁表示法

      ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。

    const foo = 'bar';
    const baz = {foo};
    baz // {foo: "bar"}
    
    // 等同于
    const baz = {foo: foo};
    

      上面代码中,变量foo直接写在大括号里面。这时,属性名就是变量名, 属性值就是变量值。下面是另一个例子。

    function f(x, y) {
      return {x, y};
    }
    
    // 等同于
    
    function f(x, y) {
      return {x: x, y: y};
    }
    
    f(1, 2) // Object {x: 1, y: 2}
    

     

    方法的简洁表示法

      除了属性简写,方法也可以简写(,这样的书写方式我们称为对象的单体模式)。

    const o = {
      method() {
        return "Hello!";
      }
    };
    
    // 等同于
    
    const o = {
      method: function() {
        return "Hello!";
      }
    };
    

      下面是一个实际的例子。

    let birth = '2000/01/01';
    
    const Person = {
    
      name: '张三',
    
      //等同于birth: birth
      birth,
    
      // 等同于hello: function ()...
      hello() { console.log('我的名字是', this.name); }
    
    };
    

      这种写法用于函数的返回值,将会非常方便。

    function getPoint() {
      const x = 1;
      const y = 10;
      return {x, y};
    }
    
    getPoint()
    // {x:1, y:10}
    

      简洁写法在打印对象时也很有用。

    let user = {
      name: 'test'
    };
    
    let foo = {
      bar: 'baz'
    };
    
    console.log(user, foo)
    // {name: "test"} {bar: "baz"}
    console.log({user, foo})
    // {user: {name: "test"}, foo: {bar: "baz"}}
    

      上面代码中,console.log直接输出userfoo两个对象时,就是两组键值对,可能会混淆。把它们放在大括号里面输出,就变成了对象的简洁表示法,每组键值对前面会打印对象名,这样就比较清晰了。

     小结: 对象的三种声明方式

    //普通函数:字面量方式创建对象,等价于fav:function()
        let person = {
            name:'超哥',
            age:18,
            fav(){
               // this指的是person
               console.log(this.name);
            }
        };
    
        person.fav();//person对象调用的fav方法,则fav方法中的this指的就是person对象
    
    
    //箭头函数
        let person2 = {
            name:'超哥2',
            age:188,
            fav:()=>{
               // this指的是person的父类,也就是谁定义了person对象,这里是window对象
               console.log(this);
            }
        };
    
        person2.fav();//person对象调用的fav方法,则fav方法中的this指的是person对象的父类
    
    
    //es6中类的定义以及调用==>更接近Python等其他高级语言的类的定义方式
        class Student{
            //对象的单体模式:其实就是普通函数的简单写法,所以this的指向和普通函数是一样的,都是指向调用该方法的对象
            constructor(name,age){
                this.name = name;
                this.age = age;
            }
            fav(){
               console.log(this.name);
            }
        }
        let s1 = new Student('alex',18);
        s1.fav();
    

       

    this的指向

      关于this的指向,⼤家不要看调用方法的对象是谁,要看被调用的方法此时在当前对象内部 使⽤的是普通函数和对象的单体模式写法,还是箭头函数。

        1.如果是普通函数或者对象的单体模式写法,那么该this指向调用函数的对象。

        2.如果是箭头函数,this指向调用函数时的对象的上下⽂(也就是⽗类) 。

     

    //普通函数:字面量方式创建对象,等价于fav:function()
        let person = {
            name:'超哥',
            age:18,
            fav(){
               // this指的是person
               console.log(this.name);
            }
        };
    
        person.fav();//person对象调用的fav方法,则fav方法中的this指的就是person对象
    
    
    //箭头函数
        let person2 = {
            name:'超哥2',
            age:188,
            fav:()=>{
               // this指的是person的父类,也就是谁定义了person对象,这里是window对象
               console.log(this);
            }
        };
    
        person2.fav();//person对象调用的fav方法,则fav方法中的this指的是person对象的父类
    
    
    //es6中类的定义以及调用==>更接近Python等其他高级语言的类的定义方式
        class Student{
            //对象的单体模式:其实就是普通函数的简单写法,所以this的指向和普通函数是一样的,都是指向调用该方法的对象
            constructor(name,age){
                this.name = name;
                this.age = age;
            }
            fav(){
               console.log(this.name);
            }
        }
        let s1 = new Student('alex',18);
        s1.fav();
    

      对于箭头函数,函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。this指向的固定化,并不是因为箭头函数内部有绑定this的机制,实际原因是箭头函数根本没有自己的this,导致内部的this就是外层代码块的this 

    
    
  • 相关阅读:
    你不知道的JavaScript(上)作用域与闭包
    csu 1982: 小M的移动硬盘
    csu 1985: 驱R符
    csu 1987: 绚丽的手链
    2017ACM/ICPC广西邀请赛 1007 Duizi and Shunzi
    2017ACM/ICPC广西邀请赛 1005 CS Course
    2017ACM/ICPC广西邀请赛 1004 Covering
    hdu 1209 Clock
    trac中wiki直接显示任务代码
    phpcms中action值的含义
  • 原文地址:https://www.cnblogs.com/lyfstorm/p/11989992.html
Copyright © 2020-2023  润新知