温故知新之ES6(二)

花名提莫 2018-09-27 16:37:11 ⋅ 760 阅读

目录

1. 数组的扩展

1 扩展运算符

扩展运算符(spread)是三个点( … )。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。【展开数组】

扩展运算符内部调用的是数据结构的 Iterator 接口,因此只要具有 Iterator 接口的对象,都可以使用扩展运算符

console.log(...[123])
// 1 2 3

console.log(1, ...[234], 5)
// 1 2 3 4 5

求数组最大值

// ES5 的写法
Math.max.apply(null, [14377])

// ES6 的写法Math.max(...[14, 3, 77])

// 等同于
Math.max(14377);
1.1 附加一个面试题
1.apply和call的区别在哪里
2.什么情况下用apply,什么情况下用call
3.apply的其他巧妙用法(一般在什么情况下可以使用apply
  1. apply:方法能劫持另外一个对象的方法,继承另外一个对象的属性.

Function.apply(obj,args)方法能接收两个参数
obj:这个对象将代替Function类里this对象
args:这个是数组,它将作为参数传给Function(args-->arguments

call:和apply的意思一样,只不过是参数列表不一样.

 Function.call(obj,[param1[,param2[,…[,paramN]]]])
obj:这个对象将代替Function类里this对象
params:这个是一个参数列表
  1. 什么情况下用apply,什么情况下用call

在给对象参数的情况下,如果参数的形式是数组的时候,比如apply示例里面传递了参数arguments,
这个参数是数组类型,并且在调用Person的时候参数的列表是对应一致的(也就是Person和Student的
参数列表前两位是一致的) 就可以采用 apply , 如果我的Person的参数列表是这样的(age,name),
而Student的参数列表是(name,age,grade),这样就可以用call来实现了,也就是直接指定
参数列表对应值的位置(Person.call(this,age,name,grade));
  1. apply的其他巧妙用法

因为Math.max 参数里面不支持Math.max([param1,param2]) 也就是数组
但是它支持Math.max(param1,param2,param3…),
所以可以根据刚才apply的那个特点来解决 var max=Math.max.apply(null,array),
这样轻易的可以得到一个数组中最大的一项(apply会将一个数组装换为一个参数接一个参数的传递给方法)
// ES5 的写法
Math.max.apply(null, [14377])
// ES6 的写法
Math.max(...[14377])
// 等同于
Math.max(14377);



// ES5的 写法
var arr1 = [012];
var arr2 = [345];
Array.prototype.push.apply(arr1, arr2);

// ES6 的写法
var arr1 = [012];
var arr2 = [345];
arr1.push(...arr2);
1.2 应用举例子
// ====  1 合并数组
// ES5
[1, 2].concat(more)
// ES6
[1, 2, ...more]

// ES6的合并数组
[...arr1, ...arr2, ...arr3]


// ==== 2 解构
const [first, ...rest] = [12345];
first // 1
rest  // [2, 3, 4, 5]

const [first, ...rest] = [];
first // undefined
rest  // []

// 如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。
const [...butLast, last] = [12345];
// 报错


// ====== 字符串
[...'hello']
// [ "h", "e", "l", "l", "o" ]
// 32位Unicode 正确
[...'x\uD83D\uDE80y'].length // 3

// ==== map
let map = new Map([
  [1, 'one'],
  [2, 'two'],
  [3, 'three'],
]);

let arr = [...map.keys()]; // [1, 2, 3]

2. Array.from()

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

let arrayLike = {
    '0''a',
    '1''b',
    '2''c',
    length3
};
// ES5的写法
var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']
// ES6的写法
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']

// 
Array.from('hello')
// ['h', 'e', 'l', 'l', 'o']


let namesSet = new Set(['a''b'])
Array.from(namesSet) // ['a', 'b']

3. Array.of() 转换为数组。

Array.of 方法用于将一组值,转换为数组。基本可以代替new Array() 、 Array();

Array.of(3118// [3,11,8]
Array.of(3// [3]
Array.of(3).length // 1

Array() // []
Array(3// [, , ,]
Array(3118// [3, 11, 8]

4. 数组实例的 copyWithin()

数组实例的 copyWithin 方法,在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。也就是说,使用这个方法,会修改当前数组。

Array.prototype.copyWithin(target, start = 0, end = this.length)
  1. target(必需):从该位置开始替换数据。

  2. start(可选):从该位置开始读取数据,默认为0。如果为负值,表示倒数。

  3. end(可选):到该位置前停止读取数据,默认等于数组长度。如果为负值,表示倒数。

// 将3号位复制到0号位
[1, 2, 3, 4, 5].copyWithin(034)
// [4, 2, 3, 4, 5]


[1, 2, 3, 4, 5].copyWithin(03)
// [4, 5, 3, 4, 5]

5. 数组实例的 find() 和 findIndex()

数组实例的 find 方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为 true 的成员,然后返回该成员。如果没有符合条件的成员,则返回 undefined 。

[14-510].find((n) => n < 0)
// -5

// 参数:当前的值、当前的位置和原数组。
[151015].find(function(value, index, arr{
  return value > 9;
}) // 10

findIndex返回第一个符合条件的数组成员的位置

[1, 5, 10, 15].findIndex(function(value, index, arr) {
  return value > 9;
}) // 2

6.数组实例的fill()

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

['a''b''c'].fill(7)
// [7, 7, 7]

new Array(3).fill(7)
// [7, 7, 7]

7. 数组实例的 entries(),keys() 和 values()

遍历数组可以用 for…of 循环进行遍历,唯一的区别是 keys() 是对键名的遍历、 values() 是对键值的遍历, entries() 是对键值对的遍历。

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"

8. 数组实例的 includes()

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

[1, 2, 3].includes(2)     // true
[1, 2, 3].includes(4)     // false
[1, 2, NaN].includes(NaN) // true

第二个参数表示搜索的起始位置,默认为 0 。

indexOf 方法有两个缺点:

1. 不够语义化,它的含义是找到参数值的第一个出现位置,所以要去比较是否不等于 -1 ,表达起来不够直观。
2. 它内部使用严格相等运算符( === )进行判断,这会导致对 NaN 的误判。

例子

[NaN].indexOf(NaN)
// -1

[NaN].includes(NaN)
//true

9. 组的空位

数组的空位指,数组的某一个位置没有任何值。注意,空位不是 undefined ,一个位置的值等于 undefined ,依然是有值的。空位是没有任何值。

ES6 则是明确将空位转为 undefined 。

由于空位的处理规则非常不统一,所以建议避免出现空位。

2. 对象的扩展

1 Object.assign() 对象合并

var target = { a: 1 };
var source1 = { b: 2 };
var source2 = { c: 3 };
Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}

注意点
Object.assign 方法实行的是浅拷贝,而不是深拷贝。也就是说,如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用。深拷贝可以用lodash代替,

常见用途

1. 为对象添加属性;
2. 为对象添加方法;
3. 克隆对象;Object.assign({}, origin);
4. 合并多个对象;const merge =
  (...sources) => Object.assign({}, ...sources);
5. 为属性指定默认值;

2. 属性的遍历

1. for...in 循环遍历对象自身的和继承的可枚举属性(不含 Symbol 属性)。
2. Object.keys 返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)。
3. Object.getOwnPropertyNames 返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)。
4. Object.getOwnPropertySymbols 返回一个数组,包含对象自身的所有 Symbol 属性。
5. Reflect.ownKeys 返回一个数组,包含对象自身的所有属性,不管属性名是 Symbol 或字符串,也不管是否可枚举。

说明

1. 首先遍历所有属性名为数值的属性,按照数字排序。
2. 其次遍历所有属性名为字符串的属性,按照生成时间排序。
3. 最后遍历所有属性名为 Symbol 值的属性,按照生成时间排序。

3. Object.keys(),Object.values(),Object.entries()

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

let {keys, values, entries} = Object;

let obj = { a1b2c3 };

for (let key of keys(obj)) {
  console.log(key); // 'a', 'b', 'c'
}

var obj = { foo'bar'baz42 };
Object.entries(obj)

// [ ["foo", "bar"], ["baz", 42] ]

// Object.entries 的基本用途是遍历对象的属性。
let obj = { one1two2 };
for (let [k, v] of Object.entries(obj)) {
  console.log(
    `${JSON.stringify(k)}${JSON.stringify(v)}`
  );
}
// "one": 1
// "two": 2

// Object.entries 方法的另一个用处是,将对象转为真正的 Map 结构。
var obj = { foo'bar'baz42 };

var map = new Map(Object.entries(obj));

map // Map { foo: "bar", baz: 42 }

3. 对象的扩展运算符( … )

1. 解构赋值

let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
// 1
// 2
// { a: 3, b: 4 }

2. 扩展运算符

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

let z = { a3b4 };
let n = { ...z };
// { a: 3, b: 4 }

// 等同于【只是copy了属性】
let aClone = Object.assign({}, z);

3. Null 传导运算符

引入了“Null 传导运算符”(null propagation operator) ?. ,简化上面的写法。

const firstName = message?.body?.user?.firstName || 'default';

上面代码有三个?.运算符,只要其中一个返回 null 或 undefined ,就不再往下运算,而是返回 undefined 。

“Null 传导运算符”有四种用法。

 obj?.prop  // 读取对象属性
 obj?.[expr]  // 同上
 func?.(...args)  // 函数或对象方法的调用
 new C?.(...args)  // 构造函数的调用
// 如果 a 是 null 或 undefined, 返回 undefined

// 否则返回 a.b.c().d
a?.b.c().d

// 如果 a 是 null 或 undefined,下面的语句不产生任何效果

// 否则执行 a.b = 42
a?.b = 42

// 如果 a 是 null 或 undefined,下面的语句不产生任何效果
delete a?.b

4 Symbol 概述

保证每个属性的名字都是独一无二,这样就从根本上防止属性名的冲突。这就是 ES6 引入 Symbol 的原因。

ES6 引入了一种新的原始数据类型 Symbol ,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,前六种是: undefined 、 null 、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。

// 变量 s 就是一个独一无二的值
let s = Symbol();
typeof s
// "symbol"

// 没有参数的情况
var s1 = Symbol();
var s2 = Symbol();
s1 === s2 // false

// 有参数的情况
var s1 = Symbol('foo');

var s2 = Symbol('foo');
s1 === s2 // false

s2.toString() // 'Symbol(foo)'

注意, Symbol 函数前不能使用 new 命令,否则会报错。这是因为生成的 Symbol 是一个原始类型的值,不是对象。也就是说,由于 Symbol 值不是对象,所以不能添加属性。基本上,它是一种类似于字符串的数据类型。

为了便于区分,加上名字。

var s1 = Symbol('foo');

var s2 = Symbol('bar');

s1 // Symbol(foo)
s2 // Symbol(bar)

1. 作为属性名的 Symbol

【Set】设置值

var mySymbol = Symbol();
var a = {};
a.mySymbol = 'Hello!';
a[mySymbol] // undefined
a['mySymbol'// "Hello!"

2. 取Symbol

【get】取值

2.1 Object.getOwnPropertySymbols

Symbol 作为属性名,该属性不会出现在 for…in 、 for…of 循环中,也不会被 Object.keys() 、 Object.getOwnPropertyNames() 、 JSON.stringify() 返回。但是,它也不是私有属性,有一个 Object.getOwnPropertySymbols 方法,可以获取指定对象的所有 Symbol 属性名。

var obj = {};
var a = Symbol('a');
var b = Symbol('b');

obj[a] = 'Hello';
obj[b] = 'World';

var objectSymbols = Object.getOwnPropertySymbols(obj);

objectSymbols
// [Symbol(a), Symbol(b)]
2.2 Reflect.ownKeys

Reflect.ownKeys 方法可以返回所有类型的键名,包括常规键名和 Symbol 键名。

let obj = {
  [Symbol('my_key')]: 1,
  enum2,
  nonEnum3
};

Reflect.ownKeys(obj)
//  ["enum", "nonEnum", Symbol(my_key)]

由于以 Symbol 值作为名称的属性,不会被常规方法遍历得到。我们可以利用这个特性,为对象定义一些非私有的、但又希望只用于内部的方法。

3. Symbol.for(),Symbol.keyFor()

重新使用同一个Symbol值,

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

s1 === s2 // true

Symbol.keyFor 方法返回一个已登记的 Symbol 类型值的 key 。

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

var s2 = Symbol("foo");
Symbol.keyFor(s2) // undefined

4. 实例--单例

// mod.js
const FOO_KEY = Symbol.for('foo');

function A() {
  this.foo = 'hello';
}

if (!global[FOO_KEY]) {
  global[FOO_KEY] = new A();
}

module.exports = global[FOO_KEY];

可以保证 global[FOO_KEY] 不会被无意间覆盖,但还是可以被改写。

var a = require('./mod.js');
global[Symbol.for('foo')] = 123;

如果确保不被修改,将Symbol.for("foo") 改为 Symbol("foo");

上面代码将导致其他脚本都无法引用 FOO_KEY 。但这样也有一个问题,就是如果多次执行这个脚本,每次得到的 FOO_KEY 都是不一样的。虽然Node会将脚本的执行结果缓存,一般情况下,不会多次执行同一个脚本,但是用户可以手动清除缓存,所以也不是完全可靠。

系列之上篇--温故知新之ES(二)

关注我们



全部评论: 0

    我有话说:

    温故知新ES6(三)

    温故知新ES6 数组集合和字典

    温故知新ES6()

    紧接本系列上篇

    玩转ES6(三):数据结构

    Set 是ES6提供的一种新的数据结构,它允许你存储任何类型的唯一值,而且Set中的元素是唯一的。

    玩转ES6(五):Iterator、Generator、async/await

    1. Iterator 和 for…of 循环 ES6 中有四种数据集合:数组( Array )、对象( Object )、Map 和 Set 。这样就需要一种统一的接口机制,来处理所有不同的数据

    Node模块Events模块(五)

    Node模块Events模块(五)

    Debian 10.6 发布

    Debian 10.6 已发布,这是 Debian 10 "Buster" 的第六个稳定版更新,修复了部分安全问题和 bug。 除了安全方面的更新,还有针对 OpenJDK, Firefox ESR

    「开源资讯」Gradle 6.7 发布,增量构建改进

    Gradle 6.7 已经发布。Gradle 是一个基于 Apache Ant 和 Apache Maven 概念的项目自动化构建工具,支持依赖管理和多项目,类似 Maven,但比简单轻便。它使用

    Java Web实战篇-代码

    代码美-小小的优化让你的代码Bug更少,执行效率更高

    微信小程序营销大转盘(

    第一个版本的大转盘都是用图片做的,奖品等信息都是不无法修改的,说白了就是没啥实际用途,作者我就直接用canvas撸了一个全手工绘制的大转盘分享给大家

    京东到家订单中心系统mysql到es的转化

    原文:https://www.toutiao.com/i6796507988602389006 京东到家订单中心系统业务中,无论是外部商家的订单生产,或是内部上下游系统的依赖,订单查询的调用量都非常大,造成了订单数据读多写少的情况。 我们把订单数...

    OpenSSH 8.6 发布

    OpenSSH 8.6 已发布,OpenSSH 是 100% 完整的 SSH 协议 2.0 版本的实现,并且包括 sftp 客户端和服务器支持,它用于远程登录的主要连接工具。OpenSSH

    Redis 6.2.2 发布

    Redis 6.2.2 现已发布,该版本升级迫切性程度为高。对于那些使用 ACL 和 pub/sub,CONFIG REWRITE,或遭受性能下降影响的用户来说,详见下文: 修复了

    JetLinks 物联网基础平台 1.6 RELEASE 发布

    JetLinks 开源物联网平台 JetLinks 基于Java8,Spring Boot 2.x,WebFlux,Netty,Vert.x,Reactor等开发, 是一个开箱即用,可次开发的企业

    Python 3.8.6 发布

    Python 3.8.6 发布了,它是 Python 3.8 的第六个维护版本。 3.8 系列的维护版本将每两个月定期更新一次,3.8.7 计划于 2020 年 11 月中旬发布。 随着维护版本的

    Redis 6.2.1 发布

    Redis 6.2.1 现已发布,该版本升级迫切性程度为低:修复了编译问题。具体更新内容如下: Bug 修复 修复带有已删除记录的 stream 的 sanitize-dump

    RediSearch 1.6.15 发布,高性能全文搜索引擎

    RediSearch 1.6.15 现已发布,这是1.6 版的维护版本,更新紧急程度较低。具体更新内容如下: Details: Minor enhancements: #1225

    Spring Framework 5.3.6 & 5.2.14 发布

    Spring Framework 5.3.6 和 5.2.14 现已发布,分别包含 19 项与 11 项修复和改进。 主要更新内容 5.3.6