การอ้างอิงตนเองในวัตถุตัวอักษร / ตัวเริ่มต้น


706

มีวิธีใดบ้างที่จะทำให้สิ่งต่อไปนี้ใน JavaScript ทำงานได้หรือไม่?

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

ในรูปแบบปัจจุบัน, รหัสนี้เห็นได้ชัดว่าข้อผิดพลาดพ่นอ้างอิงตั้งแต่ไม่ได้หมายถึงthis fooแต่เป็นมีวิธีใดที่จะมีค่าในคุณสมบัติตามตัวอักษรของวัตถุขึ้นอยู่กับคุณสมบัติอื่น ๆ ประกาศก่อนหน้านี้?

คำตอบ:


744

สิ่งเดียวที่ฉันบอกคุณได้คือทะเยอทะยาน :

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

console.log(foo.c) // 11

นี่เป็นส่วนขยายทางไวยากรณ์ที่แนะนำโดยข้อมูลจำเพาะ ECMAScript 5th Edition สนับสนุนไวยากรณ์โดยเบราว์เซอร์ที่ทันสมัยที่สุด (รวมถึง IE9)


32
คำตอบที่เป็นประโยชน์มาก ข้อมูลเพิ่มเติมเกี่ยวกับ 'รับ' สามารถดูได้ที่นี่: developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/
......

5
@FaustoR ใช่.
Charlie Martin

48
ระวังด้วยวิธีนี้ถ้าค่าของfoo.aหรือfoo.bมีการเปลี่ยนแปลงแล้วค่าของfoo.cจะเปลี่ยนในการซิงโครไนซ์ สิ่งนี้อาจจะใช่หรือไม่ใช่ก็ได้
HBP

8
โปรดทราบว่าthisผูกกับวัตถุที่ซ้อนกันที่ลึกที่สุด เช่น:... x: { get c () { /*this is x, not foo*/ } } ...
Z. Khullah

3
เพื่อทำให้ข้อความด้านบนของฉันสมบูรณ์เนื่องจากfooมีการประกาศผึ้งเป็นตัวแปรและcจะถูกประเมินในเวลาที่เรียกใช้การใช้fooภายในcจะทำงานเมื่อเทียบกับthis(โปรดระวังด้วย)
Z. Khullah

316

คุณสามารถทำสิ่งที่ชอบ:

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

นี่จะเป็นการเริ่มต้นครั้งเดียวของวัตถุ

โปรดทราบว่าคุณเป็นจริงการกำหนดค่าตอบแทนของinit()การดังนั้นคุณจะต้องfooreturn this


95
คุณยังสามารถdelete this.initก่อนหน้านี้return thisเพื่อที่fooจะไม่ถูกโพย
Billy Moon

15
@BillyMoon: ใช่แน่นอนแม้ว่าการทำเช่นนั้นจะส่งผลกระทบต่อประสิทธิภาพของการเข้าถึงคุณสมบัติที่ตามมาทั้งหมดบนวัตถุนั้นในเอ็นจินจำนวนมาก (เช่น V8)
TJ Crowder

8
@MuhammadUmer: ไม่แน่ใจว่าคลาส ES6 เกี่ยวข้องกับคำถามอย่างไร
เฟลิกซ์คลิง

8
@ MuhammadUmer: คลาสเป็นเพียงประโยคน้ำตาลสำหรับฟังก์ชั่นคอนสตรัคเตอร์ดังนั้นพวกเขาจึงไม่ได้ให้อะไรใหม่ ไม่ว่าจะด้วยวิธีใดจุดสนใจหลักของคำถามนี้คือตัวอักษรวัตถุ
เฟลิกซ์คลิง

3
@akantoword: ยอดเยี่ยม :) เนื่องจากตัวอักษรของวัตถุเป็นนิพจน์เดียวการinit()โทรจึงต่อท้ายตัวอักษรโดยตรงเพื่อให้เป็นนิพจน์เดียว แต่แน่นอนคุณสามารถเรียกใช้ฟังก์ชันแยกต่างหากได้ตามต้องการ
เฟลิกซ์คลิง

181

คำตอบที่ชัดเจนและเรียบง่ายหายไปดังนั้นเพื่อความสมบูรณ์:

