小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
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()
无法正确拷贝 get
和 set
属性,而我们可以使用 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.values
和 Object.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
点赞支持、手留余香、与有荣焉,动动你发财的小手哟,感谢各位大佬能留下您的足迹。
往期精彩推荐
Vue 虚拟 DOM 搞不懂?这篇文章帮你彻底搞定虚拟 DOM
Git 相关推荐
面试相关推荐
更多精彩详见:个人主页