zhengrenzhe's blog   About

ES6声明总结

好久没更新博客了,最近来了豆瓣实习,时间就比较少了。最近学习了ES6,发现JS是越来越强大了。之后会总结下学习每一部分的心得,这次先从ES6新增的声明部分说起。

运行ES6

我使用io.js运行我的js代码,相比node,io无需参数就能支持ES6的特性。

let

let是ES6新增的变量声明方式,用法与var基本相同,不同之处在于由let声明的变量只在其代码块中生效,也就是传说中的块级作用域。iojs在使用let关键字时其所在代码块必须处于严格模式中,所以在需要使用let的部分的顶部写上'use strict'即可。而在iojs的repl模式下执行代码时,不处于严格模式也行。

let作用范围仅限于所在代码块内

在使用let声明变量后,该变量的使用范围仅在其代码块中,若在代码块外使用这个变量,则会提示该变量undefined。

'use strict'

{
	let a = 'xx';
}

console.log(a); // a is not defined

这在循环中就非常有用,而且再也不用匿名自执行函数来模拟块级作用域啦。

let不存在变量声明提升

在使用var声明变量时,存在生命提升,意思就是即使你在声明这个变量之前使用这个变量,这也是有效的,因为js引擎会自动将声明变量提升到作用域顶部。

console.log(a); // 提示undefined,说明变量已经声明,但还未赋值
var a = 'ss';

而使用let声明的变量在声明前就就是无法使用的

console.log(a); // a is not defined
let a = 'ss';

let在一个代码块中声明的变量,不受该代码块之外的干扰

使用var声明变量时,在代码块内你可以修改在代码块之外声明的变量。

var a = 'ss';
if(true){
	a = 'nn';
}
console.log(a); // nn

但在使用let声明变量时,该变量不会受到代码块外的影响,同样它也不会影响代码块之外的同名变量。

var a = 'ss'
if(true){
    let a = 'pp';
    console.log(a); // pp
}
console.log(a); // ss

同样,使用由let声明的变量必须在它声明之前,在声明前该变量是无法使用的,即使外层代码块已经有同名变量了。

var a = 'ss'
if(true){
    console.log(a); // a is not defined
    let a = 'pp';
}
console.log(a);

let不能与var操作同名的变量

当一个变量被一种声明方式声明后,其他声明方式就不能再次声明同名变量。

'use strict'

let a = 'xx';
var a = 'cc'; // Identifier 'a' has already been declared

'use strict'

var a = 'cc';
let a = 'xx'; // Identifier 'a' has already been declared

函数声明的区别

在ES5的严格模式中,函数的声明只能在全局作用域或函数的顶层,比如就不能再if结构中声明函数了。

'use strict'
if(true){
    function b(){
        console.log('b');
    }
}

这样是会报错的,不过在非严格模式中还是可以运行的。 但在io.js中,不论是否处于严格模式,函数都是可以声明在if中的,并且声明的函数同样存在函数声明提升。但是,代码是否处于严格模式会对函数声明提升的范围产生影响。 若不在严格模式,函数声明会提升至函数作用域的最顶端,这时会忽略块级作用域。

a(); // a
function a(){
    console.log('a');
}

b(); // b
if(true){
    function b(){
        console.log('b');
    }
}

此时代码处于非严格模式,函数b声明在if中,它会穿透if而提升到代码最顶端,所以即使在if结构外调用b,也是可以成功的,这点与在ES5下的非严格模式无异。 但当代码处于严格模式中,函数声明只会提升到该块级作用域的顶层,而不会忽略块级作用域直接提升到函数作用域的顶层。

'use strict'

a(); // a
function a(){
    console.log('a');
}

b(); // b is not defined
if(true){
    function b(){
        console.log('b');
    }
}

可以看到这时会抛出异常,说b还没定义。当把b的调用移到if结构中,就能调用b了。

'use strict'

a(); // a
function a(){
    console.log('a');
}

if(true){
    b(); // b
    function b(){
        console.log('b');
    }
}

const

const用来声明常量,常量一旦定义,就无法改变。在非严格模式下,对常量重新赋值会默默的失败,不会报错,在严格模式下则会报错。

'use strict'

const a = 'xx';
a = 'cc';  // Assignment to constant variable.

console.log(a);

const a = 'xx';
a = 'cc';  // 默默的失效

console.log(a);

const同样只作用于块级作用域

'use strict'

if(true){
    const a = 'ss';
}

console.log(a); // a is not defined

const同样不会发生变量提升

'use strict'

if(true){
    console.log(a); // a is not defined
    const a = 'ss';
}

const声明的常量同样不会受到外层代码块的干扰,同样也不会感染外部变量/常量

'use strict'

var a = 'cc';
if(true){
    const a = 'ss';
    console.log(a); // ss
}
console.log(a); // cc

const指向的是常量所在的地址

对于基本数据类型来说,其本身就是不可变的,所以使用const定义了一个保存基本数据类型的常量时,它一定是不可改变的。 但当const定义的常量保存了一个引用类型数据时,由于只是保存了它的地址,而其本身还是可变的,就像下面这样:

'use strict'

const a = {};
a.name = 'xx'
console.log(a); // { name: 'xx' }

const不是保存的是常量吗?为啥还能改变呢?前面说到const保存的只是数据的地址,所以只是常量中存储的地址不可变,但如果是引用类型,其本身是可变的,所以即使把引用类型保存为常量,它还是能修改,但只能修改他的内容,而不能改变它的地址。 可以使用ES5就有的Object.freeze方法,将对象冻结,这样常量即使保存引用类型,它也不能修改啦。

'use strict'

const a = Object.freeze({});
a.name = 'xx' //  Can't add property name, object is not extensible
console.log(a);

← msp430单片机下的一个迷宫游戏  ES6解构赋值总结 →