แต่เป็นมีวิธีใดที่จะมีค่าในคุณสมบัติตามตัวอักษรของวัตถุขึ้นอยู่กับคุณสมบัติอื่น ๆ ประกาศก่อนหน้านี้?

ไม่ได้วิธีแก้ปัญหาทั้งหมดที่นี่เลื่อนออกไปจนกว่าจะสร้างวัตถุ (ในรูปแบบต่าง ๆ ) แล้วกำหนดคุณสมบัติที่สาม ง่ายวิธีที่เป็นเพียงการทำเช่นนี้:

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

คนอื่น ๆ ทั้งหมดเป็นเพียงทางอ้อมที่จะทำสิ่งเดียวกัน (เฟลิกซ์นั้นฉลาดเป็นพิเศษ แต่ต้องการสร้างและทำลายฟังก์ชั่นชั่วคราวเพิ่มความซับซ้อนและทิ้งคุณสมบัติพิเศษไว้บนวัตถุหรือ [ถ้าคุณdeleteคุณสมบัตินั้น] ส่งผลต่อประสิทธิภาพของการเข้าถึงคุณสมบัติที่ตามมาบนวัตถุนั้น)

หากคุณต้องการให้อยู่ภายในนิพจน์เดียวคุณสามารถทำได้โดยไม่ต้องมีคุณสมบัติชั่วคราว:

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

สำหรับความมีสติของตัวเองฉันพยายามค้นหาเอกสารทางการที่บอกว่าโดยทั่วไปในสิ่งเดียวกัน - ว่าวัตถุthisนั้นมีไว้สำหรับวิธีการของวัตถุดังกล่าวเท่านั้นและไม่มีคุณสมบัติอื่น ๆ ความคิดใดที่ฉันสามารถหาได้? ขอบคุณ!
David Kennell

1
@DavidKennell: ไม่ได้เป็นทางการมากกว่าสเปค :-) คุณอาจเริ่มต้นที่นี่และติดตามมัน มันเป็นภาษาที่ค่อนข้างอึดอัด แต่โดยทั่วไปคุณจะเห็นใน subclauses ต่างๆของProperty Definition Evaluationว่าวัตถุไม่พร้อมใช้งานสำหรับการดำเนินการกำหนดค่าของ initializers คุณสมบัติ
TJ Crowder

ฉันไม่เห็นผลลัพธ์ของเบราว์เซอร์สโคปที่นี่แต่สิ่งนี้ไม่ถูกต้องใช่ไหม ในสภาพแวดล้อมของฉัน v8: deleteเร็วขึ้น 10% และตุ๊กแก: deleteจะช้าลงเพียง 1%
TheMaster

1
@TheMaster - ใช่ฉันไม่คิดว่า BrowserScope จะเป็นอะไรอีกแล้ว ดูเหมือนว่าการลบจะไม่เลวร้ายอย่างที่เคยเป็นอย่างน้อยไม่ใช่ใน V8 (Chrome, ฯลฯ ) หรือ SpiderMonkey ยังคงช้าลง แต่เพียงเล็กน้อยและสิ่งเหล่านี้รวดเร็วอย่างประหลาดในวันนี้
TJ Crowder

63

เพียงยกตัวอย่างฟังก์ชั่นที่ไม่ระบุชื่อ:

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

1
@Bergi ทำไม เพราะบางคนอาจยกตัวอย่างวัตถุเดียวกันจากวัตถุอื่น ไม่ใช่ว่าพวกเขาไม่สามารถโคลนวัตถุตามตัวอักษร ไม่แตกต่างจากการส่งผ่านข้อโต้แย้งnew Point(x, y)ยกเว้นว่าฟังก์ชั่นไม่ได้ถูกตั้งชื่อเพื่อใช้ซ้ำ
zzzzBov

1
@zzzzBov: แน่นอนว่าพวกเขาสามารถโคลนวัตถุได้ แต่เมื่อเทียบกับโซลูชัน IEFE (เช่นในคำตอบของ TJCrowder) โซลูชันของคุณรั่วไหลฟังก์ชันตัวสร้างและสร้างวัตถุต้นแบบฟุ่มเฟือย
Bergi

5
@zzzzBov: เพียงใช้var foo = function() { this.…; return this; }.call({});ซึ่งเป็น syntactically ไม่แตกต่างกันมาก แต่มีสติความหมาย
Bergi

