let 命令:let声明的变量只在它所在的代码块有效。
{
let a=10;
var b=1;
}
let不存在变量提升,var命令会发生“变量提升”现象,即变量可以在声明之前使用,值为undefined.let命令改变了
语法行为,必须先声明后使用。
let不允许重复声明。
ES6 允许块级作用域的任意嵌套。
const的作用域与let命令相同:只在声明所在的块级作用域内有效。
ES5 只有两种声明变量的方法:var命令和function命令。ES6除了添加let和const命令,后面章节还会提到,
另外两种声明变量的方法:import命令和class命令。所以,ES6 一共有6种声明变量的方法。
ES6开始,全局变量逐步与顶层对象的属性脱钩。
var a=1;
window.a //1
let b=1;
window.b //undefined
变量的解构赋值:ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。
以前:
let a=1;
let b=2;
let c=3;
ES6允许这样写:
let[a,b,c]=[1,2,3];
本质上属于“模式匹配”,如果匹配不成功,则返回undefined.
另一种情况是不完全解构,即等号左边的模式,只匹配一部分的等号右边的数组。这种情况下,解构依然可以成功。
let[x,y]=[1,2,3];
console.log(x); //1
console.log(y); //2
解构不仅可以用于数组,还可以用于对象。
字符串的扩展:
传统上,JavaScript只有indexOf方法,可以用来确定一个字符串是否包含在另一个字符串中。ES6又提供了三种新方法。
-includes():返回布尔值,表示是否找到了参数字符串。
-startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
-endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。
repeat()
repeat方法返回一个新字符串,表示将原字符串重复n次。
'x'.repeat(3) //"xxx"
'hello'.repeat(2) //"hellohello"
ES2017 引入了字符串补全长度的功能。如果某个字符串不够指定长度,会在头部或尾部补全。
padStart()用于头部补全,padEnd()用于尾部补全。
模板字符串:
传统的javascript语言,输出模板通常是这样写的。
$('#result').append(
'There are ' + basket.count + ' ' +
'items in your basket, ' +
'' + basket.onSale +
' are on sale!'
);
上面这种写法相当繁琐不方便,ES6引入了模板字符串解决这个问题。
$("#result").append(`
There are ${$basket.count}items
in your basket,${basket.onSale}
are on sale!
`);
模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,
也可以用来定义多行字符串,或者在字符串中嵌入变量。
ES6 提供了二进制和八进制数值的新的写法,分别用前缀0b(或0B)和0o(或0O)表示。
ES6 在Number对象上,新提供了Number.isFinite()和Number.isNaN()两个方法。
Number.isFinite()用来检查一个数值是否为有限的(finite)。
Number.isFinute(15); //true
Number.isFinute(0.8); //true
Number.isFinute(NaN); //false
ES6 将全局方法parseInt()和parseFloat(),移植到Number对象上面,行为完全保持不变。
// ES5的写法
parseInt('12.34') // 12
parseFloat('123.45#') // 123.45
// ES6的写法
Number.parseInt('12.34') // 12
Number.parseFloat('123.45#') // 123.45
这样做的目的,是逐步减少全局性方法,使得语言逐步模块化。
Number.parseInt === parseInt // true
Number.parseFloat === parseFloat // true
Number.isInteger()用来判断一个值是否为整数。需要注意的是,
在 JavaScript 内部,整数和浮点数是同样的储存方法,
所以3和3.0被视为同一个值。
Number.isInteger(25) //true
Number.isInteger(25.0) //true
Number.isInteger(25.1) //false
函数的扩展
1.函数参数的默认值
ES6 之前,不能直接为函数的参数指定默认值,只能采用变通的方法。
function log(x,y){
y=y||'World';
console.log(x,y);
}
log('Hello'); // Hello World
log('Hello','China') // Hello China
log('Hello','') // Hello World
上面代码检查函数log的参数y有没有赋值,如果没有,则指定默认值为World。
这种写法的缺点在于,如果参数y赋值了,但是对应的布尔值为false,
则该赋值不起作用。就像上面代码的最后一行,参数y等于空字符,结果被改为默认值.
为了避免这个问题,我们需要判断一下参数y是否被赋值,如果没有,再等于默认值。
if(typeof y==='undefined'){
y='World';
}
ES允许为函数的参数设置默认值,即直接写在参数定义的后面。
function log(x,y='World'){
console.log(x,y);
}
log('Hello'); // Hello World
log('Hello','China') //Hello China
log('Hello','') //Hello
对象的扩展:
Object.is() 与严格比较运算符(===)的行为基本一致,
不同之处只有两个:一是+0不等于-0,二是NaN等于自身。
+0 === -0 //true
NaN === NaN // false
Object.is(+0, -0) // false
Object.is(NaN, NaN) // true
Object.assign()方法用于对象的合并,将源对象的所有可枚举属性,复制到目标对象。
var target = { a: 1 };
var source1 = { b: 2 };
var source2 = { c: 3 };
Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}
ES5 引入了Object.keys方法,返回一个数组,
成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键名。
var obj = { foo: 'bar', baz: 42 };
Object.keys(obj)
// ["foo", "baz"]
Object.keys(),Object.values(),Object.entries()
ES5 引入了Object.keys方法,返回一个数组,
成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键名。
var obj = { foo: 'bar', baz: 42 };
Object.keys(obj)
// ["foo", "baz"]
ES2017 引入了跟Object.keys配套的Object.values和Object.entries,
作为遍历一个对象的补充手段,供for...of循环使用。
let {keys,values,entries}=Object;
let obj={a:1,b:2,c:3};
for (let key of keys(obj)){
console.log(key);
}
for(let value of values(obj)){
console.log(value);
}
for(let[key,value] of entries(obj)){
console.log([key,value]);
}
Object.values方法返回一个数组,
成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值。
var obj = { foo: 'bar', baz: 42 };
Object.values(obj)
// ["bar", 42]
Object.entries方法返回一个数组,
成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组。
var obj = { foo: 'bar', baz: 42 };
Object.entries(obj)
// [ ["foo", "bar"], ["baz", 42] ]
ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,
前六种是:undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。
let s = Symbol();
typeof s
// "symbol"
Set和Map数据结构
1.Set
ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。
Set 本身是一个构造函数,用来生成 Set 数据结构。
const s=new Set();
[2,3,5,4,5,2,2].forEach(x=>s.add(x));
for(let i of s){
console.log(i);
}
向Set加入值的时候,不会发生类型转换,所以5和"5"是两个不同的值。
Set内部判断两个值是否不同,使用的算法叫做“Same-value equality”,
它类似于精确相等运算符(===),主要的区别是NaN等于自身,
而精确相等运算符认为NaN不等于自身。
let set = new Set();
let a = NaN;
let b = NaN;
set.add(a);
set.add(b);
set // Set {NaN}
上面代码向 Set 实例添加了两个NaN,但是只能加入一个。这表明,在 Set 内部,两个NaN是相等。
Set 实例的方法分为两大类:操作方法(用于操作数据)和遍历方法(用于遍历成员)。
下面先介绍四个操作方法。
add(value):添加某个值,返回Set结构本身。
-delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
-has(value):返回一个布尔值,表示该值是否为Set的成员。
-clear():清除所有成员,没有返回值。
s.add(1).add(2).add(2);
// 注意2被加入了两次
s.size // 2
s.has(1) // true
s.has(2) // true
s.has(3) // false
s.delete(2);
s.has(2) // false
Set 结构的实例有四个遍历方法,可以用于遍历成员。
-keys():返回键名的遍历器
-values():返回键值的遍历器
-entries():返回键值对的遍历器
-forEach():使用回调函数遍历每个成员
let set = new Set(['red', 'green', 'blue']);
for (let item of set.keys()) {
console.log(item);
}
// red
// green
// blue
for (let item of set.values()) {
console.log(item);
}
// red
// green
// blue
for (let item of set.entries()) {
console.log(item);
}
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]
WeakSet 结构
Map结构
const m = new Map();
const o = {p: 'Hello World'};
m.set(o, 'content')
m.get(o) // "content"
m.has(o) // true
m.delete(o) // true
m.has(o) // false
WeakMap
WeakMap结构与Map结构类似,也是用于生成键值对的集合。
Proxy
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,
可以译为“代理器”。
ES6 原生提供 Proxy 构造函数,用来生成 Proxy 实例。
var proxy = new Proxy(target, handler);
Proxy 对象的所有用法,都是上面这种形式,不同的只是handler参数的写法。其中,new Proxy()表示生成一个Proxy实例,
target参数表示所要拦截的目标对象,handler参数也是一个对象,
用来定制拦截行为。
var proxy = new Proxy({}, {
get: function(target, property) {
return 35;
}
});
proxy.time // 35
proxy.name // 35
proxy.title // 35
如果handler没有设置任何拦截,那就等同于直接通向原对象。
var target = {};
var handler = {};
var proxy = new Proxy(target, handler);
proxy.a = 'b';
target.a // "b"
Generator 函数的语法
yield在英语里的意思就是“产出”)。
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloWorldGenerator();
下一步,必须调用遍历器对象的next方法,使得指针移向下一个状态。
也就是说,每次调用next方法,
内部指针就从函数头部或上一次停下来的地方开始执行,
直到遇到下一个yield表达式(或return语句)为止。换言之,
Generator 函数是分段执行的,yield表达式是暂停执行的标记,
而next方法可以恢复执行。
hw.next()
// { value: 'hello', done: false }
hw.next()
// { value: 'world', done: false }
hw.next()
// { value: 'ending', done: true }
hw.next()
// { value: undefined, done: true }
ES6的Class
JavaScript 语言中,生成实例对象的传统方法是通过构造函数。下面是一个例子。
基本上,ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,
新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
上面的代码用 ES6 的class改写,就是下面这样
//定义类
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
ES6 的类,完全可以看作构造函数的另一种写法。
class Point {
// ...
}
typeof Point // "function"
Point === Point.prototype.constructor // true
构造函数的prototype属性,在 ES6 的“类”上面继续存在。
事实上,类的所有方法都定义在类的prototype属性上面。
Class的继承,extends关键字实现继承。
class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y); // 调用父类的constructor(x, y)
this.color = color;
}
toString() {
return this.color + ' ' + super.toString(); // 调用父类的toString()
}
}