【建议点赞收藏】ES系列总结(二)

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

1. Object.getOwnPropertyDescriptors

1.1 简介

ES5的 Object.getOwnPropertyDescriptor 方法会返回某个对象属性的描述对象(descriptor),ES2017引入了 Object.getOwnPropertyDescriptors 方法,返回制定对象的所有自身属性(非继承属性)的描述对象。

使用:

const obj = {
  name: '小_Battle',
  get test() {
    return '掘金'
  }
}
Object.getOwnPropertyDescriptors(obj)
// { name:
//    { value: '小_Battle',
//      writable: true,
//      enumerable: true,
//      configurable: true },
//   test:
//    { get: [Function: get test],
//      set: undefined,
//      enumerable: true,
//      configurable: true } }
// 该方法的实现就基于了ES5的 Object.getOwnPropertyDescriptor()

const getOwnPropertyDescriptors = (obj) => {
  const res = {}
  for (let key of Reflect.ownKeys(obj)) {
    res[key] = Object.getOwnPropertyDescriptor(obj, key)
  }
  return res
}

引入该方法的目的,主要是为了解决在对象浅拷贝时, Object.assign() 无法正确拷贝 getset 属性,而我们可以使用 Object.defineProperties() 配合 Object.getOwnPropertyDescriptor 来实现正确的浅拷贝。

const source = {
  name: '小_Battle',
  set test(val) {
    console.log(val)
  }
}
const res = {}
Object.defineProperties(res, Object.getOwnPropertyDescriptors(source))
Object.getOwnPropertyDescriptors(res)
// { name:
//    { value: '小_Battle',
//      writable: true,
//      enumerable: true,
//      configurable: true },
//   test:
//    { get: [Function: get test],
//      set: undefined,
//      enumerable: true,
//      configurable: true } }
/**
可用于 创建子类
创建子类的典型方法是定义子类,将其原型设置为超类的实例,然后在该实例上定义属性。这么写很不优雅,特别是对于 getters 和 setter 而言。可以下面方式设置原型:
*/

function superclass() {}
superclass.prototype = {
  // 在这里定义方法和属性
}
function subclass() {}
subclass.prototype = Object.create(
  superclass.prototype,
  Object.getOwnPropertyDescriptors({
    // 在这里定义方法和属性
  }),
)

2. Object.values/Object.entries

2.1 简介

ES2017新增了两个新的函数, 分别是 Object.valuesObject.entries

Object.values 方法返回一个给定对象自身的所有可枚举属性值的数组

Object.entries 方法返回一个给定对象自身可枚举属性的键值对数组 两方法在遍历时,若属性的 key 是数值,则遍历的顺序是按照数值从小到大顺序。

2.2 Object.values

const obj = { name: '_Battle', address: 'juejin' }

Object.values(obj) // ['_Battle', 'juejin']

当属性key为数字

const obj = { 2: '_Battle', 1: 'juejin' }
Object.values(obj) // ['_Battle', 'juejin']

只返回对象本身的可遍历属性, 若属性值是一个对象,则需要该对象开启 enumerable 属性, 否则不会返回该属性.

const obj = Object.create({}, {name: { value: 1 }})
Object.values(obj) // []
 
const obj = Object.create({}, {name: { value: 1, enumerable: true }})
Object.values(obj) // [1]

若属性值Symbol,Object.values 会过滤属性Key为 Symbol 值的属性

Object.values({ [Symbol()]:123, name: '_Battle' }) // '_Battle'

若参数非对象,例如为数值或布尔类型,Object.values 不会为实例添加非继承的属性,所以这时会返回空数组 若参数是一个字符串,则会返回各个字符组成的一个数组

Object.values('_Battle')
// ['_', 'B', 'a', 't', 't', 'l', 'e']

2.3 Object.entries

Object.entries() , 返回一个可迭代对象 遍历对象的属性

const obj = { a: 5, b: 7, c: 9 }
for (const [key, value] of Object.entries(obj)) {
  console.log('key', key, 'value', value)
}
// key: a, value: 5
// key: b, value: 7
// key: c, value: 9
// 将Object转Map结构
new Map() // 构造函数接受一个可迭代的 entries。借助 Object.entries 方法你可以很容易的将 Object 转换为 Map:

var obj = { foo: 'bar', baz: 42 }
var map = new Map(Object.entries(obj))
 
console.log(map)
// Map { foo: "bar", baz: 42 }

3. String padStart() & padEnd()

3.1 简介

在String对象中,ES2017新增两个新的函数,字符串补全长度功能。如果字符串不够指定长度,会在头部或尾部不全。padStart() 用于头部补全,padEnd() 用于尾部补全。

语法

str,padStart(targetLength [, String])
str,padEnd(targetLength [, String])
/**
参数
(1) targetLength:当前字符串需要填充到的目标长度,如果此数值小于等于字符串长度,则返回字符串本身。否则根据超出的长度做String左右填充。
(2) padString: 可选,用于填充的字符,默认值为 " "
*/

使用

// 若 targetLength 小于等于字符串长度:
'xxx'.padStart(2, 'ab') // 'xxx'
'xxx'.padEnd(2, 'cd') // 'xxx'
// 若 targetLength 大于字符串长度:

'abc'.padStart(10, '0123456789')
// '0123456abc'
// 如果省略第二个参数,默认使用空格补全长度。

