草庐IT

javascript - 通过从构造函数返回现有实例来实现 javascript 实例存储

coder 2024-12-28 原文

我正在尝试在 Backbone.js 中实现我的“实例存储”版本,正如 Soundcloud 在他们最近的博客文章中所描述的那样:

http://backstage.soundcloud.com/2012/06/building-the-next-soundcloud/

相关摘录:

为了解决这个问题,我们使用了一个我们称之为实例存储的结构。这个存储是一个对象,每次调用模型的构造函数时都会隐式访问和修改它。首次构建模型时,它会将自己注入(inject)到商店中,使用其 id 作为唯一键。如果使用相同的 id 调用相同的模型构造函数,则返回原始实例。

var s1 = new Sound({id: 123}),
    s2 = new Sound({id: 123});

s1 === s2; // true, these are the exact same object.

这之所以有效,是因为 Javascript 有一个鲜为人知的特性。如果构造函数返回一个对象,那么这就是分配的值。因此,如果我们返回对先前创建的实例的引用,我们将获得所需的行为。在幕后,构造函数基本上是这样做的:

var store = {};

function Sound(attributes) {
    var id = attributes.id;

    // check if this model has already been created
    if (store[id]) {
        // if yes, return that
        return store[id];
    }
    // otherwise, store this instance
    store[id] = this;
}

我通过覆盖 Backbone.Model 类来创建我自己的构造函数来实现我的版本。

var MyModel = Backbone.Model.extend({
    constructor: function (attributes, options) {
        var id = attributes ? attributes.id : undefined;

        if (this.store[id]) {
            return this.store[id];
        }

        Backbone.Model.prototype.constructor.apply(this, arguments);

        if (id) {
            this.store[id] = this;
        }
    }
});

var MyOtherModel = MyModel.extend({
    store: {},

    //other model stuff
});

这工作得很好,但肯定发生了一些变化,现在它停止工作了,我不确定为什么。新创建的实例毫无问题地存储在存储对象中——每个扩展 MyModel 类的类都有自己的空存储,以避免具有相同 ID 的不同类型的实例发生冲突。当使用现有 id 调用构造函数时,也可以毫无问题地检索到正确的实例,但是当它们从构造函数返回时,返回值将被忽略。我对规范的理解是,构造函数可以返回一个对象——但不是原始对象——当使用 new 运算符调用构造函数时,返回的对象将被分配到赋值语句的左侧。这不会发生,即使构造函数返回一个对象,也会使用由 new 运算符创建的空对象。

一些调试信息。不确定此信息会有多大帮助。这是第一次实例化对象的 MyModel 构造函数中的“this”。

child
    _callbacks: Object
    _escapedAttributes: Object
    _previousAttributes: Object
    _setting: false
    attributes: Object
        id: "4fd6140032a6e522f10009ac"
        manufacturer_id: "4f4135ae32a6e52a53000001"
        name: "Tide"
        uniqueName: "tide"
    __proto__: Object
    cid: "c50"
    collection: child
    id: "4fd6140032a6e522f10009ac"
    __proto__: ctor
        constructor: function (){ parent.apply(this, arguments); }
        defaults: Object
        store: Object
        url: function () {
        urlRoot: function () {
        __proto__: ctor

当它是从实例存储返回的对象时,这是 MyModel 构造函数中的“this”:

child
    _callbacks: Object
    _escapedAttributes: Object
    _previousAttributes: Object
    _setting: false
    attributes: Object
        _validate: function (attrs, options) {
        bind: function (events, callback, context) {
        change: function (options) {
        changedAttributes: function (diff) {
        clear: function (options) {
        clone: function () {
        constructor: function (){ parent.apply(this, arguments); }
        defaults: Object
        destroy: function (options) {
        escape: function (attr) {
        fetch: function (options) {
        get: function (attr) {
        has: function (attr) {
        hasChanged: function (attr) {
        idAttribute: "id"
        initialize: function (){}
        isNew: function () {
        isValid: function () {
        manufacturer_id: 0
        name: ""
        off: function (events, callback, context) {
        on: function (events, callback, context) {
        parse: function (resp, xhr) {
        previous: function (attr) {
        previousAttributes: function () {
        save: function (key, value, options) {
        set: function (key, value, options) {
        store: Object
        toJSON: function () {
        trigger: function (events) {
        unbind: function (events, callback, context) {
        unset: function (attr, options) {
        url: function () {
        urlRoot: function () {
        __proto__: Object
        cid: "c141"
     __proto__: ctor
        constructor: function (){ parent.apply(this, arguments); }
        defaults: Object
        store: Object
        url: function () {
        urlRoot: function () {
        __proto__: ctor

我注意到第二个中的属性对象包含主干对象的所有方法,但它们不应该包含在内。它也没有 id,我也不确定为什么。希望这提供了一些见解。谢谢。

最佳答案

我不会为此使用扩展,我认为拥有一个单独的“工厂”是正确的想法。它可以让您扩展模型而不用担心副作用。

来自annotated source backbone 用 extend 做了一些奇怪的事情,我还没有完全理解它。 (另请查看 inherits )所以让我们暂时跳过它并坚持使用您的工作解决方案。

我已经修改了您生成工厂模型的方法,您应该能够像普通模型一样使用它们(例如,将它们设置在一个集合中),除了扩展它们不起作用。他们还将像 soundcloud 示例那样处理使用新数据更新您的模型。

var makeStoreable = function(model){
  var StoreModel = function(attr, opt){
    if(!attr || !attr.id){
      // The behavior you exhibit here is up to you
      throw new Error('Cool Models always have IDs!');
    }
    if(this.store[attr.id]){
      this.store[attr.id].set(attr, opt);
    }else{
      var newModel = new model(attr, opt);
      this.store[attr.id] = newModel;
    }
    return this.store[attr.id];
  };
  StoreModel.prototype.store = {};
  return StoreModel;
};

var CoolModel = Backbone.Model.extend({});

CoolModel = makeStoreable(CoolModel);

var a = new CoolModel({
    id: 4,
    coolFactor: 'LOW'
});

var b = new CoolModel({
    id:4,
    coolFactor: 'HIGH'
});

console.log(a===b); //true!
console.log(a.get('coolFactor') === 'HIGH'); //true!

here's a fiddle玩。

另外,我欢迎有人想出一个模型内解决方案,将“商店”保留在模型实例的原型(prototype)中。此外,为了防止内存泄漏,我们可能应该在工厂或模型本身上创建一个引用计数销毁方法。

关于javascript - 通过从构造函数返回现有实例来实现 javascript 实例存储,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11145159/

有关javascript - 通过从构造函数返回现有实例来实现 javascript 实例存储的更多相关文章

  1. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  2. ruby - 为什么 4.1%2 使用 Ruby 返回 0.0999999999999996?但是 4.2%2==0.2 - 2

    为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返

  3. ruby-on-rails - 如何使用 instance_variable_set 正确设置实例变量? - 2

    我正在查看instance_variable_set的文档并看到给出的示例代码是这样做的:obj.instance_variable_set(:@instnc_var,"valuefortheinstancevariable")然后允许您在类的任何实例方法中以@instnc_var的形式访问该变量。我想知道为什么在@instnc_var之前需要一个冒号:。冒号有什么作用? 最佳答案 我的第一直觉是告诉你不要使用instance_variable_set除非你真的知道你用它做什么。它本质上是一种元编程工具或绕过实例变量可见性的黑客攻击

  4. ruby 正则表达式 - 如何替换字符串中匹配项的第 n 个实例 - 2

    在我的应用程序中,我需要能够找到所有数字子字符串,然后扫描每个子字符串,找到第一个匹配范围(例如5到15之间)的子字符串,并将该实例替换为另一个字符串“X”。我的测试字符串s="1foo100bar10gee1"我的初始模式是1个或多个数字的任何字符串,例如,re=Regexp.new(/\d+/)matches=s.scan(re)给出["1","100","10","1"]如果我想用“X”替换第N个匹配项,并且只替换第N个匹配项,我该怎么做?例如,如果我想替换第三个匹配项“10”(匹配项[2]),我不能只说s[matches[2]]="X"因为它做了两次替换“1fooX0barXg

  5. ruby - 在没有 sass 引擎的情况下使用 sass 颜色函数 - 2

    我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re

  6. ruby-on-rails - 在 ruby​​ 中使用 gsub 函数替换单词 - 2

    我正在尝试用ruby​​中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了

  7. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  8. ruby - 检查字符串是否包含散列中的任何键并返回它包含的键的值 - 2

    我有一个包含多个键的散列和一个字符串,该字符串不包含散列中的任何键或包含一个键。h={"k1"=>"v1","k2"=>"v2","k3"=>"v3"}s="thisisanexamplestringthatmightoccurwithakeysomewhereinthestringk1(withspecialcharacterslike(^&*$#@!^&&*))"检查s是否包含h中的任何键的最佳方法是什么,如果包含,则返回它包含的键的值?例如,对于上面的h和s的例子,输出应该是v1。编辑:只有字符串是用户定义的。哈希将始终相同。 最佳答案

  9. ruby - 在 Ruby 中有条件地定义函数 - 2

    我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

  10. ruby-on-rails - Rails - 从另一个模型中创建一个模型的实例 - 2

    我有一个正在构建的应用程序,我需要一个模型来创建另一个模型的实例。我希望每辆车都有4个轮胎。汽车模型classCar轮胎模型classTire但是,在make_tires内部有一个错误,如果我为Tire尝试它,则没有用于创建或新建的activerecord方法。当我检查轮胎时,它没有这些方法。我该如何补救?错误是这样的:未定义的方法'create'forActiveRecord::AttributeMethods::Serialization::Tire::Module我测试了两个环境:测试和开发,它们都因相同的错误而失败。 最佳答案

随机推荐