一、变量
1. let/const 关键字
在 ES6 之前,我们声明变量都用的关键字 var
,甚至可以直接通过为变量赋值来声明全局变量 a = 1(等价于window.a = 1)
。在 ES6 中新增了 let
const
关键字用于变量声明,相比较 var
关键字,他们有如下特点:
-
let
关键字不存在变量提升的特性;不允许重复声明;仅在代码块中有效。 -
const
关键字用于常量声明,不允许修改,因此在声明时就必须赋值。 - 注意:
const
声明的对象其属性可以修改的,声明的数组其数组元素也可以修改
// let
console.log(a); // undefined
let a = 10;
let a = 20; // 报错:Uncaught SyntaxError: Identifier 'a' has already been declared
// const
const num = 1;
num = 2; // 报错:Uncaught TypeError: Assignment to constant variable.
const obj = {name: 'mu', age: 18};
obj.age = 19; // {name: 'mu', age: 19}
const arr = [1, 2, 3, 4];
arr[0] = 5; // [5, 2, 3, 4]
面试常见题
for(var i = 0; i < 3; i++) {
setTimeout(function () {
console.log(i); // 输出 3 3 3
}, 10)
}
for(let i = 0; i < 3; i++) {
setTimeout(function () {
console.log(i); // 输出 0 1 2
}, 10)
}
原因: let
仅在代码块有效,因此在获取动态更替的值时可以解决作用域问题。
2. 解构赋值
ES6 允许按照一定的格式从数组和对象(字符串将切割为数组)中获取特定的值。原理上基于模式匹配,即等号左右两边的格式保持一致时,将进行一一对应赋值。有以下几种解构赋值:
- 数组的解构赋值
let [a, b, c] = [1, 2, 3];
let [a, [b, c], d] = [1, [2, 3], 4];
- 对象的解构赋值
let {name, age} = {name: 'mu', age: 18};
let {name: n1, age: a1} = {name: 'mu', age: 18}; // n1 = 'mu', a1 = 18
- 字符串的解构赋值
let {length} = 'abcd'; // length = 4;
let [s1, s2, s3, s4] = 'abcd'; // s1 = 'a', s2 = 'b', s3 = 'c', s4 = 'd'
此外,解构赋值允许设置默认值,此时等号左右格式可以不一致。
let [x, y = 2] = [1]; // x = 1, y = 2
二、函数
ES6 在函数声明和函数传参上都做了更新,使得函数更加简洁,使用更加方便。
1. 箭头函数
ES5 中函数声明需要采用 function () {}
的形式,在 ES6 中函数声明可以省去 function
关键字,引入 “=>
” 符号,常应用于回调函数的声明中。例如:可以用如下方式声明一个简单的 add
函数,传入数组的 map
方法中。
let arr = [0, 1, 2, 3];
let newArr = arr.map( (el) => {return el + 1} ); // [1, 2, 3, 4]
当传入参数仅有一个是,“
=>
” 左侧的括号可以省略当函数主体只有一个返回语句,“
=>
” 右侧的花括号可以省略。(注意:当返回的值是对象时,需要在对象外添加括号() => ({age: 18})
)
箭头函数相比较普通函数有如下两点不同:
内部的
this
继承上一个作用域内的this
,因此箭头函数不能作为构造函数;函数内部没有
arguments
这个默认参数
var name = 'window';
var obj = {
name: 'obj',
print: () => {
console.log(this.name)
}
}
obj.print(); // 'window'
2. 参数
ES6 支持默认参数,rest 参数(...变量名
),在函数在参数传递上更加方便,使得函数的功能更加灵活。
- 默认参数。当设置了参数默认值,那么在函数初始化到初始化结束期间,参数会有一个单独的作用域
function add(x, y = 0) {
return x + y;
}
add(2, 3) // 5
add(2) // 2
- rest 参数
function add(...args) {
return args.reduce((a, b) => a+b);
}
add(1, 2, 3, 4) // 10
作用域问题:
var n = 'window';
function fn(n, m = n) {
console.log(m)
}
fn('function') // 'function'
/*
分析:调用fn时,生成一个参数作用域
{
n = 'function';
m = n;
}
因此输出m = 'funciton'
*/
var n = 'window';
function fn(m = n) {
var n = 'function';
console.log(m);
}
fn() // 'window'
三、数据结构
1. Set 和 Map
ES6 中新增了两种数据结构 Set
和 Map
,两者的实例都通过 new
关键字声明。这里简单介绍他们的功能和常用的属性和函数:
-
Set
:集合,与数组的功能类似,不同的是内部不存在重复的元素,因此常用于数组去重。-
size
属性:等同于数组的length
属性,获取数据集合的长度。 -
add(val)
方法:向集合中添加元素 -
delete(val)
方法: 删除集合中某一个值 -
has(val)
方法:等同于数组的includes()
方法,判断集合中是否包含某个值 -
clear()
方法:清空集合。
-
-
Map
:哈希表,映射关系键值对,“键”可为任意类型(包括函数、对象和基本类型)。相比普通的Object
在涉及频繁增删键值对的场景下会有些性能优势。-
size
属性: 同上。 -
get(key)
方法:获取哈希表中该键所对应的值。 -
set(key, val)
方法:用于存储新的键值对到哈希表中。 -
keys()
和values()
方法:分别用于获取该哈希表中所有的键、所有的值。
-
如何判断某一实例是否为 Map
或 Set
:
使用 typeof
对实例进行判断得到的是 object
,同 Array
一样,如果要判断该实例是否为 Set
或 Map
,可以使用以下两种方法:
调用原型上的
toString
方法使用原型链判断实例的
constructor
属性
let m = new Map();
let s = new Set();
console.log(Object.prototype.toString.call(m)) // '[object Map]'
console.log(Object.prototype.toString.call(s)) // '[object Set]'
console.log(m.constructor === Map) // true
console.log(s.constructor === Set) // true
如何遍历拿到 Map
或 Set
中的所有元素:
这两种数据类型都是有序的,可以通过 for...of
来进行迭代,其原理在于生成一个遍历器(指针对象),指针默认指向当前对象的内存地址。每次迭代,指针后移(当前元素的字节长度),返回数据并判断是否结束(判断指针是否等于对象结束的位置,起始位置+单元字节*元素长度)
for (let val of set) {}
for (let item of map) {} // 每次返回形式为 [key, value]的数组
for (let key of map.keys()) {}
for (let key of map.values()) {}
面试常见题
// 实现数组的去重
let arr = [1, 2, 3, 3, 9, 8, 9, 2];
arr = Array.from(new Set(...[arr])) // [1, 2, 3, 9, 8]
2. Symbol
在 ES5 中数据类型包括:String
、Number
、Boolean
、Array
、Undefined
、Null
、Object
,而在 ES6 中新增了 Symbol
数据类型(使用 typeof
输出为 “symbol
” )。Symbol
的最大特点是独一无二,任意两个 Symbol
都是不相等的。需要注意的事它并不需要 new
关键字进行声明,直接使用 Symbol()
进行创建即可,可以传入一个字符串作为其描述。
let sym1 = Symbol();
let sym2 = Symbol('mu');
Symbol('foo') === Symbol('foo'); // false