Lyf凤

靡不有初,鲜克有终

扩大
缩小

三天精通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 '\n' 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 


posted on 2019-12-05 16:09  Lyf凤  阅读(1007)  评论(0编辑  收藏  举报

导航

levels of contents