1.let 和 const
在ES6之前,js只有全局作用域和函数作用域,ES6中let关键字为其引入了块级作用域。
//{}代码块 { var a = 5; let b = 6; } console.log(a); //5 console.log(b); //b is undefined
let声明的变量只能在其所在的代码块内才能访问,var声明的变量由于是全局变量,因此可以在代码块外访问
const用来定义常量,相当于java中的final关键字。
并且const声明常量之后就必须立即初始化!
2.暂时性死区
var声明的变量可以在声明之前使用,相当于默认为其声明其值为undefined了;
但是,let声明的变量一旦用let声明,那么在声明之前,此变量都是不可用的,术语称为“暂时性死区”。
console.log(a); //undefined var a=8; console.log("----------"); console.log(b); //控制台报错 let b=9;
所以我们要养成变量先声明再使用的好习惯。
3.解构赋值
以前,为变量赋值,只能直接指定值。
let a = 1; let b = 2; let c = 3;
ES6 允许写成下面这样。可以理解为“模式匹配”。
let [a, b, c] = [1, 2, 3];
如果解构不成功,变量的值就等于undefined。
let [x, y, ...z] = ['a']; x // "a" y // undefined z // []
另一种情况是不完全解构,即等号左边的模式,只匹配一部分的等号右边的数组。这种情况下,解构依然可以成功。
let [x, y] = [1, 2, 3]; x // 1 y // 2 let [a, [b], d] = [1, [2, 3], 4]; a // 1 b // 2 d // 4
4.字符串的扩展
//Unicode表示法 "u{码点}" "u{41}u{42}u{43}" //"ABC" let str = "書剑恩仇录"; str.codePointAt(0).toString(16); //返回字符的码点并由十进制转到16进制---66f8 String.fromCodePoint(0x66f8); //返回码点对应的字符---書 for (let a of str){ console.log(a); } //for...of循环遍历字符串中每个字符挨个输出字符 str.at(0); //返回指定位置的字符,目前只是提案 str.startsWith('書',0); //从指定位置往后开始检查,是否以“書”开始,位置参数可省略,默认为0 str.endsWith('剑',1); //从指定位置向前检查,是否以“剑”结束 str.includes('恩',1); //同上,不再啰嗦 str.repeat(2); //字符串重复指定次数“書剑恩仇录書剑恩仇录”,小数取整,Infinity和负数报错 str.padStart(8,'ab'); //指定字符从前开始补直到字符串长度符合要求,"aba書剑恩仇录" str.padEnd(8,'ab'); //指定字符从后开始补直到字符串长度符合要求,"書剑恩仇录aba",若长度小于原长度,返回原字符串,上同
模板字符串
模板字符串采用反引号 `
标识,并且模板字符串中的空格、换行将在输出时有所保留。
传统的 JavaScript 语言,输出模板通常是这样写的。
$('#result').append( 'There are <b>' + basket.count + '</b> ' + 'items in your basket, ' + '<em>' + basket.onSale + '</em> are on sale!' );
上面这种写法相当繁琐不方便,ES6 引入了模板字符串解决这个问题。
$('#result').append(` There are <b>${basket.count}</b> items in your basket, <em>${basket.onSale}</em> are on sale! `);
// 普通字符串 `In JavaScript ' ' is a line-feed.` // 多行字符串 `In JavaScript this is not legal.` console.log(`string text line 1 string text line 2`); // 字符串中嵌入变量 let name = "Bob", time = "today"; `Hello ${name}, how are you ${time}?`
5.正则的扩展
RegExp 构造函数
在 ES5 中,RegExp构造函数的参数有两种情况。
//第一种情况是,参数是字符串,这时第二个参数表示正则表达式的修饰符(flag)。 var regex = new RegExp('xyz', 'i'); // 等价于 var regex = /xyz/i; //第二种情况是,参数是一个正则表示式,这时会返回一个原有正则表达式的拷贝。 var regex = new RegExp(/xyz/i); // 等价于 var regex = /xyz/i;
但是,ES5 不允许此时使用第二个参数添加修饰符,否则会报错。
var regex = new RegExp(/xyz/, 'i'); // Uncaught TypeError: Cannot supply flags when constructing one RegExp from another
ES6 改变了这种行为。如果RegExp构造函数第一个参数是一个正则对象,那么可以使用第二个参数指定修饰符。而且,返回的正则表达式会忽略原有的正则表达式的修饰符,只使用新指定的修饰符。
new RegExp(/abc/ig, 'i').flags // "i"
上面代码中,原有正则对象的修饰符是ig,它会被第二个参数i覆盖。
6.数值的扩展
//从 ES5 开始,在严格模式之中,八进制就不再允许使用前缀0表示,ES6 进一步明确,要使用前缀0o表示。 //如果要将0b和0o前缀的字符串数值转为十进制,要使用Number方法。 Number('0b111') // 7 Number('0o10') // 8 //Number.isFinite()用来检查一个数值是否为有限的(finite),即不是Infinity。 Number.isFinite(0.1); // true Number.isFinite(NaN); // false Number.isFinite(Infinity); // false Number.isFinite('0.1'); // false Number.isFinite(true); // false //传统方法会先调用Number()将非数值的值转为数值,再进行判断,而这两个新方法只对数值有效 isFinite("25") // true Number.isFinite("25") // false //Number.isNaN()用来检查一个值是否为NaN。 Number.isNaN(NaN) // true Number.isNaN(15) // false Number.isNaN('15') // false Number.isNaN(true) // false Number.isNaN(9/NaN) // true Number.isNaN('true' / 0) // true Number.isNaN('true' / 'true') // true //传统方法会先调用Number()将非数值的值转为数值,再进行判断,而这两个新方法只对数值有效 isNaN("NaN") // true Number.isNaN("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.isInteger()用来判断一个数值是否为整数。 Number.isInteger(25) // true Number.isInteger(25.0) // true Number.isInteger(25.1) // false Number.isInteger() // false Number.isInteger(null) // false Number.isInteger('15') // false Number.isInteger(true) // false //ES6 在Number对象上面,新增一个极小的常量Number.EPSILON。根据规格,它表示 1 与大于 1 的最小浮点数之间的差。 Number.EPSILON === Math.pow(2, -52) // true Number.EPSILON // 2.220446049250313e-16 Number.EPSILON.toFixed(20) // "0.00000000000000022204" //ES6 引入了Number.MAX_SAFE_INTEGER和Number.MIN_SAFE_INTEGER这两个常量,用来表示这个范围的上下限。 Number.MAX_SAFE_INTEGER === Math.pow(2, 53) - 1// true Number.MAX_SAFE_INTEGER === 9007199254740991// true Number.MIN_SAFE_INTEGER === -Number.MAX_SAFE_INTEGER// true Number.MIN_SAFE_INTEGER === -9007199254740991// true //ES6 在 Math 对象上新增了 17 个与数学相关的方法。所有这些方法都是静态方法,只能在 Math 对象上调用。 Math.trunc(4.1) // 4 Math.trunc方法用于去除一个数的小数部分,返回整数部分。 Math.sign(-5) // -1 Math.sign方法用来判断一个数到底是正数、负数、还是零 Math.cbrt('8') // 2 Math.cbrt方法用于计算一个数的立方根。 //........
7.函数的扩展
参数默认值
ES5中的写法 function log(x, y) { //y = y || 'World'; --> 这种写法如果y是false则不起作用,所以用下面的方式复制 if (typeof y === 'undefined') { y = 'World'; } console.log(x, y); } log('Hello') // Hello World ES6中的写法 function log(x, y = 'World') { console.log(x, y); } log('Hello') // Hello World
与解构赋值默认值的结合
function m1({x = 0, y = 0} = {}) { return [x, y]; }
作用域
let x = 1; function f(y = x) { let x = 2; console.log(y); } f() // 1
参数y的默认值等于变量x。调用函数f时,参数形成一个单独的作用域。在这个作用域里面,默认值变量x指向第一个参数x,而不是全局变量x,所以输出是2。
name属性
function foo() {} foo.name // "foo"
箭头函数
var f = v => v; // 等同于 var f = function (v) { return v; }; //如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。 var f = () => 5; // 等同于 var f = function () { return 5 };
8.数组的扩展
扩展运算符(spread)是三个点(…)。它好比rest参数的逆运算,将一个数组转为用逗号分隔的参数序列。
//----------------------------复制数组 // ES5的 写法 const a1 = [1, 2]; const a2 = a1.concat(); a2[0] = 2; a1 // [1, 2] // ES6的 写法 const a1 = [1, 2]; // 写法一 const a2 = [...a1]; // 写法二 const [...a2] = a1; //-----------------------------合并数组 const arr1 = ['a', 'b']; const arr2 = ['c']; const arr3 = ['d', 'e']; // ES5 的合并数组 arr1.concat(arr2, arr3); // [ 'a', 'b', 'c', 'd', 'e' ] // ES6 的合并数组 [...arr1, ...arr2, ...arr3] // [ 'a', 'b', 'c', 'd', 'e' ]
Array.from方法用于将两类对象转为真正的数组
Array.from([1, 2, 3])// [1, 2, 3]
Array.of方法用于将一组值,转换为数组。
Array.of(3, 11, 8) // [3,11,8]
数组实例的copyWithin方法,在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。也就是说,使用这个方法,会修改当前数组。
//Array.prototype.copyWithin(target, start = 0, end = this.length) [1, 2, 3, 4, 5].copyWithin(0, 3) // [4, 5, 3, 4, 5] --> 上面代码表示将从 3 号位直到数组结束的成员(4 和 5),复制到从 0 号位开始的位置,结果覆盖了原来的 1 和 2。
数组实例的find方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined。
[1, 4, -5, 10].find((n) => n < 0) // -5 [1, 5, 10, 15].find(function(value, index, arr) { return value > 9; }) // 10
数组实例的findIndex方法的用法与find方法非常类似,返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1。
[1, 5, 10, 15].findIndex(function(value, index, arr) { return value > 9; }) // 2
fill方法使用给定值,填充一个数组。
['a', 'b', 'c'].fill(7) // [7, 7, 7] new Array(3).fill(7) // [7, 7, 7]
entries(),keys()和values()——用于遍历数组
for (let index of ['a', 'b'].keys()) { console.log(index); } // 0 // 1 for (let elem of ['a', 'b'].values()) { console.log(elem); } // 'a' // 'b' for (let [index, elem] of ['a', 'b'].entries()) { console.log(index, elem); } // 0 "a" // 1 "b" //如果不使用for...of循环,可以手动调用遍历器对象的next方法,进行遍历。 let letter = ['a', 'b', 'c']; let entries = letter.entries(); console.log(entries.next().value); // [0, 'a'] console.log(entries.next().value); // [1, 'b'] console.log(entries.next().value); // [2, 'c']
includes方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes方法类似
[1, 2, 3].includes(2) // true [1, 2, 3].includes(4) // false [1, 2, NaN].includes(NaN) // true
数组的空位指,数组的某一个位置没有任何值。比如,Array构造函数返回的数组都是空位。
Array(3) // [, , ,] 0 in [undefined, undefined, undefined] // true 0 in [, , ,] // false
9.对象的扩展
//ES6 允许直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。 function f(x, y) { return {x, y}; } // 等同于 function f(x, y) { return {x: x, y: y}; } f(1, 2) // Object {x: 1, y: 2} //JavaScript 定义对象的属性,有两种方法。 // 方法一 obj.foo = true; // 方法二 obj['a' + 'bc'] = 123; var obj = { foo: true, abc: 123 };
10.Symbol
ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,前六种是:undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。
let s = Symbol(); typeof s // "symbol"
上面代码中,变量s就是一个独一无二的值。typeof运算符的结果,表明变量s是 Symbol 数据类型,而不是字符串之类的其他类型。
11.Set和Map
ES6 提供了新的数据结构 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); } // 2 3 5 4
ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适。
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
12.Proxy与Reflect
Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程
实例:使用 Proxy 实现观察者模式
观察者模式(Observer mode)指的是函数自动观察数据对象,一旦对象有变化,函数就会自动执行。
const person = observable({ name: '张三', age: 20 }); function print() { console.log(`${person.name}, ${person.age}`) } observe(print); person.name = '李四'; // 输出 // 李四, 20
13.Promise
Promise 是异步编程的一种解决方案,比传统的解决方案–回调函数和事件--更合理和更强大。它由社区最早提出和实现,ES6将其写进了语言标准,统一了语法,原生提供了Promise
所谓Promise ,简单说就是一个容器,里面保存着某个未来才回结束的事件(通常是一个异步操作)的结果。从语法上说,Promise是一个对象,从它可以获取异步操作的消息。
Promise 对象的状态不受外界影响
三种状态:
pending:进行中
fulfilled : 已经成功
rejected :已经失败
ajax的异步:
$(function(){ $('#send').click(function(){ $.ajax({ type: "GET", url: "test.json", data: {username:$("#username").val(), content:$("#content").val()}, dataType: "json", success: function(data){ $('#resText').empty(); //清空resText里面的所有内容 var html = ''; $.each(data, function(commentIndex, comment){ html += '<div class="comment"><h6>' + comment['username'] + ':</h6><p class="para"' + comment['content'] + '</p></div>'; }); $('#resText').html(html); } }); }); });
上面的例子实现了一个类似于表单提交的功能。
Promise的异步:
let promise = new Promise(function(resolve, reject) { console.log('Promise'); //实例化后立即执行 resolve(); //任务(成功)完成 }); promise.then(function() { console.log('resolved.'); //任务结束执行 }); console.log('Hi!'); //在实例化promise的同时,执行
14.Iterator接口
for...in循环:只能获取对象的键名 for...of循环:可以获得键值,只返回具有数字索引的属性(键或值) var arr = ['a', 'b', 'c', 'd']; for (let a in arr) { console.log(a); // 0 1 2 3 } for (let a of arr) { console.log(a); // a b c d }
15.async函数
async就表示asynchronous(异步的),async函数会返回一个Promise对象,自然能够调用其方法:
promise.then(success()).catch(error()).finally(function(){});
async function getStockPriceByName(name) { const symbol = await getStockSymbol(name); const stockPrice = await getStockPrice(symbol); return stockPrice; //stockPrice是一个promise对象 } getStockPriceByName('goog').then(function (result) { console.log(result); });
16.class
class ColorPoint extends Point { constructor(x, y, color) { super(x, y); // 调用父类的constructor(x, y) this.color = color; } toString() { return this.color + ' ' + super.toString(); // 调用父类的toString() } }
17.模块化
//lib.js var counter = 3; function incCounter() { counter++; } module.exports = { counter: counter, incCounter: incCounter, };
这是一个js模块,对外暴露两个属性counter(变量)、incCounter(函数),然后再去加载这个模块
这样就可以去调用该模块对外暴露的属性了:
// main.js var mod = require('./lib'); console.log(mod.counter); // 3 mod.incCounter(); console.log(mod.counter); // 3