1
@Bergi ถ้าคุณรู้สึกว่ามันสำคัญมากทำไมไม่เพิ่มคำตอบของคุณเองลงไป
zzzzBov

3
คุณได้รับสิ่งนี้ แน่นอนฉันไม่ได้สังเกตเห็นnewคำหลัก
แรนดี้

26

ตอนนี้ใน ES6 คุณสามารถสร้างคุณสมบัติแคชขี้เกียจ ในการใช้งานครั้งแรกคุณสมบัติประเมินหนึ่งครั้งเพื่อกลายเป็นคุณสมบัติคงที่ปกติ ผลลัพธ์: ครั้งที่สองที่ฟังก์ชันโอเวอร์เฮดของฟังก์ชันคณิตศาสตร์ถูกข้าม

ความมหัศจรรย์อยู่ในทะเยอทะยาน

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

ในลูกศรทะเยอทะยานthisหยิบขึ้นขอบเขตคำศัพท์รอบ

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

1
es5 ยังมีคุณสมบัติที่คุณต้องใช้Object.defineProperty(foo, 'c', {get:function() {...}})เพื่อกำหนด สิ่งนี้ทำได้ง่ายในลักษณะที่ไม่เป็นการรบกวนในโรงงานเช่นโรงงานแห่งนี้ แน่นอนถ้าคุณสามารถใช้getน้ำตาลก็อ่านได้ง่ายขึ้น แต่มีความสามารถอยู่ที่นั่น
Aluan Haddad

21

การปิดบางอย่างควรจัดการกับสิ่งนี้;

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

    return {
        a: a,
        b: b,
        c: c
    }
}();

ตัวแปรทั้งหมดที่ประกาศไว้ภายในfooเป็นแบบส่วนตัวfooเช่นเดียวกับที่คุณคาดหวังกับการประกาศฟังก์ชั่นใด ๆ และเนื่องจากพวกเขาอยู่ในขอบเขตพวกเขาทั้งหมดมีการเข้าถึงซึ่งกันและกันโดยไม่จำเป็นต้องอ้างถึงthisเช่นเดียวกับที่คุณคาดหวังกับฟังก์ชั่น แตกต่างก็คือฟังก์ชั่นนี้ส่งกลับวัตถุที่ exposes fooตัวแปรส่วนตัวและกำหนดว่าวัตถุไปยัง ในที่สุดคุณจะกลับมาเพียงอินเทอร์เฟซที่คุณต้องการเปิดเผยเป็นวัตถุที่มีreturn {}คำสั่ง

ฟังก์ชั่นจะถูกดำเนินการแล้วในตอนท้ายด้วย()ซึ่งเป็นสาเหตุของวัตถุ foo ทั้งหมดจะได้รับการประเมินตัวแปรทั้งหมดภายใน instantiated foo()และวัตถุผลตอบแทนเพิ่มเป็นสมบัติของ


14
มันทำให้สับสนและทำให้เข้าใจผิดว่านี่เรียกว่า "การปิด" แม้ว่าความคิดเห็นที่แตกต่างกันไปในความหมายที่แม่นยำที่ส่งคืนค่า ojbect จากฟังก์ชั่นไม่ได้เป็นการปิดในหนังสือของทุกคน

16

คุณสามารถทำสิ่งนี้ได้

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

โดยการกำหนดตัวเองเป็นวัตถุที่มีฟังก์ชั่นการพิมพ์ที่คุณอนุญาตให้ฟังก์ชั่นเพื่ออ้างถึงวัตถุนั้น ซึ่งหมายความว่าคุณจะไม่ต้อง 'ผูก' ฟังก์ชั่นการพิมพ์ไปยังวัตถุหากคุณต้องการส่งผ่านที่อื่น

หากคุณต้องการใช้แทน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!

ด้วยการใช้วิธีการด้วยตนเองคุณรับประกันได้ว่าการพิมพ์จะส่งคืนวัตถุเดียวกันเสมอโดยไม่คำนึงถึงบริบทที่เรียกใช้ฟังก์ชัน โค้ดข้างต้นจะทำงานได้ดีและเข้าสู่ระบบ 0, 1, 2 และ 3 createMyObject()เมื่อใช้รุ่นของตัวเอง


