Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ES6 基础概述 #36

Open
Checkson opened this issue Jun 5, 2019 · 0 comments
Open

ES6 基础概述 #36

Checkson opened this issue Jun 5, 2019 · 0 comments

Comments

@Checkson
Copy link
Owner

Checkson commented Jun 5, 2019

1. let 和 const 命令

在ES6之前,JavaScript 声明变量只能用 var 这个关键字;而在ES6中,则引入了其他两个关键字 letconst,那么,它们俩,到底与 var 有哪些异同呢?请看下表:

名称 作用 是否存在变量提升 是否存在块级作用域 是否能重复声明
var 变量声明
let 变量声明 不能
const 常量声明 不能

2. 变量的解构赋值

ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。

数据类型 常见解构形式 描述
数组 let [a, b, c] = [1, 2, 3]; 按照对应位置,对变量赋值
对象 let { foo, bar } = { foo: 'aaa', bar: 'bbb' }; 变量必须与属性同名
字符串 const [a, b, c, d, e] = 'hello'; 字符串被转成类数组对象
数值 let {toString: s} = 123; 解构前转为对象
布尔值 let {toString: s} = true; 解构前转为对象
函数参数 let add = ([x, y]) => x + y; 类似数组解构
函数参数 let sub = ({x, y}) => x - y; 类似对象解构

3. 字符串扩展

3.1 模板字符串

