Mozilla 的虚拟 getter 文档中试图展示的示例代码是什么?

问题描述

我正在使用 Mozilla 的文档阅读 JavaScript 的虚拟 getter:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get

其中有一个部分包含一些示例代码

在下面的例子中,对象有一个 getter 作为它自己的属性获取属性后,该属性将从对象中删除并 重新添加,但这次隐式作为数据属性。最后, 返回值。

get notifier() {
  delete this.notifier;
  return this.notifier = document.getElementById('bookmarked-notification-anchor');
},

这个例子是在文章讨论惰性/智能/记忆化之后出现的,但我没有看到代码是如何成为惰性/智能/记忆化getter的例子。

或者那部分完全是在谈论别的东西?

感觉好像我没有跟上文章的流程,可能是因为我不理解某些关键概念。

请告诉我,如果我只是过度思考这一点而该部分只是硬塞进来,或者该部分是否确实以某种方式与懒惰/智能/记忆有关。

谢谢您的指导??

更新 1:
我想也许我不知道如何验证代码是否被记住了。

我尝试在页面上的 IDE 中运行它:

const obj = {
  log: ['a','b','c'],get latest() {
    if (this.log.length === 0) {
      return undefined;
    }
    return this.log[this.log.length - 1];
  },get notifier() {
    delete this.notifier;
    return this.notifier = document.getElementById('bookmarked-notification-anchor');
  },};

console.log(obj.latest);
// expected output: "c"
console.log(obj.notifier);  // returns null

这似乎更合适,但我无法验证是否正在使用缓存:

