传统上,开发人员在 JavaScript 类中为实例中可能需要的任何数据创建属性。对于在构造函数中随时可用的小块数据来说,这不是问题。但是,如果在实例中可用之前需要计算某些数据,您可能不想预先支付该费用。例如,考虑这个类:

class MyClass {
constructor() {
this.data = someExpensiveComputation();
}
}

在这里,data属性是作为执行一些昂贵计算的结果而创建的。如果您不确定是否会使用该属性,则预先执行该计算可能效率不高。幸运的是,有几种方法可以将这些操作推迟到以后。

按需属性模式

优化执行昂贵操作的最简单方法是等到需要数据后再进行计算。例如,您可以使用带有 getter 的访问器属性来按需进行计算,如下所示:

class MyClass {
get data() {
return someExpensiveComputation();
}
}

在这种情况下,直到有人第一次读取该data属性时,您的昂贵计算才会发生,这是一种改进。但是,每次data读取属性时都会执行相同的昂贵计算,这比之前的示例更糟糕,其中至少只执行了一次计算。这不是一个好的解决方案,但您可以在此基础上创建一个更好的解决方案。

凌乱的延迟加载属性模式

只有在访问属性时才执行计算是一个好的开始。您真正需要的是在该点之后缓存信息并仅使用缓存版本。但是您将这些信息缓存在哪里以便于访问?最简单的方法是定义一个具有相同名称的属性并将其值设置为计算数据,如下所示:

class MyClass {
get data() {
const actualData = someExpensiveComputation(); Object.defineProperty(this, "data", {
value: actualData,
writable: false,
configurable: false,
enumerable: false
}); return actualData;
}
}

在这里,该data属性再次定义为类上的 getter,但这次它缓存了结果。调用Object.defineProperty()创建一个名为的新属性data,该属性具有固定值actualData,并且设置为不可写、可配置和不可枚举(以匹配 getter)。之后,返回值本身。下次data访问该属性时,它将从新创建的属性中读取而不是调用 getter:

const object = new MyClass();

// calls the getter
const data1 = object.data; // reads from the data property
const data2 = object.data;

实际上,所有计算仅在第一次data读取属性时完成。对该data属性的每次后续读取都返回缓存的版本。

这种模式的一个缺点是data属性开始是不可枚举的原型属性,最终是不可枚举的自己的属性:

const object = new MyClass();
console.log(object.hasOwnProperty("data")); // false const data = object.data;
console.log(object.hasOwnProperty("data")); // true

虽然这种区别在很多情况下并不重要,但理解这种模式很重要,因为它在传递对象时可能会导致微妙的问题。幸运的是,使用更新的模式很容易解决这个问题。

类的唯一自己的延迟加载属性模式

如果您有一个用例,其中延迟加载的属性始终存在于实例中很重要,那么您可以使用Object.defineProperty()在类构造函数中创建属性。它比前面的例子有点混乱,但它会确保该属性只存在于实例上。下面是一个例子:

class MyClass {
constructor() { Object.defineProperty(this, "data", {
get() {
const actualData = someExpensiveComputation(); Object.defineProperty(this, "data", {
value: actualData,
writable: false,
configurable: false
}); return actualData;
},
configurable: true,
enumerable: true
}); }
}

在这里,构造函数data使用Object.defineProperty(). 该属性是在实例上创建的(通过使用this)并定义一个 getter 并指定该属性为可枚举和可配置的(典型的自己的属性)。将data属性设置为可配置特别重要,以便您可以Object.defineProperty()再次调用它。

然后 getter 函数进行计算并再次调用Object.defineProperty()。该data属性现在被重新定义为具有特定值的数据属性,并且不可写和不可配置以保护最终数据。然后,计算数据从 getter 返回。下次data读取属性时,它将从存储的值中读取。作为奖励,该data财产现在仅作为自己的财产存在,并且在第一次阅读之前和之后的行为都相同:

const object = new MyClass();
console.log(object.hasOwnProperty("data")); // true const data = object.data;
console.log(object.hasOwnProperty("data")); // true

对于类,这很可能是您要使用的模式;另一方面,对象文字可以使用更简单的方法。

对象字面量的延迟加载属性模式

如果您使用对象字面量而不是类,则过程要简单得多,因为在对象字面量上定义的 getter 被定义为可枚举的自身属性(而不是原型属性),就像数据属性一样。这意味着您可以对类使用凌乱的延迟加载属性模式而对于对象来说不会凌乱:

const object = {
get data() {
const actualData = someExpensiveComputation(); Object.defineProperty(this, "data", {
value: actualData,
writable: false,
configurable: false,
enumerable: false
}); return actualData;
}
}; console.log(object.hasOwnProperty("data")); // true const data = object.data;
console.log(object.hasOwnProperty("data")); // true

结论

在 JavaScript 中重新定义对象属性的能力提供了一个独特的机会来缓存可能计算成本很高的信息。通过从重新定义为数据属性的访问器属性开始,您可以将计算推迟到第一次读取属性时,然后缓存结果以供以后使用。这种方法既适用于类,也适用于对象字面量,并且在对象字面量中更简单一些,因为您不必担心您的 getter 会在原型上结束。

