首页 文章

对象文字/初始化器中的自引用

提问于
浏览
581

有没有办法在JavaScript中使用以下内容?

var foo = {
    a: 5,
    b: 6,
    c: this.a + this.b  // Doesn't work
};

在当前形式中,此代码显然会引发引用错误,因为 this 不引用 foo . 但有没有办法让对象文字的属性中的值依赖于之前声明的其他属性?

21 回答

  • 3

    好吧,我唯一可以告诉你的是吸气剂:

    var foo = {
      a: 5,
      b: 6,
      get c () {
        return this.a + this.b;
      }
    };
    
    foo.c; // 11
    

    这是ECMAScript第5版规范引入的语法扩展,大多数现代浏览器(包括IE9)都支持该语法 .

  • 2

    你可以这样做:

    var foo = {
       a: 5,
       b: 6,
       init: function() {
           this.c = this.a + this.b;
           return this;
       }
    }.init();
    

    这将是对象的某种一次初始化 .

    请注意,您实际上是将 init() 的返回值分配给 foo ,因此您必须 return this .

  • 0

    缺少明显的简单答案,所以为了完整性:

    但是有没有办法让对象文字的属性中的值依赖于之前声明的其他属性?

    不会 . 这里的所有解决方案都会推迟到创建对象之后(以各种方式),然后分配第三个属性 . 最简单的方法就是这样做:

    var foo = {
        a: 5,
        b: 6
    };
    foo.c = foo.a + foo.b;
    

    所有其他人只是更间接的方式来做同样的事情 . (Felix's特别聪明,但需要创建和销毁临时函数,增加复杂性;要么在对象上留下额外的属性,要么[如果你 delete 属性] impacts the performance后续属性访问该对象 . )

    如果你需要它在一个表达式中,你可以在没有临时属性的情况下做到这一点:

    var foo = function(o) {
        o.c = o.a + o.b;
        return o;
    }({a: 5, b: 6});
    

    或者当然,如果您需要多次执行此操作:

    function buildFoo(a, b) {
        var o = {a: a, b: b};
        o.c = o.a + o.b;
        return o;
    }
    

    然后你需要使用它:

    var foo = buildFoo(5, 6);
    
  • 8

    只需实例化一个匿名函数:

    var foo = new function () {
        this.a = 5;
        this.b = 6;
        this.c = this.a + this.b;
    };
    
  • -3

    有些关闭应该处理这个问题;

    var foo = function() {
        var a = 5;
        var b = 6;
        var c = a + b;
    
        return {
            a: a,
            b: b,
            c: c
        }
    }();
    

    foo 中声明的所有变量都是 foo 的私有,正如您对任何函数声明所期望的那样,并且因为它们都在范围内,所以它们都可以相互访问而无需引用 this ,就像您对函数所期望的那样 . 区别在于此函数返回一个公开私有变量的对象,并将该对象分配给 foo . 最后,只返回要作为具有 return {} 语句的对象公开的接口 .

    然后使用 () 在最后执行该函数,这将导致整个foo对象被评估,实例化中的所有变量和作为 foo() 的属性添加的返回对象 .

  • 2

    现在在ES6中,您可以创建惰性缓存属性 . 首次使用时,属性将评估一次以成为正常的静态属性 . 结果:第二次跳过数学函数开销 .

    魔法在吸气剂中 .

    const foo = {
        a: 5,
        b: 6,
        get c() {
            delete this.c;
            return this.c = this.a + this.b
        }
    };
    

    在箭头getter this 拿起surrounding lexical scope .

    foo     // {a: 5, b: 6}
    foo.c   // 11
    foo     // {a: 5, b: 6 , c: 11}
    
  • 153

    你可以这样做

    var a, b
    var foo = {
        a: a = 5,
        b: b = 6,
        c: a + b
    }
    

    当我不得不引用最初声明函数的对象时,该方法对我有用 . 以下是我如何使用它的最小例子:

    function createMyObject() {
        var count = 0, self
        return {
            a: self = {
                log: function() {
                    console.log(count++)
                    return self
                }
            }
        }
    }
    

    通过将self定义为包含print函数的对象,您可以让函数引用该对象 . 这意味着如果您需要将打印功能传递给其他地方,则不必将打印功能“绑定”到对象上 .

    相反,如果您愿意,请使用 this ,如下图所示

    function createMyObject() {
        var count = 0
        return {
            a: {
                log: function() {
                    console.log(count++)
                    return this
                }
            }
        }
    }
    

    然后,以下代码将记录0,1,2,然后给出错误

    var o = createMyObject()
    var log = o.a.log
    o.a.log().log() // this refers to the o.a object so the chaining works
    log().log() // this refers to the window object so the chaining fails!
    

    通过使用self方法,您可以保证print始终返回相同的对象,而不管运行函数的上下文 . 当使用 createMyObject() 的自我版本时,上面的代码将运行正常并记录0,1,2和3 .

  • 7

    为了完成,在ES6中我们有类(在撰写时仅支持最新的浏览器,但在Babel,TypeScript和其他转换器中可用)

    class Foo {
      constructor(){
        this.a = 5;
        this.b = 6;
        this.c = this.a + this.b;
      }  
    }
    
    const foo = new Foo();
    
  • 1

    有几种方法可以实现这一目标;这是我会用的:

    function Obj() {
     this.a = 5;
     this.b = this.a + 1;
     // return this; // commented out because this happens automatically
    }
    
    var o = new Obj();
    o.b; // === 6
    
  • 287

    您可以使用模块模式执行此操作 . 就像:

    var foo = function() {
      var that = {};
    
      that.a = 7;
      that.b = 6;
    
      that.c = function() {
        return that.a + that.b;
      }
    
      return that;
    };
    var fooObject = foo();
    fooObject.c(); //13
    

    使用此模式,您可以根据需要实例化几个foo对象 .

    http://jsfiddle.net/jPNxY/1/

  • 14

    只是为了思考 - 在时间轴之外放置对象的属性:

    var foo = {
        a: function(){return 5}(),
        b: function(){return 6}(),
        c: function(){return this.a + this.b}
    }
    
    console.log(foo.c())
    

    上面也有更好的答案 . 这就是我修改你所质疑的示例代码的方法 .

    UPDATE:

    var foo = {
        get a(){return 5},
        get b(){return 6},
        get c(){return this.a + this.b}
    }
    // console.log(foo.c);
    
  • 18

    在对象文字上创建新函数并调用构造函数似乎与原始问题完全不同,并且它是不必要的 .

    在对象文字初始化期间,您无法引用同级属性 .

    var x = { a: 1, b: 2, c: a + b } // not defined 
    var y = { a: 1, b: 2, c: y.a + y.b } // not defined
    

    计算属性的最简单解决方案如下(没有堆,没有函数,没有构造函数):

    var x = { a: 1, b: 2 };
    
    x.c = x.a + x.b; // apply computed property
    
  • 7

    这一切的关键是 SCOPE .

    您需要封装要定义的属性的"parent"(父对象)作为它自己的实例化对象,然后您可以使用关键字 this 来引用兄弟属性

    要记住,如果你在没有这么做的情况下引用 this ,那么 this 将引用外部范围......这将是 window 对象 .

    var x = 9   //this is really window.x
    var bar = {
      x: 1,
      y: 2,
      foo: new function(){
        this.a = 5, //assign value
        this.b = 6,
        this.c = this.a + this.b;  // 11
      },
      z: this.x   // 9 (not 1 as you might expect, b/c *this* refers `window` object)
    };
    
  • 4

    这里发布的其他答案更好,但这里有一个替代方案:

    • 设置初始化时的值(不是getter或派生等)

    • 不需要任何类型的 init() 或对象文字之外的代码

    • 是对象文字,而不是工厂函数或其他对象创建机制 .

    • 不应对性能产生任何影响(初始化除外)

    Self-executing anonymous functions and window storage

    var foo = {
        bar:(function(){
            window.temp = "qwert";
            return window.temp;
        })(),
        baz: window.temp
    };
    

    订单是guaranteed(在 baz 之前 bar ) .

    它当然会污染 window ,但我无法想象有人编写需要 window.temp 持久的脚本 . 如果你是偏执狂,也许 tempMyApp

    它也很难看,但偶尔也很有用 . 例如,当您使用具有严格初始化条件的API并且不想重构时,因此范围是正确的 .

    当然,它很干燥 .

  • 1

    如果您的对象被写为返回对象的函数,并且您使用ES6对象属性'methods',则可能:

    const module = (state) => ({
      a: 1,
      oneThing() {
        state.b = state.b + this.a
      },
      anotherThing() {
        this.oneThing();
        state.c = state.b + this.a
      },
    });
    
    const store = {b: 10};
    const root = module(store);
    
    root.oneThing();
    console.log(store);
    
    root.anotherThing();
    console.log(store);
    
    console.log(root, Object.keys(root), root.prototype);
    
  • 0

    这是一个整洁的ES6方式:

    var foo = (o => ({
        ...o,
        c: o.a + o.b
      }))({
        a: 5,
        b: 6
      });
      
    console.log(foo);
    

    我用它来做这样的事情:

    const constants = Object.freeze(
      (_ => ({
        ..._,
        flag_data: {
          [_.a_flag]: 'foo',
          [_.b_flag]: 'bar',
          [_.c_flag]: 'oof'
        }
      }))({
        a_flag: 5,
        b_flag: 6,
        c_flag: 7,
      })
    );
    
    console.log(constants.flag_data[constants.b_flag]);
    
  • 59

    这个解决方案怎么样也适用于带有数组的嵌套对象

    Object.prototype.assignOwnProVal
         = function (to,from){ 
                function compose(obj,string){ 
                    var parts = string.split('.'); 
                    var newObj = obj[parts[0]]; 
                    if(parts[1]){ 
                        parts.splice(0,1);
                        var newString = parts.join('.'); 
                        return compose(newObj,newString); 
                    } 
                    return newObj; 
                } 
                this[to] = compose(this,from);
         } 
         var obj = { name : 'Gaurav', temp : 
                      {id : [10,20], city:
                            {street:'Brunswick'}} } 
         obj.assignOwnProVal('street','temp.city.street'); 
         obj.assignOwnProVal('myid','temp.id.1');
    
  • 578

    我使用以下代码作为替代,它的工作原理 . 变量也可以是数组 . (@ Fausto R.)

    var foo = {
      a: 5,
      b: 6,
      c: function() {
        return this.a + this.b;
      },
    
      d: [10,20,30],
      e: function(x) {
        this.d.push(x);
        return this.d;
      }
    };
    foo.c(); // 11
    foo.e(40); // foo.d = [10,20,30,40]
    
  • 18

    投入选项,因为我没有看到这个确切的情况 . 如果您不想在 ab 更新时更新 c ,则ES6 IIFE运行良好 .

    var foo = ((a,b) => ({
        a,
        b,
        c: a + b
    }))(a,b);
    

    根据我的需要,我有一个与最终用于循环的数组有关的对象,所以我只想计算一次常用设置,所以这就是我所拥有的:

    let processingState = ((indexOfSelectedTier) => ({
          selectedTier,
          indexOfSelectedTier,
          hasUpperTierSelection: tiers.slice(0,indexOfSelectedTier)
                                 .some(t => pendingSelectedFiltersState[t.name]),
      }))(tiers.indexOf(selectedTier));
    

    因为我需要为 indexOfSelectedTier 设置一个属性,我需要在设置 hasUpperTierSelection 属性时使用该值,我首先计算该值并将其作为参数传递给IIFE

  • 7

    另一种方法是保持原始对象的属性,使用第一个对象属性的动态属性创建第二个对象,然后使用spread进行合并 .

    所以:

    Object

    var foo = {
        a: 5,
        b: 6,
    };
    

    Object 2

    var foo2 = {
        c: foo.a + foo.b
    };
    

    Merge

    Object.assign(foo, foo2);
    

    要么

    foo = {...foo, ...foo2};
    

    Result of foo:

    { 
      "a":5,
      "b":6,
      "c":11
    }
    

    所有在同一个js内,对象内没有新功能,只使用赋值或传播 .

  • 2

    注意:此解决方案使用Typescript(如果需要,您可以使用TS编译的vanilla JS)

    class asd {
        def = new class {
            ads= 'asd';
            qwe= this.ads + '123';
        };
    
        // this method is just to check/test this solution 
        check(){
            console.log(this.def.qwe);
        }
    }
    
    // these two lines are just to check
    let instance = new asd();
    instance.check();
    

    这里使用类表达式来获取我们想要的嵌套对象文字界面 . 这是IMHO在创建过程中能够引用对象属性的下一个最好的事情 .

    需要注意的主要事项是在使用此解决方案时,您拥有与对象文字完全相同的界面 . 语法非常接近对象文字本身(与使用函数等) .

    比较以下内容

    我提出的解决方案

    class asd {
        def = new class {
            ads= 'asd';
            qwe= this.ads + '123';
        };
    

    解决方案,如果对象文字已经足够了

    var asd = {
        def : {
            ads:'asd',
            qwe: this.ads + '123';, //ILLEGAL CODE; just to show ideal scenario
        }
    }
    

    另一个例子

    在这个类中,您可以在它们之间组合多个相对路径,这对于对象文字是不可能的 .

    class CONSTANT {
        static readonly PATH = new class {
            /** private visibility because these relative paths don't make sense for direct access, they're only useful to path class
             *
             */
            private readonly RELATIVE = new class {
                readonly AFTER_EFFECTS_TEMPLATE_BINARY_VERSION: fs.PathLike = '\\assets\\aep-template\\src\\video-template.aep';
                readonly AFTER_EFFECTS_TEMPLATE_XML_VERSION: fs.PathLike = '\\assets\\aep-template\\intermediates\\video-template.aepx';
                readonly RELATIVE_PATH_TO_AFTER_EFFECTS: fs.PathLike = '\\Adobe\\Adobe After Effects CC 2018\\Support Files\\AfterFX.exe';
                readonly OUTPUT_DIRECTORY_NAME: fs.PathLike = '\\output';
                readonly INPUT_DIRECTORY_NAME: fs.PathLike = '\\input';
                readonly ASSETS_DIRECTORY_NAME: fs.PathLike = '\\assets';
            };
        }
    }
    

相关问题