模板字符串(template string)是增强版的字符串,用反引号(`)标识。

// es5
var name = 'Checkson';
var str = 'Hello ' + name + '!';

// es6
const name = 'Checkson';
const str = `Hello ${Checkson}!`;

3.2 includes() 实例方法

返回布尔值,表示是否找到了参数字符串(接受第二参数,代表开始搜索下标)。

// es5
var str = 'This is just test!';
if (str.indexOf('is') > -1) {
   // do something...
}

// es6
const str = 'This is just test!';
if (str.includes('is')) {
   // do something...
}

3.3 startsWith(), endsWith() 实例方法

两者都返回布尔值,前者表示参数字符串是否在原字符串的头部,后者表示参数字符串是否在原字符串的尾部(都接受第二参数,代表开始搜索下标)。

// es5
var str = 'Learn Once, Write Anywhere';
if (/^Learn/.test(str) || /Anywhere$/.test(str)) {
    // do something...
}

// es6
const str = 'Learn Once, Write Anywhere';
if (str.startsWith('Learn') || str.endsWith('Anywhere')) {
    // do something...
}

3.4 repeat() 实例方法

repeat方法返回一个新字符串,表示将原字符串重复n次,n为非负整数。

// es5
var str = '';
for (var i = 0; i < 6; i++) {
    str += 'template';
}

// es6
var str = 'template'.repeat(6);

3.5 padStart(), padEnd() 实例方法

ES2017 新增了 padStart()padEnd() 实例方法用来分别为字符串首、尾补全指定的字符。

// padStart
// es5
function prevZero (num) {
    return ('0' + num).substr(-2);
}

// es6
function prevZero (num) {
    return ('' + num).padStart(2, '0');
}

// padEnd 同理

3.6 trimStart(), trimEnd() 实例方法

ES2019 新增了 trimStart()trimEnd() 实例方法。它们的行为与trim()一致,前者消除字符串头部的空格,后者消除尾部的空格。

// trimStart
// es5
function trimStartSpace (str) {
    var res = '', isFirst = true ;
    for (var i = 0, len = str.length; i < len; i++) {
        if (isFirst && str[i] === ' ') {
            continue;
        }
        isFirst && (isFirst = !isFirst);
        res += str[i];
    }
    return res;
}

// es6
function trimStartSpace (str) {
    return str.trimStart();
}

// trimEnd 同理

4. 正则的扩展

字符串对象共有 4 个方法,可以使用正则表达式:match()replace()search()split()。ES6 将这 4 个方法,在语言内部全部调用RegExp的实例方法,从而做到所有与正则相关的方法,全都定义在RegExp对象上。

5. 数值的扩展

5.1 二进制和八进制表示法

ES6 提供了二进制和八进制数值的新的写法,分别用前缀0b(或0B)和0o(或0O)表示。

// 二进制
0b1000 === 8 // true
// 八进制
0o10 === 8   // true
// 转成十进制
Number(0b1000); // 8
Number(0o10);   // 8

5.2 Number.isFinite(), Number.isNaN()

ES6在Number对象上,新提供了Number.isFinite()Number.isNaN()两个方法。前者检查传入值是否有限,后者检查传入值是否为NaN。

// es5
isFinite(25) // true
isFinite("25") // true
isFinite(true) // true
// es6
Number.isFinite(25) // true
Number.isFinite("25") // false
Number.isFinite(true) // false

// es5
isNaN(NaN) // true
isNaN("NaN") // true
// es6
Number.isNaN(NaN) // true
Number.isNaN("NaN") // false
Number.isNaN(1) // false

它们与传统的全局方法isFinite()isNaN()的区别在于,传统方法先调用Number()将非数值的值转为数值,再进行判断,而这两个新方法只对数值有效,Number.isFinite()对于非数值一律返回false, Number.isNaN()只有对于NaN才返回true,非NaN一律返回false

5.3 Number.parseInt(), Number.parseFloat()

ES6 将全局方法parseInt()和parseFloat(),移植到Number对象上面,行为完全保持不变。这样做的目的,是逐步减少全局性方法,使得语言逐步模块化。

Number.parseInt === parseInt // true
Number.parseFloat === parseFloat // true

5.4 指数运算符

ES2016 新增了一个指数运算符(**)。

// es5
Math.pow(2, 2); // 4
Math.pow(2, 3); // 8

// es6
2 ** 2 // 4
2 ** 3 // 8

6. 函数的扩展

6.1 函数参数的默认值

ES6 之前,不能直接为函数的参数指定默认值,只能采用变通的方法。

// es5
function foo (x, y) {
    y = y || 'World';
    return x + y;
}

// es6
function foo (x, y = 'World') {
    return x + y;
}

6.2 解构 + 函数参数默认值

// es5
function foo (obj) {
    obj.y = obj.y || 2;
    return obj.x + obj.y;
}

// es6
function foo ({x, y = 2}) {
    return x + y;
}

6.3 rest 参数

ES6 引入 rest 参数(形式为...变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。

// es5
function add () {
    var args = [].slice.call(arguments);
    var sum = 0;
    args.forEach(function (val) {
         sum += val;
    });
    return sum;
}

// es6
function add(...args) {
  let sum = 0;
  for (let val of args) {
    sum += val;
  }
  return sum;
}

6.4 箭头函数

ES6 允许使用“箭头”(=>)定义函数。

// es5
var foo = function () {
    return false;
}

// es6
const foo = () => false;

注意: 对于箭头函数,函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象;不可以当作构造函数(不能被new);不可以使用arguments对象;不可以使用yield命令。

6.5 尾递归

函数调用自身,称为递归。如果尾调用自身,就称为尾递归。

// 非尾递归 - 容易栈溢出
function factorial (n) {
  if (n === 1) return 1;
  return n * factorial(n - 1);
}

// 尾递归
function factorial (n, total) {
    if (n === 1) return total;
    return factorial(n - 1, total * n);
}

7. 数组的扩展

7.1 Array.from()

Array.from方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)。

let arrayLike = {
    '0': 'a',
    '1': 'b',
    '2': 'c',
    length: 3
};

// es5
var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']

// es6
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']

7.2 Array.of()

Array.of方法用于将一组值,转换为数组。

//  es5
var arr = [];
[].push.call(arr, 1, 2, 3);

// es6
const arr = Array.of(1, 2, 3);

7.3 find() 实例方法

数组实例的find方法,用于找出第一个符合条件的数组成员。若果找到符合条件的成员,则返回第一个满足条件的成员;如果找不到满足条件的成员,返回undefined

var arr = [1, 2, 3, 4],  res = 'undefine';

// es5
for (var i = 0; i < arr.length; i++) {
      if (arr[i] > 2) {
           res = arr[i]; // 3
           break;
      }
}

// es6
res = arr.find(item => item > 2); // 3

7.4 findIndex() 实例方法

findIndex 方法与 indexOf 方法作用类似,但是findIndex能识别数组是否存在NaN

// es5
[1, NaN, 2, NaN].indexOf(NaN); // -1

// es6
[1, NaN, 2, NaN].findIndex(item => isNaN(item)); // 1

7.5 fill() 实例方法

fill方法使用给定值,填充一个数组。

// es5
new Array(6, 6, 6); // [6, 6, 6]

// es6
new Array(3).fill(6); // [6, 6, 6]

7.6 includes() 实例方法

ES2016引入Array.prototype.includes方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes方法类似。

// es5
[1, 2, 3].indexOf(2) > -1 // true 

// es6
[1, 2, 3].includes(2); // true
[1, 2, NaN].includes(NaN); // true

7.7 flat 实例方法

数组的成员有时还是数组,Array.prototype.flat()用于将嵌套的数组“拉平”,变成一维的数组。该方法返回一个新数组,对原数据没有影响。

// es5
function flatten(arr) {
    return arr.reduce((a, b) => {
        return a.concat(Array.isArray(b) ? flatten(b) : b);
    }, []);
};
flatten([1, 2, [3, 4]]); // [1, 2, 3, 4]

// es6
[1, 2, [3, 4]].flat(); // [1, 2, 3, 4]

8. 对象的扩展

8.1 属性的简洁表示法

ES6 允许直接写入变量和函数,作为对象的属性和方法。

// es5
function foo (x, y) {
    return { x: x, y: x };
}

// es6
function foo (x, y) {
    return {x, y};
}

8.2 属性名表达式

ES6 允许字面量定义对象时,表达式作为对象的属性名,即把表达式放在方括号内。

// es5
var obj = {
     foo: 1
};
obj['b' + 'ar'] = 2;

// es6
var obj = {
    foo: 1,
    ['b' + 'ar']: 2
}

8.3 属性遍历方法

ES6 一共有 5 种方法可以遍历对象的属性。

(1)for...in

for...in循环遍历对象自身的和继承的可枚举属性(不含 Symbol 属性)。

(2)Object.keys(obj)

Object.keys返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)的键名。

(3)Object.getOwnPropertyNames(obj)

Object.getOwnPropertyNames返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)的键名。

(4)Object.getOwnPropertySymbols(obj)

Object.getOwnPropertySymbols返回一个数组,包含对象自身的所有 Symbol 属性的键名。

(5)Reflect.ownKeys(obj)

Reflect.ownKeys返回一个数组,包含对象自身的所有键名,不管键名是 Symbol 或字符串,也不管是否可枚举。

8.4 扩展运算符

对象的扩展运算符(...)用于取出参数对象的所有可遍历属性,拷贝到当前对象之中。

var foo = { a: 1 };
var bar = { b: 2, c: 3 };

// es5
var baz = {};
for (var k in foo) baz[k] = foo[k];
for (var k in bar) baz[k] = bar[k];

// es6
var baz = {
    ...foo,
    ...bar
}

8.5 Object.is()

Object.is()方法用来判断两值是否一样

// es5
-0 === +0 // true
NaN === NaN // false

// es6
Object.is(-0, +0); // false
Object.is(NaN, NaN); // true;

8.6 Object.assign()

Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。

var foo = { a: 1 };
var bar = { b: 2 };
var baz = { c: 3 };

// es5 - 对象合并
for (var prop in bar) foo[prop] = bar[prop];
for (var prop in baz) foo[prop] = baz[prop];
console.log(foo);  // { a: 1, b: 2, c: 3 }

// es6 - 对象合并
Object.assign(foo, bar, baz); 
console.log(foo); // { a: 1, b: 2, c: 3};

8.7 Object.values()

Object.values方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值。

var obj = { foo: 'bar', baz: 42 };
// es5
var arr = [];
for (var prop in obj) arr.push(obj[prop]); // ['bar', 42]

// es6
Object.values(obj); // ['bar', 42]

8.8 Object. entries()

Object.entries()方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组。

var obj = { foo: 'bar', baz: 42 };
// es5
var arr = [];
for (var prop in obj) arr.push([prop, obj[prop]]); // [["foo", "bar"], ["baz", 42]]

// es6
Object.entries(obj); // [["foo", "bar"], ["baz", 42]]

9. Symbol

ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值,可以保证不会与其他属性名产生冲突。

var x = Symbol();
var y = Symbol();

x === y // false
typeof x; // "Symbol"

9.1 可以为Symbol添加描述

const sym = Symbol('foo');
sym.description // foo

9.2 作为属性名的 Symbol

let mySymbol = Symbol();

// 第一种写法
let a = {};
a[mySymbol] = 'Hello!';

// 第二种写法
let a = {
  [mySymbol]: 'Hello!'
};

// 第三种写法
let a = {};
Object.defineProperty(a, mySymbol, { value: 'Hello!' });

9.3 Symbol.for

该方法可以重用同一个 Symbol 值。

const s1 = Symbol.for('foo');
const s2 = Symbol.for('foo');

s1 === s2 // true

9.4 Symbol.keyFor

该方法返回一个已登记的 Symbol 类型值的key

let s1 = Symbol.for("foo");
Symbol.keyFor(s1) // "foo"

10. Set 和 Map 数据解构

10.1 Set 定义

ES6 提供了新的数据结构 Set (集合)。它类似于数组,但是成员的值都是唯一的,没有重复的值。

Set 本身是一个构造函数,它可以接受一个数组(或者具有 iterable 接口的其他数据结构)作为参数,用来初始化。下面罗列几种常见的创建形式:

// 方式一
const set = new Set();
[2, 3, 3, 4, 5, 5, 6].forEach(x => set.add(x));
console.log(set); // Set(5) {2, 3, 4, 5, 6}

// 方式二
const set = new Set([2, 3, 3, 4, 5, 5, 6]);
console.log(set); // Set(5) {2, 3, 4, 5, 6}

// 方式三
const set = new Set(document.querySelectorAll('div'));

10.2 Set 实例的属性和方法

  • Set 实例的属性:
    • Set.prototype.constructor: 构造函数,默认是就是 Set 函数。
    • Set.prototype.size: 返回 Set 实例的成员总数。
  • Set 实例的方法:
    • add(value):添加某个值,返回 Set 结构本身。
    • delete(value): 删除某个值,返回一个布尔值,表示删除是否成功。
    • has(value): 返回一个布尔值,表示该值是否为 Set 的成员。
    • clear():清除所有成员,没有返回值。
var set = new Set();

set.add(1).add(2).add(2);

set.size // 2

set.has(1); // true
set.has(2); // true
set.has('2'); // false
set.has(3); // false

set.delete(2); // true
set.has(2); // false

set.clear();
set.size: // 0

10.3 Set 遍历

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"]

10.4 Set 应用

  • 数组去重
[...new Set([1, 1, 2, 3, 3, 4, 4, 4, 5])]; // [1, 2, 3, 4, 5]
  • 字符串去重
[...new Set('abababcd')].join(''); // abcd
  • 实现并集(Union)、交集(Intersect)和差集(Difference)
let a = new Set([1, 2, 3]);
let b = new Set([2, 3, 4]);

// 并集
let union = new Set([...a, ...b]);
// Set {1, 2, 3}

// 交集
let intersect = new Set([...a].filter(x => b.has(x)));
// Set {2, 3}

// 差集
let difference = new Set([...a].filter(x => !b.has(x)));
// Set {1}

10.5 Map 定义

JavaScript 的对象(Object),本质上是键值对的集合(Hash 结构),但是传统上只能用字符串当作键。这给它的使用带来了很大的限制。

var a = { foo: 1 }, b = {};
b[a] = 1;

console.log(b); // {[object Object]: 1} 自动将对象转化为字符串

为了解决这个问题,ES6 提供了 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

Map作为构造函数,也可以接受一个数组作为参数。该数组的成员是一个个表示键值对的数组。

const map = new Map([
  ['name', '张三'],
  ['title', 'Author']
]);

map.size // 2
map.has('name') // true
map.get('name') // "张三"
map.has('title') // true
map.get('title') // "Author"

10.6 Map 实例属性和方法

  • Map 属性:
    • Map.prototype.constructor: 构造函数,默认是就是 Map 函数。
    • Map.prototype.size: 返回 Map 实例的成员总数。
  • Map 操作方法:
    • set(key, value) 方法设置键名key对应的键值为value,然后返回整个 Map 结构。
    • get(key) 方法读取 key 对应的键值,如果找不到 key,返回 undefined
    • has(key) 方法返回一个布尔值,表示某个键是否在当前 Map 对象之中。
    • delete(key) 方法删除某个键,返回 true。如果删除失败,返回 false
    • clear() 方法清除所有成员。
const map = new Map();

map.set('name', 'checkson');
map.get('name'); // checkson
map.set('age', 23).set('sex', 'male'); // 链式用法

map.has('name');
map.size; // 3

map.delete('age');
map.size; // 2

map.clear();
map.size; // 0

10.7 Map遍历

Map 结构原生提供三个遍历器生成函数和一个遍历方法。

  • keys():返回键名的遍历器。
  • values():返回键值的遍历器。
  • entries():返回所有成员的遍历器。
  • forEach():遍历 Map 的所有成员。

需要特别注意的是,Map 的遍历顺序就是插入顺序。

const map = new Map([
  ['F', 'no'],
  ['T',  'yes'],
]);

for (let key of map.keys()) {
  console.log(key);
}
// "F"
// "T"

for (let value of map.values()) {
  console.log(value);
}
// "no"
// "yes"

for (let item of map.entries()) {
  console.log(item[0], item[1]);
}
// "F" "no"
// "T" "yes"

// 或者
for (let [key, value] of map.entries()) {
  console.log(key, value);
}
// "F" "no"
// "T" "yes"

// 等同于使用map.entries()
for (let [key, value] of map) {
  console.log(key, value);
}
// "F" "no"
// "T" "yes"

参考图书

《ECMAScript 6 入门》

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant