当前位置: 首页 > 面试题库 >

Bluebird的util.toFastProperties函数如何使对象的属性“快速”?

宋翔
2023-03-14
问题内容

在Bluebird的util.js文件中,它具有以下功能:

function toFastProperties(obj) {
    /*jshint -W027*/
    function f() {}
    f.prototype = obj;
    ASSERT("%HasFastProperties", true, obj);
    return f;
    eval(obj);
}

由于某种原因,在return函数之后有一条语句,我不确定为什么会在其中。

同样,这似乎是有意的,因为作者已对此静默了JSHint警告:

“返回”后无法到达“评估”。(W027)

此功能的作用是什么?难道util.toFastProperties真的让一个对象的属性“快”?

我已经在Bluebird的GitHub存储库中搜索了源代码中的任何注释或问题列表中的解释,但找不到任何注释。


问题答案:

2017年更新:首先,对于今天要来的读者-这是与Node 7(4+)兼容的版本:

function enforceFastProperties(o) {
    function Sub() {}
    Sub.prototype = o;
    var receiver = new Sub(); // create an instance
    function ic() { return typeof receiver.foo; } // perform access
    ic(); 
    ic();
    return o;
    eval("o" + o); // ensure no dead code elimination
}

没有一两个小的优化-以下所有内容仍然有效。

让我们首先讨论它的作用以及为什么这样做更快,然后讨论它为什么起作用。

它能做什么

V8引擎使用两种对象表示形式:

  • 字典模式 -将对象作为键值映射存储为哈希映射。
  • 快速模式 -对象像structs一样存储,其中属性访问不涉及任何计算。

这是一个演示速度差异的简单演示。在这里,我们使用该delete语句强制对象进入慢速词典模式。

引擎会尽可能尝试使用快速模式,并且通常会在执行许多属性访问时尝试使用快速模式-
但是有时会陷入字典模式。处于字典模式会降低性能,因此通常需要将对象置于快速模式。

该hack旨在将对象从字典模式强制为快速模式。

  • 蓝鸟的佩特卡本人在这里谈论它。
  • 维亚切斯拉夫·埃格罗夫(Vyacheslav Egorov)的这些幻灯片也提到了这一点。
  • 这篇过时的文章仍然是不错的读物,可以使您很好地了解v8中如何存储对象。

为什么更快

在JavaScript原型中,通常存储许多实例之间共享的函数,并且很少动态更改。因此,非常希望让它们处于快速模式,以避免每次调用函数时产生额外的损失。

为此-v8会很高兴将.prototype作为函数属性的对象置于快速模式,因为它们将由通过调用该函数作为构造函数创建的每个对象共享。通常,这是一个聪明而理想的优化。

这个怎么运作

首先让我们看一下代码,弄清楚每一行的作用:

function toFastProperties(obj) {
    /*jshint -W027*/ // suppress the "unreachable code" error
    function f() {} // declare a new function
    f.prototype = obj; // assign obj as its prototype to trigger the optimization
    // assert the optimization passes to prevent the code from breaking in the
    // future in case this optimization breaks:
    ASSERT("%HasFastProperties", true, obj); // requires the "native syntax" flag
    return f; // return it
    eval(obj); // prevent the function from being optimized through dead code 
               // elimination or further optimizations. This code is never  
               // reached but even using eval in unreachable code causes v8
               // to not optimize functions.
}

我们不会
找到自己的代码就断言V8这是否优化,我们可以改为阅读V8单元测试:

// Adding this many properties makes it slow.
assertFalse(%HasFastProperties(proto));
DoProtoMagic(proto, set__proto__);
// Making it a prototype makes it fast again.
assertTrue(%HasFastProperties(proto));

阅读并运行此测试向我们表明,此优化确实在v8中有效。但是-很高兴看到如何。

如果我们检查objects.cc,可以找到以下功能(L9925):

void JSObject::OptimizeAsPrototype(Handle<JSObject> object) {
  if (object->IsGlobalObject()) return;

  // Make sure prototypes are fast objects and their maps have the bit set
  // so they remain fast.
  if (!object->HasFastProperties()) {
    MigrateSlowToFast(object, 0);
  }
}

