Javascript 对象文本/初始值设定项中的自引用

Javascript 对象文本/初始值设定项中的自引用,javascript,object-literal,Javascript,Object Literal,有没有办法让下面这样的东西在JavaScript中工作 var foo = { a: 5, b: 6, c: this.a + this.b // Doesn't work }; 在当前表单中,这段代码显然抛出了一个引用错误,因为它没有引用foo。但是,有没有办法让对象文本的属性中的值依赖于前面声明的其他属性?那么,我唯一能告诉您的是: 变量foo={ a:5, b:6, 得到c{ 返回这个.a+这个.b; } } console.logfoo.c//11您可以执行

有没有办法让下面这样的东西在JavaScript中工作

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

在当前表单中,这段代码显然抛出了一个引用错误,因为它没有引用foo。但是,有没有办法让对象文本的属性中的值依赖于前面声明的其他属性?

那么,我唯一能告诉您的是:

变量foo={ a:5, b:6, 得到c{ 返回这个.a+这个.b; } }
console.logfoo.c//11您可以执行以下操作:

var foo = {
   a: 5,
   b: 6,
   init: function() {
       this.c = this.a + this.b;
       return this;
   }
}.init();
这将是对象的某种一次性初始化


请注意,您实际上是在将init的返回值赋给foo,因此必须返回它。

有几种方法可以实现这一点;这就是我要使用的:

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

缺少显而易见的简单答案,因此为了完整性:

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

不可以。这里的所有解决方案都将其延迟到以各种方式创建对象之后,然后指定第三个属性。最简单的方法就是这样做:

var foo = {
    a: 5,
    b: 6
};
foo.c = foo.a + foo.b;
所有其他方法都只是做同样事情的更间接的方法。Felix特别聪明,但需要创建和销毁临时函数,增加复杂性;并在对象上留下一个额外属性,或者[如果删除该属性]该对象上后续属性访问的属性

如果需要将其全部放在一个表达式中,则可以不使用临时属性:

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);

您可以使用模块模式来完成。就像:

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对象


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

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

你可以这样做

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定义为包含打印函数的对象,可以允许函数引用该对象。这意味着,如果需要将打印函数传递到其他地方,则不必将其“绑定”到对象

如果您愿意,请按如下所示使用此选项

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将始终返回相同的对象,而不管函数在哪个上下文中运行。当使用CreateMObject的自版本时,上面的代码将正常运行并记录0、1、2和3

一些闭包应该解决这个问题

var foo = function() {
    var a = 5;
    var b = 6;
    var c = a + b;

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

然后在最后执行该函数,该函数将计算整个foo对象,实例化其中的所有变量,并将返回对象添加为foo的属性。

所有这些的关键是作用域

您需要将要定义的属性的父对象封装为自己的实例化对象,然后可以使用关键字this引用同级属性

非常非常重要的是要记住,如果你没有先这么做就引用这个,那么这将引用外部范围。。。这将是窗口对象


在对象文本上创建新函数并调用构造函数似乎与原来的问题完全不同,这是不必要的

在对象文字初始化期间不能引用同级属性

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

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

在初始化时设置值,而不是getter或派生值等 不需要对象文本之外的任何类型的init或代码 是对象文字,而不是工厂函数或其他对象创建机制。 除了在初始化时,不应有任何性能影响 自动执行匿名函数和窗口存储

订单是在baz之前发出的

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

它也很难看,但偶尔也有用。例如,当您使用具有严格初始化条件的API时,不想进行重构,因此作用域是正确的

而且是干的,
当然。

只是为了思考-将对象的属性从时间线中放置:

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

console.log(foo.c())
上面也有更好的答案。这就是我如何修改您质疑的示例代码的方法

更新:


作为补充,在ES6中,我们在编写本文时只支持最新浏览器的类,但在Babel、TypeScript和其他Transpiler中也可以使用

class Foo {
  constructor(){
    this.a = 5;
    this.b = 6;
    this.c = this.a + this.b;
  }  
}

const foo = new Foo();

我使用下面的代码作为替代,它可以工作。变量也可以是数组。@福斯托河

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]

如果将对象编写为返回对象的函数,并且使用ES6对象属性“方法”,则可能:

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);

现在在ES6中,您可以创建延迟缓存属性。第一次使用时,该属性计算一次,成为普通静态属性。结果:第二次跳过数学函数开销

魔法就在吸气剂中

const foo = {
    a: 5,
    b: 6,
    get c() {
        delete this.c;
        return this.c = this.a + this.b
    }
};
在“箭头获取器”中,这将拾取箭头


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

      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');

因为我没有看到这个确切的场景,所以加入了一个选项。如果您不希望在a或b更新时更新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