'x'.padStart(4) // '   x'
'x'.padEnd(4) // 'x   '

4. Trailling-function-commas

函数参数列表允许逗号结尾

4.1简介

ES2017允许函数的最后一个参数有尾逗号(trailling comma),此前,函数定义和调用的时候,都不允许最后一个参数出现逗号。

语法

function test(
  param1,
  param2,
)

5 Shared Memory and Atomics

5.1 简介

Javascript 是单线程,Web Worker 引入了多线程,主线程用于和用户互动,Worker 线程用于承担计算任务。每个线程的数据都是隔离的,通过 postMessage 通信。 主线程

const w = new Worker('worker.js')
w.postMessage('hello')
w.onmessage = function (res) {
  console.log(res.data) 
}
// worker.js, 和主线程之间通过通过 postMessage() 通信。

onmessage = function (res) {
  console.log(res.data)
  postMessage('hello')
}

5.2 SharedArrayBuffer 对象

介绍:线程之间的数据交换可以是各种格式,而数据的交换方式是复制机制。即一个进程将想要分享的数据复制一份,通过 postMessage 方法发送给另一个进程。如果数据量较大,则通信的效率会比较低,这时可以考虑共享内存的方式来提高效率。

SharedArrayBuffer 对象用来表示一个通用的,固定长度的原始二进制数据缓冲区,类似 ArrayBuffer 对象,但前者可以实现数据共享。

// 我们可以在主线程中创建共享内存对象

// 主线程

const w = new Worker('worker.js')
// 新建1KB共享内存
const shareBuffer = new SharedArrayBuffer(1024)
// 通过postMessage通信,共享内存地址
w.postMessage(shareBuffer)
// 在共享内存上建立视图,供数据写入
const sharedArray = new Int32Array(sharedBuffer)
Worker.js

onmessage = function (res) {
  // 访问共享内存
  const sharedBuffer = res.data
  // 访问可读写区域
  const sharedArray = new Int32Array(sharedBuffer)
  // 这时我们的worker线程可以对共享可读写区域进行读写操作
  sharedArray[0] = 100
  console.log(sharedArray) // Int32Array(256) [100, 0, ...., 0]
}

5.3 Atomics 对象实现原子操作

介绍:多线程中共享内存,需要注意的一个重要问题就是:防止两个线程同时修改同一个内存地址;或者说:当一个线程修改 SharedArray 后,需要同步给其他线程。 ES2017的 SharedArrayBuffer api提供 Atomics 对象,保证所有共享内存的操作都是“原子性”,并且保证每次操作在所有线程内同步。

原子性:多个共享内存的线程能够同时在同一位置上读写数据。原子操作会确保正在读写的数据是符合预期的,即下一个原子操作会在上一个操作结束后执行。

// 语法:
Atomics.store(): 用于向共享内存中写入数据,并返回该值;
Atomics.load():用于从共享内存中取出数据。
Atomics.exchange():用于从共享内存中修改数据。

Atomics.store(typedArray, index, value) // typedArray是指定类型的 **shared** 数组
Atomics.load(typedArray, index) // 取出对应位置的数据
使用,进行读、写、修改等原子操作

const buffer = new SharedArrayBuffer(1024)
const array = new Unit8Array(buffer)
Atomics.store(array, 0, 12) // 12
Atomics.load(array, 0) // 12
Atomics.exchange(array, 0, 11) // 11

5.4 用 Atomics.wait 和 Atomics.notify 来给内存加锁(让其他线程休眠)

Worker.js

self.addEventListener(
  'message',
  (event) => {
     const sharedArray = new Int32Array(event.data) // 读取共享内存
     const expectIndex = 0, expectValue = 10
     Atomics.wait(sharedArray, expectIndex, expectValue) // wait方法:当sharedArray[expectIndex] === expectValue, 则Worker.js开始休眠,等待唤醒。
     console.log(Atomics.load(sharedArray, index))
   }
)
// 主线程
const shareBuffer = new SharedArrayBuffer(1024)
const sharedArray = new Int32Array(sharedBuffer)
Atomics.store(sharedArray, 0, 100) // 主线程写数据原子操作
Atomics.notify(sharedArray, 0, 1) // notify方法:唤醒sharedArray内存上0号位置的一个线程
// 注意:浏览器主线程不适合设置休眠,这会导致用户失去响应。而且实际上,浏览器主线程会拒绝 Atomics.wait

点赞支持、手留余香、与有荣焉,动动你发财的小手哟,感谢各位大佬能留下您的足迹。

11.png

往期精彩推荐

【建议点赞收藏】ES系列总结(一)

前端常用的几种加密方法

canvas 爬坑路【方法篇】

不懂 seo 优化?一篇文章帮你了解如何去做 seo 优化

canvas 爬坑路【属性篇】

【实战篇】微信小程序开发指南和优化实践

聊一聊移动端适配

前端性能优化实战

聊聊让人头疼的正则表达式

获取文件blob流地址实现下载功能

Vue 虚拟 DOM 搞不懂?这篇文章帮你彻底搞定虚拟 DOM

Git 相关推荐

通俗易懂的 Git 入门

git 实现自动推送

我在工作中是如何使用 git 的

面试相关推荐

前端万字面经——基础篇

前端万字面积——进阶篇

更多精彩详见:个人主页