现在,JSObject::MigrateSlowToFast只需显式地使用Dictionary并将其转换为快速的V8对象。这是一本值得一读的书,并且对v8对象内部有一个有趣的见解-
但这不是这里的主题。我仍然热烈建议您在这里阅读它,因为这是学习v8对象的好方法

如果我们检SetPrototypeobjects.cc,我们可以看到它在12231行中被调用:

if (value->IsJSObject()) {
    JSObject::OptimizeAsPrototype(Handle<JSObject>::cast(value));
}

反过来,FuntionSetPrototype这就是我们所得到的.prototype =

这样做__proto__ =还是.setPrototypeOf可以的,但这些都是ES6函数,Bluebird自Netscape
7起便在所有浏览器上运行,因此简化此处的代码是不可能的。例如,如果我们检查.setPrototypeOf我们可以看到:

// ES6 section 19.1.2.19.
function ObjectSetPrototypeOf(obj, proto) {
  CHECK_OBJECT_COERCIBLE(obj, "Object.setPrototypeOf");

  if (proto !== null && !IS_SPEC_OBJECT(proto)) {
    throw MakeTypeError("proto_object_or_null", [proto]);
  }

  if (IS_SPEC_OBJECT(obj)) {
    %SetPrototype(obj, proto); // MAKE IT FAST
  }

  return obj;
}

直接打开Object

InstallFunctions($Object, DONT_ENUM, $Array(
...
"setPrototypeOf", ObjectSetPrototypeOf,
...
));

所以-我们走了从Petka编写的代码到裸机的道路。很好

免责声明:

请记住,这是所有实现细节。像Petka这样的人都是优化狂。永远记住,过早的优化是97%的时间里所有邪恶的根源。蓝鸟经常做一些非常基本的事情,因此从这些性能技巧中受益匪浅-
像回调一样快并不容易。您 很少 需要在不提供库功能的代码中执行类似的操作。



 类似资料:
  • 问题内容: 我在带有标志的Windows上使用 节点v0.11.14-nightly-20140819-pre。 我的JavaScript对象在其原型中定义了两种方法: 的对象返回一个承诺。 当我使用如下对象时: 在对象的结束时,由被称为一旦承诺得到满足。 我尝试同时使用 ES6 和 Bluebird promise实现。 为什么最终会出现这种情况? 问题答案: 为什么最终会出现这种情况? 因为您

  • 问题内容: 说我这样创建一个对象: 检索属性名称列表的最佳方法是什么?即我想以一些变量“键”结束: 问题答案: 在现代浏览器(IE9 +,FF4 +,Chrome5 +,Opera12 +,Safari5+)中,您可以使用内置的Object.keys方法: 上面有完整的polyfill,但简化的版本是: 或者替换为,以允许您调用任何对象。扩展原型会产生一些副作用,我不建议您这样做。

  • 问题内容: 如何枚举JavaScript对象的属性? 我实际上想列出所有已定义的变量及其值,但是我了解到定义一个变量实际上会创建window对象的属性。 问题答案: 很简单: 现在,您将无法以这种方式获取私有变量,因为它们不可用。 编辑:是正确的,除非您使用该方法,否则您将获得继承的属性- 但是,我不知道为什么任何熟悉面向对象编程的人都期望得到更少的东西!通常,提起此问题的人会受到道格拉斯·克罗克

  • 结果: org.springframework.expression.spel.SpelEvaluationException: EL1008E:(pos 0): 在类型为'org.springframework.cache.interceptor.CacheExpressionRootObject'的对象上找不到属性或字段'id' - 也许不是公共的?

  • 问题内容: 我有一个像这样的JSON字符串: 我想将属性更新为,如果值为,并且值为。 我已经到了这一点,但是不确定如何进行: 我不知道怎么去的是一个孩子。 提前致谢。任何指针都很棒。 问题答案: 您可以使用属性作为键来访问对象: 对于您的示例,请尝试:

  • 本文向大家介绍如何使用jQuery迭代JavaScript对象的属性?,包括了如何使用jQuery迭代JavaScript对象的属性?的使用技巧和注意事项,需要的朋友参考一下 使用该方法可以迭代JavaScript对象的属性。 上面的照片- 将用于任何集合遍历,无论是一个对象或一个数组。我们来看另一个例子- 示例 输出结果 上面的照片-