3
ตัวอย่างที่ดียกเว้นคุณเว้นเครื่องหมายอัฒภาคไว้หมด

1
ไม่มีอัฒภาค - มัน ดี จริงๆ!
Kerem Baydoğan

9

เพื่อให้เสร็จสมบูรณ์ใน ES6 เรามีคลาส (รองรับในขณะที่เขียนเฉพาะเบราว์เซอร์ล่าสุดเท่านั้น แต่มีใน Babel, TypeScript และ transpilers อื่น ๆ )

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

const foo = new Foo();

7

คุณสามารถทำได้โดยใช้รูปแบบโมดูล เหมือนกับ:

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

ด้วยรูปแบบนี้คุณสามารถยกตัวอย่างวัตถุหลายอย่างตามที่คุณต้องการ

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


2
นี่ไม่ใช่ตัวอย่างของรูปแบบโมดูลเพียงแค่ฟังก์ชั่น หากบรรทัดสุดท้ายของคำจำกัดความของ foo คือ}();มันจะดำเนินการด้วยตนเองและส่งคืนวัตถุไม่ใช่ฟังก์ชัน นอกจากนี้ยังfoo.cมีฟังก์ชั่นดังนั้นการเขียนไปที่มันจะปิดการทำงานและการเรียกใช้ครั้งต่อไปผ่านทางfooObject.c()จะล้มเหลว บางทีซอนี้มีความใกล้เคียงกับสิ่งที่คุณกำลังทำอยู่ (มันเป็นซิงเกิลตันที่ไม่ได้ออกแบบมาเพื่อให้เป็นอินสแตนซ์)
Hollister

2
"รูปแบบของโมดูลได้รับการนิยามให้เป็นวิธีการห่อหุ้มทั้งส่วนบุคคลและสาธารณะสำหรับคลาสในวิศวกรรมซอฟต์แวร์ทั่วไป" จาก: รูปแบบการเรียนรู้การออกแบบ JavaScript นั่นเป็นวัตถุตามรูปแบบของโมดูลที่อธิบายไว้ข้างต้น แต่อาจไม่ใช่วิธีที่ดีที่สุดที่จะอธิบายว่าเพราะไม่แสดงคุณสมบัติ / วิธีการของสาธารณะและส่วนตัว หนึ่งjsfiddle.net/9nnR5/2นี้เป็นวัตถุเดียวกันกับคุณสมบัติสาธารณะและวิธีการ / สาธารณะ ดังนั้นทั้งคู่จึงติดตามรูปแบบนี้
ราฟาเอลโรชา

6

มีหลายวิธีในการบรรลุเป้าหมายนี้ นี่คือสิ่งที่ฉันจะใช้:

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

2
ใช้งานได้ แต่กำจัดข้อดีของสัญกรณ์ตัวอักษร
kpozin

จริงขอโทษฉันพลาดแท็กตัวอักษรในตอนแรก ฉันส่วนใหญ่จะใช้ตัวอักษรวัตถุสำหรับโครงสร้างข้อมูลและทุกครั้งที่ฉันต้องการตรรกะเพิ่มเติมใด ๆ (ซึ่งอาจคล้ายคลาส) ฉันสร้างวัตถุเป็นผลมาจากฟังก์ชั่นด้วยเหตุผลอย่างนี้
เคน

6

เพื่อประโยชน์ในการคิด - วางคุณสมบัติของวัตถุออกจากไทม์ไลน์:

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

2
ใน ES6 คุณสามารถทำให้วิธีการทั่วไปนี้สวยงามยิ่งขึ้น: var foo = { get a(){return 5}, get b(){return 6}, get c(){return this.a + this.b} }ดังนั้นตอนนี้คุณสามารถทำได้foo.cแทนที่จะเป็นfoo.c():) (อย่าลังเลที่จะวางลงในคำตอบของคุณเพื่อการจัดรูปแบบดีกว่า!)

5

การสร้างฟังก์ชั่นใหม่บนวัตถุของคุณตามตัวอักษรและเรียกใช้คอนสตรัคเตอร์ดูเหมือนว่าจะเกิดจากปัญหาเดิมและไม่จำเป็น

