ES6

变量let、const

ES5 中,使用

var
定义变量( var 是 variable 的简写)。

ES6 中,新增了 let 和 const 来定义变量:

  • let
    :定义 变量 ,替代 var。

  • const
    :定义 常量 (定义后,不可修改)。

1、let:定义变量

举例 1:

 {
let a = 'hello';
}
console.log(a); // 打印结果报错:Uncaught ReferenceError: a is not defined

上方代码,打印报错。

举例 2:

 var a = 2;
{
let a = 3;
}

console.log(a); // 打印结果:2

通过上面两个例子可以看出, 用块级作用域内, 用let 声明的变量,只在局部起作用

经典面试题

let 可以防止数据污染,我们来看下面这个 for 循环 的经典面试题。

1、用 var 声明变量:

 for (var i = 0; i < 10; i++) {
console.log('循环体中:' + i);
}

console.log('循环体外:' + i);

上方代码的最后一行可以正常打印结果,且最后一行的打印结果是 10。说明 循环体外 定义的变量 i,是 全局作用域 下的 i。

2、用 let 声明变量:

 for (let i = 0; i < 10; i++) {
console.log('循环体中:' + i); // // 每循环一次,就会在 { } 所在的块级作用域中,重新定义一个新的变量 i
}

console.log('循环体外:' + i);

上方代码的关键在于: 每次循环都会产生一个块级作用域,每个块级作用域中会重新定义一个新的变量 i

另外,上方代码的最后一行,打印会报错。因为用 let 定义的变量 i,只在

{ }
这个 块级作用域 里生效。

总结: 我们要习惯用 let 声明,减少 var 声明带来的 污染全局空间

为了进一步强调 let 不会带来污染,需要说明的是:当我们定义了

let a = 1
时,如果我们在同一个作用域内继续定义
let a = 2
,是会报错的。

2、const:定义常量

在程序开发中,有些变量是希望声明后,在业务层就不再发生变化,此时可以用 const 来定义 常量 。常量就是值(内存地址)不能变化的量。

举例:

 const name = 'smyhvae'; //定义常量 

用 const 声明的常量,只在局部(块级作用域内)起作用;而且,用 const 声明常量时,必须赋值,否则报错。

let 和 const 的特点【重要】

  • 不属于顶层对象 Window

  • 不允许重复声明

  • 不存在变量提升 必须先定义后使用

  • 暂时性死区 当内部变量与外部你变量同名时,内部变量屏蔽外部变量

  • 支持块级作用域 该变量在该作用域不会消失

相反, 用

var
声明的变量:存在变量提升、可以重复声明、 没有块级作用域

var/let/const 的共同点

  • 全局作用域中定义的变量,可以在函数中使用。

  • 函数中声明的变量,只能在函数及其子函数中使用,外部无法使用。

this 指向

this:是函数体内的内置对象(this只能出现在函数体内)

1、以函数的形式(包括普通函数、定时器函数、立即执行函数)调用时,this 的指向永远都是 window。比如

fun();
相当于
window.fun();

2、以方法的形式调用时,this 指向调用方法的那个对象

3、以构造函数的形式调用时,this 指向实例对象

4、以事件绑定函数的形式调用时,this 指向 绑定事件的对象

5、使用 call 和 apply 调用时,this 指向指定的那个对象

第 1 条的举例

 function fun() {
console.log(this);
console.log(this.name);
}

var obj1 = {
name: 'smyh',
sayName: fun,
};

var obj2 = {
name: 'vae',
sayName: fun,
};

var name = '全局的name属性';

//以函数形式调用,this是window
fun(); //可以理解成 window.fun()

打印结果:

 Window
全局的name属性

上面的举例可以看出,this 指向的是 window 对象,所以 this.name 指的是全局的 name。

第 2 条的举例

 function fun() {
console.log(this);
console.log(this.name);
}

var obj1 = {
name: 'smyh',
sayName: fun,
};

var obj2 = {
name: 'vae',
sayName: fun,
};

var name = '全局的name属性';

//以方法的形式调用,this是调用方法的对象
obj2.sayName();

打印结果:

 Object
vae

上面的举例可以看出,this 指向的是 对象 obj2 ,所以 this.name 指的是 obj2.name。

ES6 箭头函数中 this 的指向

ES6 中的箭头函数并不使用上面的准则,而是会继承外层函数调用的 this 绑定(无论 this 绑定到什么)。

bind() 方法

bind() 方法的作用

bind() 方法 不会调用函数 ,但是可以改变函数内部的 this 指向。

把call()、apply()、bind()这三个方法做一下对比,你会发现:实际开发中, bind() 方法使用得最为频繁。如果有些函数,我们不需要立即调用,但是又想改变这个函数内部的this指向,此时用 bind() 是最为合适的。

语法:

 新函数 = fn1.bind(想要将this指向哪里, 函数实参1, 函数实参2); 

参数:

  • 第一个参数:在 fn1 函数运行时,指定 fn1 函数的this 指向。如果不需要改变 this 指向,则传 null。

  • 其他参数:fn1 函数的实参。

