ECMAScript 6 入门
在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声明常量之后就必须立即初始化!
var声明的变量可以在声明之前使用,相当于默认为其声明其值为undefined了;
但是,let声明的变量一旦用let声明,那么在声明之前,此变量都是不可用的,术语称为“暂时性死区”。
console.log(a); //undefined
var a=8;
console.log("----------");
console.log(b); //控制台报错
let b=9;
所以我们要养成变量先声明再使用的好习惯。
以前,为变量赋值,只能直接指定值。
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
//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 ' + 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!
`);
// 普通字符串
`In JavaScript '\n' 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}?`
在 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覆盖。
//从 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方法用于计算一个数的立方根。
//........
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。
function foo() {}
foo.name // "foo"
var f = v => v;
// 等同于
var f = function (v) {
return v;
};
//如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。
var f = () => 5;
// 等同于
var f = function () { return 5 };
扩展运算符(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
//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
};
ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,前六种是:undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。
let s = Symbol();
typeof s
// "symbol"
上面代码中,变量s就是一个独一无二的值。typeof运算符的结果,表明变量s是 Symbol 数据类型,而不是字符串之类的其他类型。
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
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
Promise 是异步编程的一种解决方案,比传统的解决方案–回调函数和事件--更合理和更强大。它由社区最早提出和实现,ES6将其写进了语言标准,统一了语法,原生提供了Promise
所谓Promise ,简单说就是一个容器,里面保存着某个未来才回结束的事件(通常是一个异步操作)的结果。从语法上说,Promise是一个对象,从它可以获取异步操作的消息。
Promise 对象的状态不受外界影响
三种状态:
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 += ';
});
$('#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的同时,执行
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
}
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);
});
class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y); // 调用父类的constructor(x, y)
this.color = color;
}
toString() {
return this.color + ' ' + super.toString(); // 调用父类的toString()
}
}
//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
' + comment['username'] + ':
+ comment['content'] + '