var foo=o=>{ ……哦, c:o.a+o.b }{ a:5, b:6 }; console.logfoo; 注意:此解决方案使用Typescript,如果需要,您可以使用TS编译到的vanilla JS

这里我们使用类表达式来获得我们想要的嵌套对象文本接口。这是在创建过程中能够引用对象属性的次佳选择

需要注意的主要问题是,在使用此解决方案时,您拥有与从对象文本获得的接口完全相同的接口。语法非常接近对象文字本身,而不是使用函数等等

比较下列各项 我提出的解决方案 解决方案,如果对象文字足够的话 另一个例子 在这个类中,您可以在它们之间组合多个相对路径,这在对象文本中是不可能的

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';
        };
    }
}

如果您想使用原生JS,其他答案提供了很好的解决方案

但如果您愿意编写自引用对象,如:

{ 
  a: ...,
  b: "${this.a + this.a}",
}

我编写了一个名为npm的库,它支持该语法并返回一个本机对象。

另一种方法是在将属性分配给对象之前先声明对象:

const foo = {};
foo.a = 5;
foo.b = 6;
foo.c = foo.a + foo.b;  // Does work
foo.getSum = () => foo.a + foo.b + foo.c;  // foo.getSum() === 22
这样,就可以使用对象变量名访问已分配的值。 最适合config.js文件

这与@slicedtoad的答案几乎相同,但不使用函数。

get属性工作得很好,您还可以对昂贵的函数使用绑定闭包,这些函数应该只运行一次,这只适用于var,而不适用于const或let