解释:它不会调用 fn1 函数,但会返回 由指定this 和指定实参的 原函数拷贝 。可以看出, bind() 方法是有返回值的。

箭头函数

定义箭头函数的语法

语法:

 (参数1, 参数2 ...) => { 函数体 } 

解释:

  • 如果有且仅有 1 个形参,则

    ()
    可以省略

  • 如果函数体内有且仅有 1 条语句,则

    {}
    可以省略,但前提是,这条语句必须是 return 语句。

    举例

    写法 1、定义和调用函数:(传统写法)

     function fn1(a, b) {
    console.log('haha');
    return a + b;
    }

    console.log(fn1(1, 2)); //输出结果:3

    写法 2、定义和调用函数:(ES6 中的写法)

     const fn2 = (a, b) => {
    console.log('haha');
    return a + b;
    };

    console.log(fn2(1, 2)); //输出结果:3

    上面的两种写法,效果是一样的。

    从上面的箭头函数中,我们可以很清晰地看到变量名、参数名、函数体。

    【重要】在箭头函数中,如果方法体内只有一句话,且这句话是 return 语句,那就可以把

    {}
    省略。写法如下:

     const fn2 = (a, b) => a + b;

    console.log(fn2(1, 2)); //输出结果:3

    在箭头函数中,如果形参只有一个参数,则可以把

    ()
    省略。写法如下:

     const fn2 = (a) => {
    console.log('haha');
    return a + 1;
    };

    console.log(fn2(1)); //输出结果:2

    箭头函数的 this 的指向


    ES6 之前的普通函数中:this 指向的是函数被调用的对象(也就是说,谁调用了函数,this 就指向谁)。

    而 ES6 的箭头函数中: 箭头函数本身不绑定 this ,this 指向的是 箭头函数定义位置的 this (也就是说,箭头函数在哪个位置定义的,this 就跟这个位置的 this 指向相同)。

     const obj = { name: '123' };

    function fn1() {
    console.log(this); // 第一个 this
    return () => {
    console.log(this); // 第二个 this
    };
    }

    const fn2 = fn1.call(obj);
    fn2();

    打印结果:

     obj
    obj

    代码解释:

    上面的代码中,箭头函数是在 fn1()函数里面定义的,所以第二个 this 跟 第一个 this 指向的是 同一个位置 。又因为,在执行

    fn1.call(obj)
    之后,第一个 this 就指向了 obj,所以第二个 this 也是指向 了 obj。

变量的解构赋值

解构赋值 :ES6 允许我们,按照一一对应的方式,从数组或者对象中 提取值 ,再将提取出来的值赋值给变量。

解构:分解数据结构;赋值:给变量赋值。

解构赋值在实际开发中可以大量减少我们的代码量,并且让程序结构更清晰。

数组的解构赋值

数组的结构赋值:将数组中的值按照 位置 提取出来,然后赋值给变量。

语法

在 ES6 之前,当我们在为一组变量赋值时,一般是这样写:

 var a = 1;
var b = 2;
var c = 3;

或者是这样写:

 var arr = [1, 2, 3];

var a = arr[0];
var b = arr[1];
var c = arr[2];

现在有了 ES6 之后,我们可以通过数组解构的方式进行赋值:(根据 位置 进行一一对应)

 let [a, b, c] = [1, 2, 3]; 

二者的效果是一样的,但明显后者的代码更简洁优雅。

对象的解构赋值

对象的结构赋值:将对象中的值按照 属性匹配的方式 提取出来,然后赋值给变量。

语法

在 ES6 之前,我们从接口拿到 json 数据后,一般这么赋值:

 var name = json.name;

var age = json.age;

var sex = json.sex;

上面这种写法,过于麻烦了。

现在,有了 ES6 之后,我们可以使用对象解构的方式进行赋值。举例如下:

 const person = { name: 'qianfan', age: 25, sex: '男' };
let { name, age, sex } = person; // 对象的结构赋值

console.log(name); // 打印结果:qianguyihao
console.log(age); // 打印结果:28
console.log(sex); // 打印结果:男

上方代码可以看出,对象的解构与数组的结构,有一个重要的区别: 数组 的元素是按次序排列的,变量的取值由它的 位置 决定;而 对象的属性没有次序 ,是 根据键来取值 的。

圆括号的使用

如果变量 foo 在解构之前就已经定义了,此时你再去解构,就会出现问题。下面是错误的代码,编译会报错:

 let foo = 'haha';
{ foo } = { foo: 'smyhvae' };
console.log(foo);

要解决报错,只要在解构的语句外边,加一个圆括号即可:

 let foo = 'haha';
({ foo } = { foo: 'smyhvae' });
console.log(foo); //输出结果:smyhvae

字符串解构

字符串也可以解构,这是因为,此时字符串被转换成了一个类似数组的对象。举例如下:

 const [a, b, c, d] = 'hello';
console.log(a);
console.log(b);
console.log(c);

console.log(typeof a); //输出结果:string

打印结果:

 h
e
l
string

标签: Javascript

添加新评论