下载代码:https://github.com/woojean/es6-practice
array
// 扩展运算符:将一个数组转为参数序列(用逗号分隔)
// ====================================================================================================
console.log(1, ...[2, 3, 4], 5); // 1 2 3 4 5
function add(x, y) {
return x + y;
}
const numbers = [4, 38];
console.log(add(...numbers)); // 42 即add(...[x,y])相当于add(x,y)
// 如果扩展运算符后面是一个空数组,则不产生任何效果
console.log([...[], 1]); // [1]
// 与解构赋值结合使用
const [first, ...rest] = [1, 2, 3, 4, 5];
console.log(first); // 1
console.log(rest); // [2, 3, 4, 5]
// 如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。
// 任何 Iterator 接口的对象,都可以用扩展运算符转为真正的数组。
let map = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three'],
]);
let arr = [...map.keys()]; // [1, 2, 3]
console.log(arr);
const go = function*(){
yield 1;
yield 2;
yield 3;
};
console.log([...go()]); // [1, 2, 3]
// Array.from()
// ====================================================================================================
// Array.from方法用于将两类对象转为真正的数组:类似数组的对象和可遍历的对象
// 所谓类似数组的对象,本质特征只有一点,即必须有length属性。因此,任何有length属性的对象,都可以通过Array.from方法转为数组
let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
};
let arr = Array.from(arrayLike);
console.log(arr); // ['a', 'b', 'c']
console.log(Array.from('hello')); // ['h', 'e', 'l', 'l', 'o']
let namesSet = new Set(['a', 'b'])
console.log(Array.from(namesSet)); // ['a', 'b']
console.log(Array.from({ length: 3 })); // [ undefined, undefined, undefined ]
// Array.from还可以接受第二个参数,用来对每个元素进行处理,将处理后的值放入返回的数组
console.log(Array.from([1, 2, 3], (x) => x * x)); // [1, 4, 9]
// Array.of()
// ====================================================================================================
// Array.of方法用于将一组值,转换为数组
console.log(Array.of(3, 11, 8)); // [3,11,8]
// copyWithin()
// ====================================================================================================
// 数组实例的copyWithin方法,在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。
console.log([1, 2, 3, 4, 5].copyWithin(0, 3)); // [4, 5, 3, 4, 5]
console.log([1, 2, 3, 4, 5].copyWithin(0, 3, 4)); // [4, 2, 3, 4, 5]
// find()和findIndex()
// 用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员。
console.log([1, 4, -5, 10].find((n) => n < 0)); // -5
console.log([1, 5, 10, 15].find(function(value, index, arr) {
return value > 9;
})); // 10
console.log([1, 5, 10, 15].findIndex(function(value, index, arr) {
return value > 9;
})); // 2
// fill()
// ====================================================================================================
console.log(['a', 'b', 'c'].fill(7)); // [7, 7, 7]
console.log(['a', 'b', 'c'].fill(7, 1, 2)); // ['a', 7, 'c']
// entries(),keys() 和 values()
// ====================================================================================================
// 它们都返回一个遍历器对象,可以用for...of循环进行遍历,唯一的区别是keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历。
for (let index of ['a', 'b'].keys()) {
console.log(index);
}
// 0
// 1
for (let [index, elem] of ['a', 'b'].entries()) {
console.log(index, elem);
}
// 0 "a"
// 1 "b"
// includes()
// ====================================================================================================
// includes()方法返回一个布尔值,表示某个数组是否包含给定的值
console.log([1, 2, 3].includes(2)); // true
console.log([1, 2, 3].includes(3, -1)); // true
// 数组的空位
// ====================================================================================================
// 数组的空位指,数组的某一个位置没有任何值。
[,'a'].forEach((x,i) => console.log(i)); // 1
console.log([,'a'].every(x => x==='a')); // true
console.log(Array.from(['a',,'b'])); // [ "a", undefined, "b" ]
let arr = [, ,];
for (let i of arr) {
console.log(1);
}
// 1
// 1
// 由于空位的处理规则非常不统一,所以建议避免出现空位。
destructuring
// 解构数组
// ====================================================================================================
let [x, y, ...z] = ['abc'];
console.log(x); // "abc"
console.log(y); // undefined
console.log(z); // []
let [...a] = ['abc'];
console.log(a); // [ 'abc' ]
let [b] = [];
console.log(b); // undefined
let [c = true] = [];
console.log(c); // true
let [d = 1] = [undefined];
console.log(d); // 1
let [e = 1] = [null]; // 默认值生效的条件是,对象的属性值严格等于(===)undefined
console.log(e); // null
// 解构生成器
// ====================================================================================================
function* fibs() {
let a = 0;
let b = 1;
while (true) {
yield a;
[a, b] = [b, a + b];
}
}
let [first, second, third] = fibs();
let [fourth, fifth, sixth, seven] = fibs();
console.log(first); // 0
console.log(second); // 1
console.log(third); // 1
console.log(fourth); // 0
console.log(fifth); // 1
console.log(sixth); // 1
console.log(seven); // 2
// 解构对象
// ====================================================================================================
// 对象的属性没有次序,变量必须与属性同名,才能取到正确的值
let { bar, foo } = { foo: "aaa", bar: "bbb" }; // 相当于 let { foo: foo, bar: bar } = { foo: "aaa", bar: "bbb" };
console.log(foo); // "aaa"
console.log(bar); // "bbb"
let { baz } = { foo: "aaa", bar: "bbb" };
console.log(baz); // undefined
// 如果变量名与属性名不一致,必须写成下面这样
let obj1 = { first: 'hello', last: 'world' };
let { first: f, last: l } = obj1;
console.log(f); // 'hello'
console.log(l); // 'world'
// 用于嵌套结构的对象
let obj2 = {
p: [
'Hello',
{ y2: 'World' }
]
};
let { p: [x2, { y2 }] } = obj2;
console.log(x2); // "Hello"
console.log(y2); // "World"
// 由于数组本质是特殊的对象,因此可以对数组进行对象属性的解构
let arr = [1, 2, 3];
let {0 : firstItem, [arr.length - 1] : lastItem} = arr;
console.log(firstItem); // 1
console.log(lastItem); // 3
// 解构字符串
// ====================================================================================================
let {length : len} = 'hello';
console.log(len); // 5
// 解构函数参数
// ====================================================================================================
let ret = [[1, 2], [3, 4]].map(([a, b]) => a + b);
console.log(ret); // [ 3, 7 ]
function
// 函数参数的默认值
// ====================================================================================================
// 参数变量是默认声明的,所以不能用let或const再次声明
function foo(x = 5) {
let x = 1; // SyntaxError: Identifier 'x' has already been declared
console.log(x);
}
// 参数默认值是惰性求值的
let x = 99;
function foo(p = x + 1) {
console.log(p);
}
foo() // 100
x = 100;
foo() // 101
// 参数默认值与结构赋值同时使用
function foo({x, y = 5}) {
console.log(x, y);
}
foo({}) // undefined 5
foo({x: 1}) // 1 5
foo({x: 1, y: 2}) // 1 2
foo() // TypeError: Cannot read property 'x' of undefined
// 指定了默认值以后,函数的length属性,将返回没有指定默认值的参数个数
console.log((function (a, b = 1, c) {}).length); // 1
// 一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域
let x = 1;
function f(y = x) {
let x = 2;
console.log(y);
}
f() // 1
/*
函数f调用时,参数y = x形成一个单独的作用域。这个作用域里面,变量x本身没有定义,所以指向外层的全局变量x。函数调用时,函数体内部的局部变量x影响不到默认值变量x。
如果此时,全局变量x不存在,就会报错。
*/
// rest参数(用于获取函数的多余参数)
// ====================================================================================================
function add(...values) {
let sum = 0;
for (var val of values) {
sum += val;
}
return sum;
}
console.log(add(2, 5, 3)) // 10
// 函数的length属性,不包括rest参数
console.log((function(a, ...b) {}).length) // 1
/*
ES6规定只要函数参数使用了默认值、解构赋值、或者扩展运算符,那么函数内部就不能显式设定为严格模式
*/
// 箭头函数
// ====================================================================================================
// 由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错
let getTempItem = id => ({ id: id, name: "Temp" });
console.log(getTempItem(1));
// 箭头函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象
// ====================================================================================================
function foo() {
setTimeout(() => {
console.log('id:', this.id);
}, 100);
}
var id = 21;
foo.call({ id: 42 });
// id: 42
// 尾调用优化与尾递归
// ====================================================================================================
// 略。
Generator
// 基本概念
// ====================================================================================================
// 执行Generator函数会返回一个遍历器对象
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloWorldGenerator();
console.log(hw.next()); // { value: 'hello', done: false }
console.log(hw.next()); // { value: 'world', done: false }
console.log(hw.next()); // { value: 'ending', done: true }
console.log(hw.next()); // { value: undefined, done: true }
// next方法的参数
// ====================================================================================================
// next方法可以带一个参数,该参数就会被当作"上一个yield表达式的"返回值
function* f() {
for(var i = 0; true; i++) {
var reset = yield i;
if(reset) { i = -1; }
}
}
var g = f();
console.log(g.next()) // { value: 0, done: false }
console.log(g.next()) // { value: 1, done: false }
console.log(g.next(true)) // { value: 0, done: false }
console.log(g.next()) // { value: 1, done: false }
// Generator.prototype.throw()
// ====================================================================================================
// 遍历器对象有一个throw方法,可以在函数体外抛出错误,然后在Generator函数体内捕获
var g = function* () {
try {
yield;
} catch (e) {
console.log('内部捕获', e);
}
};
var i = g();
i.next();
try {
i.throw('a');
i.throw('b');
} catch (e) {
console.log('外部捕获', e);
}
// 内部捕获 a
// 外部捕获 b
// Generator.prototype.return()
// ====================================================================================================
// 遍历器对象有一个return方法,可以返回给定的值,并且终结遍历Generator函数。
function* gen() {
yield 1;
yield 2;
yield 3;
}
var g = gen();
console.log(g.next()); // { value: 1, done: false }
console.log(g.return('foo')); // { value: "foo", done: true }
console.log(g.next()); // { value: undefined, done: true }
Iterator
// 基本概念
// ====================================================================================================
// 遍历器对象本质上,就是一个指针对象。
// 每一次调用next方法,都会返回一个包含value和done两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。
// Iterator对象的next()方法的模拟实现
var it = makeIterator(['a', 'b']);
console.log(it.next()); // { value: "a", done: false }
console.log(it.next()); // { value: "b", done: false }
console.log(it.next()); // { value: undefined, done: true }
function makeIterator(array) {
var nextIndex = 0;
return {
next: function() {
return nextIndex < array.length ?
{value: array[nextIndex++], done: false} :
{value: undefined, done: true};
}
};
}
/*
{ value: 'a', done: false }
{ value: 'b', done: false }
{ value: undefined, done: true }
*/
let
// 基本使用
// ====================================================================================================
let a = [];
let b = [];
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
let v = 1;
b[i] = function () {
console.log(v + 10);
};
v = 2;
b[100] = function () {
let x = v;
x += 1;
console.log((()=>{return x})());
};
}
a[6](); // 6
b[6](); // 12
b[100](); // 3
//console.log(v); // ReferenceError: v is not defined
// for循环设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域
// ====================================================================================================
for (let i = 0; i < 3; i++) {
let i = 'abc';
console.log(i);
}
// abc
// abc
// abc
// “暂时性死区”(temporal dead zone,简称 TDZ)
// ====================================================================================================
var tmp = 123;
if (true) {
//console.log(tmp); // ReferenceError: tmp is not defined
//typeof tmp; // ReferenceError: tmp is not defined。注意这里不是返回undefined
let tmp;
}
/*
暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。
*/
// const注意点
// ====================================================================================================
const foo = {};
foo.prop = 123;
console.log(foo.prop); // 123
foo = {}; // TypeError: "foo" is read-only
map
// 基本用法
// ====================================================================================================
// map的键可以是对象
const m1 = new Map();
const o = {p: 'Hello World'};
m1.set(o, 'content')
console.log(m1.get(o)); // content
// 任何具有Iterator接口、且每个成员都是一个双元素的数组的数据结构都可以当作Map构造函数的参数
const m2 = new Map([
['name', '张三'],
['title', 'Author']
]);
console.log(m2); // Map { 'name' => '张三', 'title' => 'Author' }
// WeakMap
// 略。
module
// 基本概念
// ====================================================================================================
// ES6模块的设计思想,是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS和AMD模块,都只能在运行时确定这些东西(需要在运行时新建对象,再使用对象的属性或方法)。
// 一个模块就是一个独立的文件,该文件内部的所有变量,外部无法获取。如果希望外部能够读取模块内部的某个变量,就必须使用export关键字输出该变量。
// ES6的模块自动采用严格模式。
// 详略。
number
// Number对象的方法
// ====================================================================================================
// Number.isFinite(...)
console.log(Number.isFinite(15)); // true
console.log(Number.isFinite(0.8)); // true
console.log(Number.isFinite(NaN)); // false
console.log(Number.isFinite(Infinity)); // false
console.log(Number.isFinite(-Infinity)); // false
console.log(Number.isFinite('foo')); // false
console.log(Number.isFinite('15')); // false
console.log(Number.isFinite(true)); // false
// Number.isNaN(...)
console.log(Number.isNaN(NaN)); // true
console.log(Number.isNaN(15)); // false
console.log(Number.isNaN('15')); // false !!!
console.log(Number.isNaN(true)); // false !!!
console.log(Number.isNaN(9/NaN)); // true
console.log(Number.isNaN('true'/0)); // true
console.log(Number.isNaN('true'/'true')); // true
// Number.isFinite()对于非数值一律返回false, Number.isNaN()只有对于NaN才返回true,非NaN一律返回false。
// Number.parseInt(...), Number.parseFloat(...)
console.log(Number.parseInt('12.34')); // 12
console.log(Number.parseFloat('123.45#')); // 123.45
// Number.isInteger(...)
console.log(Number.isInteger(25)); // true
console.log(Number.isInteger(25.0)); // true 在JavaScript内部,整数和浮点数是同样的储存方法,所以25和25.0被视为同一个值。
console.log(Number.isInteger(25.1)); // false
console.log(Number.isInteger("15")); // false
console.log(Number.isInteger(true)); // false
// Number.EPSILON
console.log(Number.EPSILON); // 2.220446049250313e-16
// Number.isSafeInteger()
// JavaScript能够准确表示的整数范围在-2^53到2^53之间(不含两个端点),超过这个范围,无法精确表示这个值。Number.isSafeInteger()则是用来判断一个整数是否落在这个范围之内。
console.log(9007199254740993); // 9007199254740992
console.log(Math.pow(2, 53) === Math.pow(2, 53) + 1); // true
console.log(Number.MIN_SAFE_INTEGER); // -9007199254740991
// Math.trunc(),Math.fround()
console.log(Math.trunc(4.9)); // 4 去掉一个数的小数部分
console.log(Math.fround(1.337)); // 1.3370000123977661 返回一个数的单精度浮点数形式
// 指数运算符 **
console.log(2**3); // 8
/*
JavaScript所有数字都保存成64位浮点数,这决定了整数的精确程度只能到53个二进制位。大于这个范围的整数,JavaScript 是无法精确表示的,这使得 JavaScript 不适合进行科学和金融方面的精确计算。
现在有一个提案,引入了新的数据类型 Integer(整数),来解决这个问题。
*/
object
// 属性的简洁表示法
// ====================================================================================================
// ES6允许在对象之中直接写变量。这时,属性名为变量名, 属性值为变量的值
function f(x, y) {
return {x, y}; // 等同于{x: x, y: y }
}
console.log(f(1,2)); // { x: 1, y: 2 }
// 方法也可以简写
const o = {
method() {
return "Hello!";
}
};
console.log(o.method()); // Hello!
// 属性名表达式
// ====================================================================================================
let propKey = 'foo';
let obj = {
[propKey]: true,
['a' + 'bc']: 123
};
console.log(obj); // { foo: true, abc: 123 }
// Object.is()
// ====================================================================================================
// 用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致
console.log(Object.is('foo', 'foo')); // true
console.log(Object.is({}, {})); // false
console.log(Object.is(NaN, NaN)); // true,使用===则为false
// Object.assign()
// ====================================================================================================
// 用于对象的合并,将源对象的所有可枚举属性,复制到目标对象,对于同名属性,后面的值会覆盖前面的
const target = { a: 1 };
const source1 = { b: 2 };
const source2 = { c: 3 };
const source3 = { c: 4};
const source4 = true; // 无可枚举属性
Object.assign(target, source1, source2, source3, source4);
console.log(target); // {a:1, b:2, c:4}
// Object.assign方法实行的是浅拷贝,而不是深拷贝
const obj1 = {a: {b: 1}};
const obj2 = Object.assign({}, obj1);
obj1.a.b = 2;
console.log(obj2.a.b); // 2
// Object.getOwnPropertyDescriptor
// ====================================================================================================
let obj = { foo: 123 };
console.log(Object.getOwnPropertyDescriptor(obj, 'foo'));
/*
{
value: 123,
writable: true,
enumerable: true,
configurable: true
}
*/
/* enumerable属性用于标识属性是否可枚举。如下操作会忽略不可枚举的属性:
* for ... in
* Object.keys()
* JSON.stringify()
* Object.assign()
*/
// Object.getOwnPropertyDescriptors()返回指定对象所有自身属性(非继承属性)的描述对象。
// 属性的遍历
// ====================================================================================================
// 略。
// Object.setPrototypeOf()
// ====================================================================================================
// 作用与设置__proto__属性相同,用来设置一个对象的prototype对象。
let proto = {};
let obj = { x:10 };
Object.setPrototypeOf(obj, proto);
proto.y = 20;
proto.z = 40;
console.log(obj.x); // 10
console.log(obj.y); // 20
console.log(obj.z); // 40
// Object.setPrototypeOf()用于读取一个对象的原型对象。
// Object.keys(),Object.values(),Object.entries()
// ====================================================================================================
// 略。
// 对象的扩展运算符
// ====================================================================================================
// 结构运算符、扩展运算符,略。
promise
// 基本用法
// ====================================================================================================
// Promise对象就是一个保存着某个未来才会结束的事件的结果
// Promise构造函数中的参数resolve和reject是两个函数,由JavaScript引擎提供,不用自己部署
// Promise新建后会立即执行
let promise = new Promise(function(resolve, reject) {
// ...异步操作等
console.log('Promise');
resolve();
});
// then方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行
// then方法的作用是为Promise实例添加状态改变时的回调函数(第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数)
promise.then(function() {
console.log('resolved.');
});
console.log('Hi!');
/*
Promise
Hi!
resolved.
*/
// Promise.prototype.catch()
// ====================================================================================================
// Promise.prototype.catch方法是.then(null, rejection)的别名。
// Promise.all()
// ====================================================================================================
// Promise.all()用于将多个Promise实例(如果传入的对象不是Promise实例,则会通过调用Promise.resolve方法将其转换为Promise实例后再做进一步处理),包装成一个新的Promise实例
var promises = [2, 3, 5, 7, 11, 13].map(function (id) {
return id;
});
Promise.all(promises).then(function (posts) {
// ...
console.log(posts); // [ 2, 3, 5, 7, 11, 13 ]
}).catch(function(reason){
// ...
});
// Promise.resolve()
// ====================================================================================================
// 将现有对象转为Promise对象
/*
Promise.resolve('foo')
等价于
new Promise(resolve => resolve('foo'))
*/
set
// 基本用法
// ====================================================================================================
const s1 = new Set();
[1,2,2,3,4,4,4,5].forEach(x => s1.add(x));
for(let i of s1){
console.log(i); // 1 2 3 4 5
}
// 可用于数组去重
const s2 = new Set([1,2,2,3,4,'4',4,5,{},{},NaN,NaN]);
console.log([...s2]); // [ 1, 2, 3, 4, '4', 5, {}, {}, NaN ]
// add(),delete(),has(),clear()
// ====================================================================================================
const s3 = new Set();
s3.add(1).add(1).add(2);
console.log(s3.size); // 2
console.log(s3.has(1)); // true
console.log(s3.has(2)); // true
s3.delete(1);
console.log(s3.has(1)); // false
// keys(),values(),entries(),forEach()
// ====================================================================================================
let s4 = new Set(['red', 'green', 'blue']);
for (let item of s4.entries()) {
console.log(item);
}
// [ 'red', 'red' ]
// [ 'green', 'green' ]
// [ 'blue', 'blue' ]
// 交、并、差运算
// ====================================================================================================
let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);
// 并集
let union = new Set([...a, ...b]);
console.log(union);
// Set {1, 2, 3, 4}
// 交集
let intersect = new Set([...a].filter(x => b.has(x)));
console.log(intersect);
// set {2, 3}
// 差集
let difference = new Set([...a].filter(x => !b.has(x)));
console.log(difference);
// Set {1}
// WeakSet
// ====================================================================================================
/*
关键点:
1. WeakSet中只能存放对象;
2. WeakSet中的对象都是弱引用,即垃圾回收机制不考虑WeakSet对该对象的引用;
3. WeakSet没有size属性,没有办法遍历它的成员;(因为成员都是弱引用,随时可能消失)
详略。
*/
string
// includes(), startsWith(), endsWith()
// ====================================================================================================
let s = 'Hello world!';
console.log(s.startsWith('Hello')) // true
console.log(s.endsWith('!')) // true
console.log(s.includes('o')) // true
console.log(s.startsWith('world', 6)) // true
console.log(s.endsWith('Hello', 5)) // true
console.log(s.includes('Hello', 6)) // false
// repeat()
// ====================================================================================================
console.log('hello'.repeat(2)) // "hellohello"
// 模板字符串
// ====================================================================================================
let basket = {
count:2,
onSale:"item"
};
console.log(`
There are <b>${basket.count}</b> items
in your basket, <em>${basket.onSale +"-"+ basket.count}</em>
are on sale!
`.trim());