woojean的博客 模仿、熟练、超越

ES6基础练习

2017-10-09

下载代码: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());

文章目录