变量
声明变量
let
声明一个块级作用域变量。
const
声明一个常量,也具有块级作用域。
解构赋值
解构赋值,就是同时对一组变量进行赋值。可以是数组,也可以是对象。
对数组元素进行解构赋值:
let [x,y,z] = [3,4,5];
从一个对象中取出若干属性,使用解构赋值:
let person = {
name:'xiaowang',
age:11,
birthday:'May 1st'
}
let {name,birthday} = person;
console.log(name + '\'s ' + 'birthday is ' + birthday);
解构赋值的使用场景
交换变量的值
let x = 1,y = 2;
[x,y] = [y,x];
快速获取当前页面的域名和路径
let {hostname:domain, pathname:path} = location;
如果一个函数接收一个对象作为参数,那么,可以使用解构直接把对象的属性绑定到变量中。例如,下面的函数可以快速创建一个Date
对象:
function buildDate({year, month, day, hour=0, minute=0, second=0}) {
return new Date(year + '-' + month + '-' + day + ' ' + hour + ':' + minute + ':' + second);
}
它的方便之处在于传入的对象只需要year
、month
和day
这三个属性:
buildDate({ year: 2017, month: 1, day: 1 });
// Sun Jan 01 2017 00:00:00 GMT+0800 (CST)
注:解构赋值的目的主要目的是解少代码量,顺便让代码更易读。
箭头函数
箭头函数有以下几个要注意的点。
- 函数体内的
this
对象,绑定定义时所在的对象,而不是使用时所在的对象。 - 不可以当作构造函数,也就是说,不可以使用
new
命令,否则会抛出一个错误。 - 不可以使用
arguments
对象,该对象在函数体内不存在。
箭头函数相当于匿名函数,且简化了函数的定义。下面举例说明。
只有一个语句时,可省略{}
和return
,下面分别是没有参数,1 个参数,多个参数的写法。
var arrowFun = () => 1 + 2;//没有参数时,括号不能省
var arrowFun1 = x => x * x;//只有一个参数时,可以省略括号
var arrowFun2 = (x,y) => x * y;//多个参数时,括号不能省
var arrowFun3 = x => ({x:1});//返回值为对象时,要加括号
多个语句时,{}
和return
不能省
var arrowFun4 = (x,y) => {
let sum = x + y;
let multiply = x * y;
return {
sum:sum,
multiply:multiply
}
}
箭头函数的this
指向:其实箭头函数中没有this
,因此箭头函数中的this
总是指向它定义时所在的环境
var obj = {
name:'wang',
birth:1995
sayAge:function(){
setTimeout(function(){
console.log( new Date().getFullYear() - this.birth );
},500);
}
}
obj.sayAge();//NaN
var obj = {
name:'wang',
birth:1990
sayAge:function(){
setTimeout( () => {
console.log( new Date().getFullYear() - this.birth );
},500);
}
}
obj.sayAge();//23
函数绑定运算符 ::
函数绑定运算符(::),运算符左边是一个对象,右边是一个函数。该运算符自动将左边的对象,作为上下文环境(this),绑定到右边的函数上。
generator (生成器)
generator(生成器)是ES6标准引入的新的数据类型。一个generator看上去像一个函数,但可以返回多次。
例如:要做一个自增变量,又不想污染全局变量,也不想用闭包的时候。就可以这样
function* countNum(max) {
let num = 1;
while( num < max ){
yield num;
num++;
}
}
let count = countNum(10);
console.log( count.next().value );//1
console.log( count.next().value );//2
console.log( count.next().value );//3
遍历generator对象,可以象上面那样用next()
,也可以用for ... of
。
当你调用一个生成器时,它并非立即执行,而是返回一个已暂停的生成器对象。你可将这个生成器对象视为一次函数调用,只不过立即冻结了,它恰好在生成器函数的最顶端的第一行代码之前冻结了。
每当你调用生成器对象的next()
方法时,函数调用将其自身解冻并一直运行到下一个yield
表达式,再次暂停。
generator 可以在执行过程中多次返回,看上去就像一个可以记住执行过程的函数。用一个对象来保存状态则要麻烦得多。
generator还有另一个巨大的好处,就是把异步回调代码变成“同步”代码。在写 Ajax 的时候特别好用,写起来像是同步的代码,执行的时候实际上是异步的。
拓展运算符…
可变参数个数的函数调用
rest参数只能写在最后,前面用...
标识。
function sum(...rest){
let result = 0;
rest.forEach(function(value){
result += value;
});
return result;
}
复制数组、数组合并
var arr1 = [1,2,3];
var arr2 = [4,5,6];
var newArr1 = [...arr1];//复制数组
var concatArr = [...arr1,...arr2,7,8,9];//合并数组
将字符串转为数组
var str = 'Hello';
strArr = [...str];//['H','e','l','l','o']
替代apply
var arr = [1,2,3];
Math.max(...arr);
将实现了 Iterator 接口的对象转为数组
var nodeList = document.querySelectorAll('div');
var array = [...nodeList];
Map 和 Set
JavaScript对象的键必须是字符串。但实际上Number或者其他数据类型作为键也是非常合理的。
为了解决这个问题,最新的ES6引入了新的数据类型Map
。
Map
Map
是一组键值对的结构,具有极快的查找速度。初始化、赋值、获取值、判断键是否存在,删除键、成员总数、清除全部成员操作如下:
let m = new Map([['wang',11],['li',12],['ye',13]]);
m.set(333,14);
m.get('li');//12
m.has(333);//true
m.get(333);14
m.delete('wang');
m.get('wang');//undefined
m.size;//2
m.clear();
m.size;//0
Set
Set
和Map
类似,是一组key的集合,不过不储存value。由于 key不能重复,所以Set
中没有重复的数据,所以也常常用于过滤数组中的重复数据。大致操作如下:
let s = new Set([1,2,3]);
s;//Set{1,2,3}
s.has(1);//true
s.add(4);//Set{1,2,3,4}
s.delete(1);//Set{2,3,4}
s.clear();//清除全部成员
s.size;//0
//过滤数组重复项
let arr = [1,1,2,3,4,4];
let newArr = [...new Set(arr)];//[1,2,3,4]
iterable
遍历Array
可以采用下标循环,遍历Map
和Set
就不可以。为了统一集合类型,ES6引入了新的iterable
类型。
Array
、Map
、Set
都属于iterable
类型。
iterable
类型的集合可以通过新的for ... of
循环来遍历。语法如下:
let m = new Map([['wang',11],['li',12],['ye',13]]);
for (let x of m){
console.log(x);
}
for ... of
循环和for ... in
循环的区别:
for ... in
循环遍历的实际上是对象的属性名称。一个Array
数组实际上也是一个对象,它的每个元素的索引被视为一个属性。
当我们手动给Array
对象添加了额外的属性后,for ... in
循环将带来意想不到的意外效果:
let arr = [1,2,3];
a.name = 'Hello';
for (var x in arr) {
console.log(x); // '0', '1', '2', 'name'
}
for (var x of arr){
console.log(x);//1,2,3
}
for ... in
循环将把name
包括在内,但Array
的length
属性却不包括在内。
for ... of
循环则完全修复了这些问题,它只循环集合本身的元素。
这就是为什么要引入新的for ... of
循环。
当然更好的方法,是直接使用iterable
内置的方法forEach
:
let m = new Map([['wang',11],['li',12],['ye',13]]);
m.forEach(function(value,key,map){
console.log(key + ': ' + value);
});
let a = [1,2,3];
a.forEach(function(value,index,array){
console.log(value);
});
let s = new Set([3,2,1]);
s.forEach(function(key,samekey,set){
console.log(key);
});
class
ES6 引入了 class
是为了方便实现父类与子类之间的继承。让继承写起来更方便,也更易读。
举个例子:
//创建父类,定义共享函数
class Book{
//构造函数
constructor(props){
this.name = props.name || '书';
this.year = props.year || 0;
this.author = props.author || '侠名';
}
//共享函数
sayName(){
console.log( this.name );
}
}
//创建子类,继承父类
class StoryBook extends Book{
constructor(props){
super(props);//调用父类的构造方法
this.kind = 'StoryBook';
this.pages = props.pages || 0;
}
}
//创建实例
var book_theThreeKingdoms = new StoryBook({name:'三国演义'});
console.log( book_theThreeKingdoms.name );//三国演义
symbol
symbol 是ES6引入的第 6 种基本数据类型,目的是创建独一无二的属性名,解决属性名冲突的问题。
let s = Symbol();
console.log(s);//Symbol()
console.log(typeof s);//"Symbol"
let a = Symbol('age');
let b = Symbol('age');
console.log(a === b);//false
s,a,b 都是一个 Symbol 类型的值,都是独一无二的。
Symbol 是为对象属性名而生,还是来看看 Symbol 怎么作为对象的属性名吧。
使用 Symbol 类型作为对象属性名时,要用[ ]括号记法,不能用点好看哦运算符。
let name = Symbol('name');
obj[name] = 'wang';
对象上的 Symbol 类型的属性,无法通过Object.keys()、Object.getOwnPropertyNames() 来获取。
可以使用 Object.getOwnPropertySymbols()方法获取一个对象上的Symbol属性名。
也可以通过Reflect.ownKeys() 返回所有类型的属性名,包括常规属性名和 Symbol 类型的属性名。
Symbol.for():用于创造相同的 Symbol 类型
Symbol.keyFor():用于查看 Symbol.for()指定的值。
let s0 = Symbol('s0');
let s1 = Symbol.for('s2');
let s2 = Symbol.for('s1');
let s3 = Symbol.for('s2');
console.log(Symbol.keyFor(s0));//undefined
console.log(Symbol.keyFor(s1));//s2
console.log(s3 === s1);//true