const obj = {
  log: ['a',get notifier() {
    delete this.notifier;
    return this.notifier = this.log;
  },};

console.log(obj.latest);
// expected output: "c"
console.log(obj.notifier); // Array ["a","b","c"]
console.log(obj.notifier); // Array ["a","c"]

我想我不确定为什么需要先删除属性delete this.notifier;?这不会每次都使缓存失效吗?

更新 2: @Bergi,感谢您对示例代码的建议修改

我运行了这个(使用 delete):

const obj = {
  log: ['a',get notifier() {
    delete this.notifier;
    return this.notifier = console.log("heavy computation");
  },};

console.log(obj.latest);
// expected output: "c"
obj.notifier;
obj.notifier;

并得到:

> "c"
> "heavy computation"

我运行了这个(没有 delete):

const obj = {
  log: ['a',get notifier() {
    //delete this.notifier;
    return this.notifier = console.log("heavy computation");
  },};

console.log(obj.latest);
// expected output: "c"
obj.notifier;
obj.notifier;

并得到:

> "c"
> "heavy computation"
> "heavy computation"

所以这肯定证明正在发生记忆化。也许有太多的帖子复制和粘贴更新,但我很难理解为什么 delete 是必要的记忆。我分散而天真的大脑认为代码应该在没有 delete 时记忆。

抱歉,我需要坐下来考虑一下。除非您有关于如何理解正在发生的事情的快速提示

再次感谢大家的帮助??

更新 3:
我想我仍然缺少一些东西。

从 Ruby 开始,我将记忆化理解为:
如果它存在/预先计算,则使用它;如果不存在,则计算它

类似的东西:
this.property = this.property || this.calc()

使用示例代码片段中的 delete 时,该属性是不是总是不存在,因此总是需要重新计算?

我的逻辑肯定有问题,但我没有看到。我想这可能是“我不知道我不知道什么”的情景。

解决方法

如何测试记忆化

测试某些内容是否被记住的简单方法是使用 Math.random()

const obj = {
    get prop() {
        delete this.prop
        return this.prop = Math.random()
    }
}

console.log(obj.prop) // 0.1747926550503922
console.log(obj.prop) // 0.1747926550503922
console.log(obj.prop) // 0.1747926550503922

如果 obj.prop 没有被记住,它每次都会返回一个随机数:

const obj = {
    get prop() {
        return Math.random()
    }
}

console.log(obj.prop) // 0.7592929509653794
console.log(obj.prop) // 0.33531447188307895
console.log(obj.prop) // 0.685061719658401

它是如何工作的

在第一个例子中发生的是

  • delete 删除属性定义,包括 getter(当前正在执行的函数)和任何 setter,或者 all the extra stuff 连同它;
  • this.prop = ... 重新创建属性,这次更像是我们习惯的“普通”属性,因此下次访问它时不会通过 getter。

确实,MDN 上的示例演示了两者:

  • 一个“懒惰的getter”:它只会在需要该值时计算该值;
  • 和“记忆化”:它只会计算一次,然后每次都返回相同的结果。

深入讲解对象属性

所以你可以更好地理解当我们首先声明我们的对象、我们的 getter 时会发生什么,当我们删除时,然后我们重新分配属性,我将尝试更深入地了解对象的属性是。让我们举一个基本的例子:

const obj = {
  prop: 2
}

在这种情况下,我们可以通过 getOwnPropertyDescriptor 获取此属性的“配置”:

console.log(Object.getOwnPropertyDescriptor(obj,'prop'))

哪个输出

{
    configurable: true,enumerable: true,value: 2,writable: true,}

事实上,如果我们想对其进行不必要的明确,我们可以用 defineProperty 定义我们的 obj = { prop: 2 } 另一个(等效但冗长的)方式:

const obj = {}
Object.defineProperty(obj,'prop',{
    configurable: true,})

现在,当我们用一个 getter 来定义我们的属性时,它相当于像这样定义它:

Object.defineProperty(obj,get() {
        delete obj.prop
        return obj.prop = Math.random()
    }
})

当我们执行 delete this.prop 时,它会删除整个定义。事实上:

console.log(obj.prop) // 2
delete obj.prop
console.log(Object.getOwnPropertyDescriptor(obj,'prop')) // undefined

最后,this.prop = ... 重新定义了刚刚删除的属性。这是defineProperty的俄罗斯娃娃。

以下是所有完全不必要的显式定义的样子:

const obj = {}
Object.defineProperty(obj,{
    enumerable: true,configurable: true,get() {
        const finalValue = Math.random()
        Object.defineProperty(obj,{
            enumerable: true,value: finalValue,})
        return finalValue
    },})

奖金回合

有趣的事实:将 undefined 分配给我们想要“删除”的对象属性是 JS 中的一种常见模式(或假装它根本不存在)。甚至还有一种新的语法可以帮助解决这个问题,称为 "Nullish coalescing operator" (??):

const obj = {}

obj.prop = 0
console.log(obj.prop) // 0
console.log(obj.prop ?? 2) // 0

obj.prop = undefined
console.log(obj.prop) // undefined
console.log(obj.prop ?? 2) // 2

但是,我们仍然可以“检测”该属性在分配 undefined 时是否存在。只有 delete 才能真正将其从对象中移除:

const obj = {}

console.log(obj.prop) // undefined
console.log(obj.hasOwnProperty('prop')) // false
console.log(Object.getOwnPropertyDescriptor(obj,'prop')) // undefined

obj.prop = undefined
console.log(obj.prop) // undefined
console.log(obj.hasOwnProperty('prop')) // true
console.log(Object.getOwnPropertyDescriptor(obj,'prop')) // {"writable":true,"enumerable":true,"configurable":true}

delete obj.prop
console.log(obj.prop) // undefined
console.log(obj.hasOwnProperty('prop')) // false
console.log(Object.getOwnPropertyDescriptor(obj,'prop')) // undefined
,

@sheraff 的回答让我有了以下理解。希望@sheraff 的这种重新措辞对其他人有用。

来自https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get的代码:

get notifier() {
  delete this.notifier;
  return this.notifier = document.getElementById('bookmarked-notification-anchor');
},

不是在演示 getter 的/setter 的记忆行为,而是在使用 getters/setter 的同时实现记忆。

正如文章所述:

请注意,getter 本质上不是“懒惰”或“记忆化”的;你必须 如果您需要这种行为,请实施此技术。

以下示例代码片段帮助我意识到了这一点。

这是基于文章中提供的示例代码的工作片段:

const obj = {
  get notifier() {
    console.log("getter called");
    delete this.notifier;
    return this.notifier = Math.random();
  },};

console.log(obj.notifier);
console.log(obj.notifier);
// results in
> "getter called"
> 0.644950142066832
> 0.644950142066832

这是一个更详细的代码版本,它简化了一些 JavaScript 技巧,因此我更容易理解(@sheraff 的回答帮助我获得了对此代码的评论):

const obj = {
  get notifier() {
    console.log("getter called");
    delete this.notifier; // remove this getter function from the object so Math.random() won't be called again
    let value = Math.random(); // this resource intensive operation will be skipped on subsequent calls to obj.notifier
    this.notifier = value; // create a new property of the same name that does not have getter/setter
    return this.notifier; // return new property so the first call to obj.notifier does not return undefined
  },};

console.log(obj.notifier);
console.log(obj.notifier);
// results in
> "getter called"
> 0.7212959093641651
> 0.7212959093641651

如果我从未阅读过这篇文章并且来自另一种编程语言,我将如何实现记忆化:

const obj = {
  get notifier() {
    if (this._notifier) {
      return this._notifier;
    } else {
      console.log("calculation performed");
      return this._notifier = Math.random();
    }
  },};

console.log(obj.notifier);
console.log(obj.notifier);
// results in
> "calculation performed"
> 0.6209661598889056
> 0.6209661598889056

可能存在我不知道的低效率和其他原因,这应该阻止以这种方式实现记忆,但我理解它很简单明了。这是我在实现记忆化的文章中期望看到的。没看到,不知为何以为这篇文章只是在演示记忆。

以上所有代码片段都实现了记忆化,这是文章中示例代码的意图。我最初的困惑是误读了代码演示了记忆,这让我觉得 delete 语句很奇怪。

希望有帮助??

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...