คุณไม่สามารถอ้างอิงคุณสมบัติพี่น้องในระหว่างการเริ่มต้นวัตถุตามตัวอักษร

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

3

คำตอบอื่น ๆ ที่โพสต์ที่นี่ดีกว่า แต่นี่เป็นทางเลือกที่:

  • ตั้งค่าที่การเริ่มต้น (ไม่ใช่ getter หรือที่ได้รับ ฯลฯ )
  • ไม่ต้องใช้init()รหัสหรือประเภทใด ๆนอกวัตถุตามตัวอักษร
  • เป็นวัตถุตามตัวอักษรและไม่ใช่ฟังก์ชั่นจากโรงงานหรือกลไกการสร้างวัตถุอื่น ๆ
  • ไม่ควรมีผลกระทบต่อประสิทธิภาพ (ยกเว้นในการเริ่มต้น)

ฟังก์ชั่นที่ไม่ระบุชื่อดำเนินการเองและจัดเก็บข้อมูลหน้าต่าง

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

รับประกันการสั่งซื้อ( barก่อนbaz)

windowแน่นอนว่ามันเป็นมลพิษแต่ฉันไม่สามารถจินตนาการได้ว่ามีคนเขียนสคริปต์ที่ต้องwindow.tempขัดขืน บางทีtempMyAppถ้าคุณหวาดระแวง

มันก็น่าเกลียด แต่มีประโยชน์เป็นครั้งคราว ตัวอย่างคือเมื่อคุณใช้ API ที่มีเงื่อนไขการกำหนดค่าเริ่มต้นที่เข้มงวดและไม่รู้สึกว่าต้องทำการปรับโครงสร้างใหม่

และมันแห้งแน่นอน


3

กุญแจสำคัญในการทั้งหมดนี้คือขอบเขต

คุณจำเป็นต้องแค็ปซูล "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)
};

2
นี่ไม่ใช่แม้แต่ JS ที่ถูกต้อง

2

ถ้าวัตถุของคุณถูกเขียนเป็นฟังก์ชันซึ่งส่งคืนวัตถุและคุณใช้ ES6 object-attribute 'method' ก็เป็นไปได้:

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

2

ฉันใช้รหัสต่อไปนี้เป็นทางเลือกและทำงานได้ และตัวแปรสามารถเป็นอาร์เรย์ได้เช่นกัน (@ 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]

2

นี่คือวิธี 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]);


1

วิธีการแก้ปัญหานี้จะทำงานกับวัตถุที่ซ้อนกันกับอาร์เรย์เช่นกัน

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

0

โยนตัวเลือกเนื่องจากฉันไม่เห็นภาพที่แน่นอนนี้ครอบคลุม หากคุณไม่ต้องการcอัปเดตเมื่อใดaหรือbอัปเดตแสดงว่า 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


0

วิธีอื่นจะเป็นการประกาศวัตถุก่อนที่จะกำหนดคุณสมบัติเข้าไป:

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ไฟล์


นั่นไม่ใช่การอ้างอิงตนเอง แต่เป็นการอ้างอิงถึงตัวแปรที่ประกาศfooซึ่งชี้ไปยังวัตถุที่เป็นปัญหา
Derek Henderson


-1

หมายเหตุ:วิธีนี้ใช้ typescript (คุณสามารถใช้วานิลลา JS ซึ่ง TS รวบรวมเพื่อหากจำเป็น)

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

2
อาจเป็นเพราะคำตอบของคุณไม่เกี่ยวข้องทั้งหมด? ฉันยอมรับว่า downvoters ควรอธิบาย แต่คำตอบของคุณชัดเจนไม่มีอะไรเกี่ยวข้องกับคำถาม…
Manngo

@ Mango ขอบคุณสำหรับการชี้ให้เห็น สุจริตฉันต้องการคำถามเดียวกับ OP และฉันใช้โซลูชันที่ฉันแนะนำ ไม่แน่ใจว่าทำไมจึงถูกพิจารณาว่าไม่เกี่ยวข้อง หากคุณมีเวลาโปรดอธิบายเพื่อให้ฉันสามารถตอบได้ดีขึ้นหรืออย่างน้อยก็รู้ว่าฉันผิดตรงไหน ฉันโชคไม่ดีที่ไม่เข้าใจว่าทำไมนี่จึงไม่ใช่ทางออกที่สมเหตุสมผล
Dheeraj Bhaskar