变量信息={ 地址:功能{ 返回databaseLookupthis.id }.bindinfo, 获取全名{ console.log'computing fullName…' 返回`${this.first}${this.last}` }, id:'555-22-9999', 第一:"第一",, last:‘last’, } 函数数据库查找{ console.log“从远程服务器获取地址运行一次…” 回报承诺。决议'22主街,城市,国家` } //试验 异步=>{ console.loginfo.fullName console.loginfo.fullName console.logawait info.address console.logawait info.address console.logawait info.address console.logawait info.address }两个懒惰的解决方案 这里已经有很好的答案,我不是这方面的专家,但我是懒惰方面的专家,在我的专家眼里,这些答案似乎不够懒惰

第一:从匿名函数返回对象 与以下内容和答案的细微差异:

var foo==>{ //粘贴到原始对象中 常数foo={ a:5, b:6, }; //使用它们的属性 foo.c=foo.a+foo.b; //你还想做什么就做什么 //最后,返回对象 返回foo; };
console.logfoo;这是可行的,但消除了对象文字符号的优点。我通常只在数据结构中使用对象文字,任何时候我想要任何类似于类的附加逻辑,我都会基于这个原因创建对象作为函数的结果poluted@BillyMoon:是的,尽管对该对象的所有后续属性访问都是这样做的,但在许多引擎V8上都是如此。@MuhammadUmer:不确定ES6类与这个问题有什么关系。@MuhammadUmer:类只是构造函数的语法糖,所以它们并没有提供任何新的东西。不管怎样,这个问题的主要焦点都是o

object-literals@akantoword:Great:由于object-literals是一个表达式,因此init调用直接追加该文本以保持其为一个表达式。但是当然你可以单独调用你想调用的函数。非常有用的答案。有关“get”的更多信息可在此处找到:注意,使用此解决方案,如果foo.a或foo.b的值发生更改,那么foo.c的值也将同步更改。这可能是必需的,也可能不是。@HBP这与问题中发生的情况完全相同,因此在我看来,这正是预期结果。请注意,这绑定到最深的嵌套对象。例如:。。。x:{get c{/*这是x,不是foo*/}……为了完成我的上述语句,由于foo被声明为变量,并且c仅在调用时才被计算,因此在c中使用foo将起作用,与此相反,请小心,尽管这不是模块模式的示例,只是一个函数。如果foo定义的最后一行是};,它将自执行并返回一个对象,而不是一个函数。此外,foo.c是一个函数,因此写入它会破坏该函数,并且下一次通过fooObject.c调用将失败。也许这更接近你想要的,它也是一个单例,不是为了被实例化而设计的。模块模式最初被定义为一种在传统软件工程中为类提供私有和公共封装的方式。发件人:。这是一个遵循上面描述的模块模式的对象,但可能它不是最好的解释,因为它没有显示公共和私有属性/方法。此对象与具有公共和私有属性/方法的对象相同。因此,他们都遵循这一模式——称之为闭包是令人困惑和误导的。尽管对于从函数返回ojbect值的确切含义众说纷纭,但在任何人的书中都不构成闭包。很好的示例,只是您省略了所有分号。没有分号–这甚至是无效的。@Bergi,为什么?因为有人可能会从中实例化另一个相同的对象?这并不是说他们不能克隆一个对象文字。这与传递参数new Pointx,y没有什么不同,只是函数的名称不是为了重用。@zzzzBov:当然,它们可以克隆对象,但与TJCrowder的回答中的IEFE解决方案相比,您的解决方案会泄漏构造函数并创建多余的原型对象。@zzzzBov:只需使用var foo=function即可{this….;return this;}.call{};在语法上没有太大的不同,但在语义上是合理的。@Bergi,如果你觉得这很重要,为什么不在混合中添加你自己的答案呢?你已经得到了这个答案。我确实没有注意到新的关键字。在ES6中,你可以使这个通用方法更加优雅:var foo={get a{return 5},get b{return 6},get c{返回this.a+this.b}}所以现在您可以直接执行foo.c而不是foo.c:可以将其粘贴到您的答案中,这样格式就更好了!es5还有一些属性,您只需要使用Object.definePropertyfoo,'c',{get:function{…}定义它们。在像这样的工厂中,这很容易以一种不引人注目的方式完成。当然,如果你可以使用get sugar,它会更可读,但功能已经存在。这非常有效,但我可以知道为什么你要删除这个。c而它根本不存在?我尝试过不写删除这个。c但它对我自己的sani不起作用泰,我想找到一些官方文档,上面说的基本上是一样的——一个对象的这个只对该对象的方法可用,没有其他类型的属性。知道我在哪里可以找到它吗?谢谢!@DavidKennell:没有比.......更正式的了。你可能会开始并遵循它。这很简单kard语言,但基本上你会在它的各个子类中看到,对象对于确定属性初始值设定项的值的操作是不可用的。我看不到,但情况不再是这样了,对吗?在我的环境中,v8:delete快10%,gecko:delete慢1%。@TheMaster-是的,我不认为BrowserScope看起来删除已经不像以前那么糟糕了,至少在V8 Chrome等或SpiderMonkey中不是这样了。速度更慢,但只有一点点,而且这些东西现在快得离奇。可能是因为你的答案完全不相关吗?我同意选民应该解释,但你的答案清楚与否无关e问题…@Manngo谢谢你指出。老实说,我的问题与OP相同,我使用我建议的解决方案。不确定,为什么它被认为是不相关的。如果你有时间,请解释,这样我可以更好地回答,或者至少知道我错在哪里。不幸的是,我不理解为什么这不是一个合理的解决方案。这根本没有解决自我参照的问题。你所提出的是一种通过自我介绍来模拟自我参照的相当复杂的方法
请在代码中添加不必要的闭包。答案仅仅是一个外部网站的链接。@Quentin你对我如何改进我的答案有什么建议吗?这个问题的其他答案包括如何使用本机javascript编写自引用对象,但如果您希望使用与原始问题中的语法类似的语法编写自引用对象,我认为我编写的库可能对其他正在寻找解决方案的人有用。很高兴得到一些反馈。这里有几点需要改进。首先,也是最明显的一点,您使用的是没有反勾号的模板文本语法。b属性的值应为:${this.a+this.a}。其次,但不太重要的是,您希望通过使用类似parseInt的东西返回一个数字,而不是字符串。最后也是最重要的一点,当我尝试这个例子时,它根本不起作用,原因与OP的要求相同。当使用自己的对象声明时,this返回未定义@亚历克斯-leon@AlecDonaldMather-感谢您抽出时间查看并提供一些反馈!如果您对该项目感兴趣,最好将讨论转移到github,但要回答您的一些反馈:-使用backticks:正如前面的评论中提到的,JS不支持这种语法,因此,这里需要使用字符串而不是反勾号,以避免js在定义obj之前试图解决这个问题-返回一个数字,如果a+b已经是数字,这应该可以工作,因为如果a和b都已经是数字,a+b将返回一个数字。如果返回的是未定义的,你能解释一下你是如何使用这个库的吗?这不应该发生,但也许我遗漏了一个边缘案例?也就是说,这个库不能完全解决这个问题,它有自己的一套折衷方案,但是如果您有兴趣帮助我改进/使用它,请告诉我!这不是自引用,而是对声明的变量foo的引用,该变量指向所讨论的对象。
let processingState = ((indexOfSelectedTier) => ({
    selectedTier,
    indexOfSelectedTier,
    hasUpperTierSelection: tiers.slice(0,indexOfSelectedTier)
                         .some(t => pendingSelectedFiltersState[t.name]),
}))(tiers.indexOf(selectedTier));
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();
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';
        };
    }
}
{ 
  a: ...,
  b: "${this.a + this.a}",
}
const foo = {};
foo.a = 5;
foo.b = 6;
foo.c = foo.a + foo.b;  // Does work
foo.getSum = () => foo.a + foo.b + foo.c;  // foo.getSum() === 22
var x = {
    a: (window.secreta = 5),
    b: (window.secretb = 6),
    c: window.secreta + window.secretb
};