跳到主要内容

27 篇博文 含有标签「JS」

Javascript

查看所有标签

V8的快属性|Fast properties in V8

· 阅读需 19 分钟
Hanasaki
阿巴阿巴阿巴
信息

本文翻译、修改自 Fast properties in V8,其中会删除、修改、批注部分内容,但不改变原本的意思,使阅读更加通顺。

在本篇内容中,我们将介绍 V8 内部是如何处理 JavaScript 属性的。从 JS 的角度来看,对象(Object)和 字典(Dictionary)差不多:以字符串为键,任意对象为值。不过,在进行迭代时,会对整数索引的属性和其他属性进行不同的处理。其他情况下,不同属性的行为基本相同,与是否整数索引无关。

不过,出于性能和内存方面的考虑,V8 确实依赖于几种不同的属性表示方式。在本篇文章中,我们将解释 V8 是如何在处理动态添加的属性时提供快速的属性访问。同时,了解属性的工作原理对于解释 V8 是如何做优化的,例如内联缓存,也是至关重要的。

本文将先阐述处理整数索引属性和命名属性的区别,然后,我们将展示 V8 在添加命名属性时,如何维护 HiddenClasses 以便提供一种快速识别对象形状的方法。然后,我们将继续深入介绍命名属性是如何根据使用情况进行优化的,以实现快速访问或修改。最后,我们将详细介绍 V8 如何处理整数索引属性或数组索引。

JS 递归深拷贝

· 阅读需 4 分钟
Hanasaki
阿巴阿巴阿巴

思路

从最基本的拷贝开始,一步一步处理更复杂的对象、类型。

拷贝对象字面量

先考虑基本类型和层层对象的情况,直接 for-in 遍历 + 递归拷贝

function deepClone(obj) {
// 基本类型直接返回
if (typeof obj !== 'object') return obj;
const newObj = {};
for (let key in obj) {
// 只复制自身的对象
if (Object.hasOwn(obj, key)) {
newObj[key] = deepClone(obj[key]);
}
}
return newObj;
}

防抖函数绑定 this

· 阅读需 2 分钟
Hanasaki
阿巴阿巴阿巴

为什么防抖这样写不行?

function debounce(fn, delay) {
let timeout;
return function () {
if (timeout) clearTimeout(timeout);
timeout = setTimeout(fn, delay, ...arguments);
};
}

发布订阅模式 (PubSub)

· 阅读需 2 分钟

与观察者模式类似,但发布者和观察者不直接沟通,而是通过一个发布订阅中心,通过 event 消息来间接沟通。

发布订阅中心接收来自发布者的消息,然后通知订阅了该消息的订阅者们。

https://www.toptal.com/ruby-on-rails/the-publish-subscribe-pattern-on-rails

比喻一下,有个报亭,小A来注册了“来报”事件,她希望来报时打电话告诉他来报了。小B也到报亭注册了“来报”事件,他希望来报时发短信告诉他来报了。订阅、退订和通知,这三件事都是发布订阅中心来做。

根据上述比喻,写的示例代码:

interface IPubSub {
subscribe(event: string, fn: any): void;
unSubscribe(event: string, fn: any): void;
publish(event: string, data: any): void;
}

// 订阅发布中心
class PubSub implements IPubSub {
private subs: any = {};

// 注册事件和对应处理函数
subscribe(event: string, fn: any): void {
if (this.subs[event]) {
this.subs[event].push(fn);
} else {
this.subs[event] = [fn];
}
}

unSubscribe(event: string, fn: any): void {
this.subs[event] = this.subs[event].filter((sub: any) => {
sub !== fn;
});
}

// 发布者发布消息
publish(event: string, data: any): void {
for (const fn of this.subs[event]) {
fn(data);
}
}
}

const pubcenter = new PubSub();

// 注册 data 事件,事件触发时,打电话告诉 A 报纸到了
pubcenter.subscribe('data', (data: string) => {
console.log('Calling Person A: Your newspaper arrives.');
});
// 也为 B 注册 data 事件,事件触发时,发短信告诉它报纸到了
pubcenter.subscribe('data', (data: string) => {
console.log('Texting Person B: Your newspaper arrives.');
});

pubcenter.publish('data', 'Daaaaaaaaaata.');

输出:

Calling Person A: Your newspaper arrives.
Texting Person B: Your newspaper arrives.

观察者模式 (Observer)

· 阅读需 2 分钟

观察者模式是一种行为设计模式,允许你定义一种订阅机制,可在对象事件发生时通知多个 “观察” 该对象的其他对象。

  • 开闭原则,你无需修改发布者代码就能引入新的订阅者类(如果是发布者接口则可轻松引入发布者类)。
  • 你可以在运行时建立对象之间的联系。
  • 订阅者的通知顺序是随机的。

示例

具体观察者 ConcreteObserverA 实现 Observer 接口,ConcreteSubject 实现 Subject 接口。

interface Subject {
attach(observer: Observer): void;
detach(observer: Observer): void;

// 通知所有订阅者
notify(): void;
}

interface Observer {
update(): void;
}

class ConcreteSubject implements Subject {
// 观察者列表
private observerList: Observer[] = [];

attach(observer: Observer): void {
this.observerList.push(observer);
}
detach(observer: Observer): void {
this.observerList.splice(this.observerList.indexOf(observer), 1);
}

// 遍历通知每个观察者
notify(): void {
for (const observer of this.observerList) {
observer.update();
}
}
}

class ConcreteObserverA implements Observer {
update(): void {
console.log('ObserverA update');
}
}

class ConcreteObserverB implements Observer {
update(): void {
console.log('ObserverB update');
}
}

const subject = new ConcreteSubject();
const observerA = new ConcreteObserverA();
const observerB = new ConcreteObserverB();
subject.attach(observerA);
subject.attach(observerB);

subject.notify();

输出:

ObserverA update
ObserverB update