ES6
ES的全称是 ECMAScript,它是由 ECMA 国际标准化组织,制定的一项脚本语言的标准化规范
ES6是在2016年发布的版本,但是,ES6实际上是一个泛指,泛指ES2015机器后续的版本
ES6 新增了很多语法:声明变量关键字、多种对象结构、箭头函数、扩展运算符等
1、数据类型 Symbol
1.1 初识 Symbol
在ES6之前,只有6种数据类型
Number、String、Boolean、Undefined、Null、Obje
ES6新增了一种原始的数据类型 —— Symbol(符号),表示独一无二、不可变的值,它的出现就是因为对象属性名都是字符串容易造成属性名冲突,为确保对象属性使用唯一的标识符,不会发生属性冲突的危险
Smybol实际上是一种唯一的标识符,可以作为对象的唯一属性名,这样就不会覆盖了
符号没有字面量语法,需要使用 Symbol函数初始化,而且用typeof操作符对符号返回 Symbol
let sym = Symbol()
console.log(sym); // Symbol()
console.log(typeof sym); // symbol
调用Symbol函数时,也可以传入一个字符串参数作为对符号的描述
var a1 = Symbol()
var a2 = Symbol()
var b1 = Symbol('foo')
var b2 = Symbol('foo')
console.log(a1 == b1); // fakse
console.log(a2 == b2); // fakse
注意:
Smybol()函数前不能用new命令,否则会报错,这是因为生成的Symbol是一个原始类型,不是对象,所以它也不能属性
1.2 特性
- 相同参数的Symbol函数返回的值是不相同的
调用Symbol函数时,也可以传入一个字符串参数作为对符号的描述
var a1 = Symbol()
var a2 = Symbol()
var b1 = Symbol('foo')
var b2 = Symbol('foo')
console.log(a1 == b1); // fakse
console.log(a2 == b2); // fakse
- 符号不能与其它类型的数据进行运算,但是
Symbol的值可以显示转换为字符串和布尔值
var sym = Symbol('zxc')
console.log(String(sym), typeof String(sym)); // Symbol(zxc) string
console.log(Boolean(sym)); // true
- Symbol的值作为对象的属性名,由于Symbol值的唯一性,因此不会覆盖属性名
var sym = Symbol();
// 第一种写法
var obj = {}
obj[sym] = '张三'
console.log(obj); // { Symbol(): '张三' }
// 第二种写法
var obj = {
[sym]: '李四'
}
console.log(obj); // { Symbol(): '李四' }
// 第三种:defineProperty方法
var obj = {}
Object.defineProperty(obj, sym, { value: '王五' })
console.log(obj); // {Symbol(): "王五"}
Symbol值作为对象属性名时,不能用点运算符
var obj = {}
obj,sym = '张三'
console.log(obj); // 报错
1.3 遍历属性名Symbol
当Symbol作为对象属性名,不能使用 for...in、for...of 循环取出,也不能使用Object.keys() 等方法
必须使用 Object.getOwnPropertySymbols() 方法获取对象的所有Symbol属性名,该方法会返回一个数组
var a = Symbol('a');
var b = Symbol('b');
var obj = {
[a]: '张三',
[b]: '李四'
}
var arrSym = Object.getOwnPropertySymbols(obj);
console.log(arrSym); // [ Symbol(a), Symbol(b) ]
1.4 for函数 和 keyFor()函数
Symbol.for()
当我们需要使用相同的 Symbol值时,Symbol.for()就可以使用生成相同的值
var a1 = Symbol.for('a');
var a2 = Symbol.for('a');
console.log(a1 === a2); // true
它传入一个字符串参数,然后它会先搜索全局符号注册表是否存在以参数作为名称的Symbol的值,如果不存在,就会生成一个新符号实例并添加到注册表中;后续使用相同的字符串会检查注册表,发现存在该 Symbol,就直接返回该符号
Symbol.for()和Symbol()的区别是 前者会被登记在全局注册表中,而Symbol不会
Symbol.keyFor() 会返回一个已经被登记的Symbol类型的值key
var a1 = Symbol.for('a');
var a2 = Symbol('a');
console.log(Symbol.keyFor(a1)); // a
console.log(Symbol.keyFor(a2)); // undefined
2、声明变量
ES6中新增了两种声明变量的关键字 let 和 const,让js真正有了块级作用域
2.1 let
- 声明的变量只在作用域中有效(在大括号中)
if (true) {
let a = '张三';
};
console.log(a); // Uncaught ReferenceError: a is not defined
变量只在大括号中才可被访问
注意:只有使用 let声明的变量才具有块级作用域,使用 var 声明的变量不具备块级作用域
if (true) {
var a = '张三';
};
console.log(a); // 张三
所以,let可防止循环变量变成全局变量
// var
for (var i = 0; i < 2; i++) {};
// 在{}外面依然可以访问到i
console.log(i); // 2
// let
for (let i = 0; i < 2; i++) {};
// 在{}外面不可以访问到i
console.log(i); // i is not defined
- 所以推荐使用
let,特别对于for循环、if语句这类,可以防止变量变成全局变量
let无法提升变量
// 使用 var 声明变量,不会报错
console.log(a); // undefined
var a = 10;
// 报错
console.log(a); // Cannot access 'a' before initialization
var a = 10;
let不能重复声明
let a = 10
let a = 20
console.log(a); // Identifier 'a' has already been declared
重复声明会提示:变量已经被声明了
注意:是不能重复声明,重复赋值是可以的
let具有暂时性死区
var a = 20;
if (true) {
console.log(a); // Cannot access 'a' before initialization
let a = 10;
};
console.log(a); 和 let a = 10;会跟块级作用域{}绑定在一起,所以 打印的a是指{}中的变量a
2.2 const
作用:声明常量,常量的值(内存地址)是不变化的
- 同样具有块级作用域
if (true) {
const a = 10;
console.log(a); // 10
};
console.log(a); // a is not defined
- 声明变量必须同时赋值
const PI;
PI = 3.14
console.log(PI); // Missing initializer in const declaration
- 声明赋值后的变量不能更改
// 简单数据类型
const PI = 3;
PI = 3.14
console.log(PI); // 报错
// 复杂数据类型
const arr = [1, 2];
// 没有改变arr的地址
arr[0] = '张三'
arr[1] = '李四'
console.log(arr); // [ '张三', '李四' ]
// arr的地址已经别改变
arr = [1, 2]
console.log(arr); // 报错
var、let、const 的区别
var | let | const |
|---|---|---|
| 函数级作用域 | 块级作用域 | 块级作用域 |
| 变量可提升 | 不存在变量提升 | 不存在变量提升 |
| 值可以更改 | 值可以更改 | 值不可更改 |
| 可以重复声明 | 不可以重复声明 | 不可以重复声明 |
试一下水
题目一:
let arr = []
for (var i = 0; i < 2; i++) {
arr[i] = function() {
console.log(i);
}
}
arr[0]();
arr[1]();
输出什么?
- 首先需要明确一点:
i是全局变量,所以遍历结束后i = 2 - 循环结束后,全局数组存储了两个函数:
[function(){console.log(i)};, function(){console.log(i)};] - 最后调用数组中函数,结果均为2
题目二:
var arr = [];
for (let i = 0; i < 2; i++) {
arr[i] = function() {
console.log(i);
};
};
arr[0]();
arr[1]();
现在输出什么呢?
- 现在,
i变成了局部变量。所以函数中存储了对应的i - 结果自然是
0、1