-2

หากคุณต้องการใช้ Native JS คำตอบอื่น ๆ จะให้คำตอบที่ดี

แต่ถ้าคุณยินดีที่จะเขียนวัตถุอ้างอิงตนเองเช่น:

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

ฉันเขียนไลบรารี npm ที่เรียกว่าself-Reference-objectที่สนับสนุนไวยากรณ์นั้นและส่งคืนอ็อบเจกต์ดั้งเดิม


1
โปรดเชื่อมโยงหลีกเลี่ยงคำตอบเท่านั้น คำตอบที่ "แทบจะไม่มากกว่าการเชื่อมโยงไปยังเว็บไซต์ภายนอก” อาจถูกลบ .
เควนติน

@Quentin คุณมีข้อเสนอแนะใด ๆ เกี่ยวกับวิธีที่ฉันสามารถปรับปรุงคำตอบของฉันได้อย่างไร คำตอบอื่น ๆ สำหรับคำถามนี้ครอบคลุมถึงวิธีการที่คุณสามารถเขียนออบเจ็กต์อ้างอิงตนเองในจาวาสคริปต์ดั้งเดิม แต่ถ้าคุณต้องการเขียนออบเจ็กต์อ้างอิงตนเองด้วยไวยากรณ์คล้ายกับวากยสัมพันธ์ในคำถามต้นฉบับโปสเตอร์ฉันคิดว่าห้องสมุดที่ ฉันเขียนอาจเป็นประโยชน์กับคนอื่นที่กำลังมองหาวิธีแก้ปัญหา มีความสุขที่ได้รับข้อเสนอแนะบางอย่าง
alex-e-leon

สิ่งที่ต้องปรับปรุงที่นี่ อย่างแรกสุดและชัดเจนที่สุดคือคุณกำลังใช้ไวยากรณ์ตัวอักษรเทมเพลตโดยไม่มีการย้อนกลับ ของคุณมูลค่าทรัพย์สินควรจะเป็น:b ${this.a + this.a}ประการที่สอง parseIntแต่น้อยที่สำคัญที่คุณต้องการที่จะกลับมาเป็นจำนวนไม่สตริงโดยใช้สิ่งที่ต้องการ สุดท้ายและที่สำคัญที่สุดเมื่อฉันลองตัวอย่างนี้มันใช้งานไม่ได้ด้วยเหตุผลเดียวกันที่ OP ขอ thisผลตอบแทนที่ไม่ได้กำหนดเมื่อใช้การประกาศวัตถุของตัวเอง @ alex-e-leon
Alec Donald Mather

@AlecDonaldMather - ขอบคุณที่สละเวลาในการตรวจสอบและให้ข้อเสนอแนะบางอย่าง! หากคุณสนใจในโครงการคุณควรย้ายการอภิปรายนี้ไปยัง GitHub แต่จะตอบข้อเสนอแนะของคุณบางส่วน: - การใช้ backticks: ดังที่ได้กล่าวไว้ในความคิดเห็นก่อนหน้านี้ไวยากรณ์นี้ไม่สนับสนุน JS และใช้สตริงแทน ของ backticks จำเป็นต้องใช้ที่นี่เพื่อหลีกเลี่ยง js ที่พยายามแก้ไข "นี้" ก่อนที่ obj จะถูกกำหนด - คืนค่าจำนวนนี้ควรใช้งานถ้า a + b เป็นตัวเลขอยู่แล้วเนื่องจาก a + b จะคืนค่าตัวเลขถ้า a และ b เป็น ตัวเลขอยู่แล้ว
alex-e-leon

การกลับมาที่ไม่ได้กำหนดคุณสามารถอธิบายวิธีการใช้ห้องสมุดได้อย่างไร สิ่งนี้จะไม่เกิดขึ้น แต่อาจมีกรณีที่ฉันได้ออกไป? ที่กล่าวว่าห้องสมุดนี้ไม่สามารถแก้ปัญหาได้อย่างสมบูรณ์และมีชุดแลกเปลี่ยนเป็นของตัวเอง แต่ถ้าคุณสนใจที่จะช่วยฉันปรับปรุงมัน / ใช้มันให้ฉันรู้!
alex-e-leon
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.