提高性能的最佳方法之一是避免重复执行相同的工作,因此任何时候您可以缓存结果以供以后使用,都可以加快程序的运行速度。延迟加载属性模式等技术允许任何属性成为缓存层以提高性能。

JavaScript 中的延迟加载属性模式的更多相关文章

  1. js架构设计模式——理解javascript中的MVVM开发模式

    理解javascript中的MVVM开发模式 http://blog.csdn.net/slalx/article/details/7856769 MVVM的全称是Model View ViewMod ...

  2. 借助JavaScript中的Dom属性改变Html中Table边框的颜色

    借助JavaScript中的Dom属性改变Html中Table边框的颜色 -------------------- <html> <head> <title>我是页 ...

  3. Javascript中理解发布--订阅模式

    Javascript中理解发布--订阅模式 阅读目录 发布订阅模式介绍 如何实现发布--订阅模式? 发布---订阅模式的代码封装 如何取消订阅事件? 全局--发布订阅对象代码封装 理解模块间通信 回到 ...

  4. javascript中常用坐标属性offset、scroll、client

    原文:javascript中常用坐标属性offset.scroll.client 今天在学习js的时候觉得这个问题比较容易搞混,所以自己画了一个简单的图,并且用js控制台里面输出测试了下,便于理解. ...

  5. javascript 中 arguments.callee属性

    javascript 中 arguments.callee属性 可以在函数内部,指向的是这个函数(或者叫做“类”)本身. 相当于PHP 中的 self 关键字. The arguments.calle ...

  6. JavaScript中的文档模式和严格模式

    JavaScript中的文档模式和严格模式 语法模式有普通模式和严格模式两种 普通模式:正常的JavaScript语法拼写以及代码编写(相对于严格模式存在着语法上的不严谨),尽可能的识别错误以及不规范 ...

  7. 【转】Javascript中理解发布--订阅模式

    Javascript中理解发布--订阅模式 阅读目录 发布订阅模式介绍 发布---订阅模式又叫观察者模式,它定义了对象间的一种一对多的关系,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时 ...

  8. JavaScript中对象的属性

    在JavaScript中,属性决定了一个对象的状态,本文详细的研究了它们是如何工作的. 属性类型 JavaScript中有三种不同类型的属性:命名数据属性(named data properties) ...

  9. JavaScript中Number常用属性和方法

    title: JavaScript中Number常用属性和方法 toc: false date: 2018-10-13 12:31:42 Number.MAX_VALUE--1.79769313486 ...

随机推荐

  1. 【antd Vue】封装upload图片上传组件(返回Base64)

    最近需要把上传的图片信息存储到数据库,以base64的方式,需要重新封装一下antd的upload组件 1. 使用方法 引入组件然后配置一下即可使用,配置项包括 defaultImageList,需要 ...

  2. (9)MySQL进阶篇SQL优化(InnoDB锁-记录锁)

    1.概述 InnoDB行锁是通过给索引上的索引项加锁来实现的,这一点MySQL与Oracle不同,后者是通过在数据块中对相应数据行加锁来实现的.InnoDB这种行锁实现特点意味着:只有通过索引条件检索 ...

  3. Spring Cloud Alibaba(9)---Sentinel概述

    Sentinel概述 随着微服务的流行,服务和服务之间的稳定性变得越来越重要.Sentinel是面向分布式服务架构的流量控制组件,主要以流量为切入点,从限流.流量整形.熔断降级.系统自适应保护.热点防 ...

  4. PostgreSQL条件表达式

      条件表达式在日常工作中很多场景都会用到,比如某个字段为空,取另外一个字段:某个值大于多少,取什么字段,小于多少取什么字段等等.那么下面来简单的学习下PostgreSQL有那些条件表达式. 1.CA ...

  5. JAVA基础——运算符号

    运算符(java) 算数运算符:+,-,*,/,%(取余),++,-- 赋值运算符:= 关系运算符:<, >, >= ,<= ,== , != 逻辑运算符:&& ...

  6. [bug] 前台表单添加数据,后台返回成功消息,但数据库相应字段值为null

    原因 前端代码中输入框字段值和后端定义的属性值不一致 分析 bean . controller 如下图,浏览器收到后台返回消息,在后台也可打印user信息,说明已成功接收前端JSON数据并转换为use ...

  7. 服务器硬件必须支持M2 或PCIE才能支持NVME

    兆芯服务器不支持NVME. 服务器硬件必须支持M2 或PCIE才能支持NVME.1 因为物理接口只有M2 SATA 和PCIE这三中但是NVME只支持M2 和PCIE这2种2所以 NVME不支持SAT ...

  8. 电脑无法开机,用一个U盘先备份C盘部分重要文件并重装Win10系统的教程?

    电脑无法开机,用一个U盘先备份C盘部分重要文件并重装Win10系统的教程.? 这应该是修电脑的万能方法,重装系统能解决绝大多数非硬件导致的电脑故障,但之前要备份一下桌面的一些个人文件.所以想学一下,以 ...

  9. jmeter 非GUI模式下传参

    为什么要使用非GUI模式?--因为GUI模式下运行的时候消耗的内存比非GUI模式大,会使得压力测试的结果不准确. 根据参数类型分为两种:jmeter.properties和system.propert ...

  10. FireFox-background