객체 리터럴 / 이니셜 라이저의 자체 참조
JavaScript에서 다음과 같은 작업을 수행하는 방법이 있습니까?
var foo = {
a: 5,
b: 6,
c: this.a + this.b // Doesn't work
};
현재 형식에서이 코드는를 참조 this
하지 않기 때문에 분명히 참조 오류를 발생시킵니다 foo
. 그러나 인 객체 리터럴의 속성 값을 가질 수있는 방법이 선언 이전이 다른 속성에 따라 달라집니다?
글쎄요, 제가 말할 수있는 것은 게터뿐입니다.
var foo = {
a: 5,
b: 6,
get c() {
return this.a + this.b;
}
}
console.log(foo.c) // 11
이것은 ECMAScript 5th Edition Specification에 도입 된 구문 확장이며, 구문은 대부분의 최신 브라우저 (IE9 포함)에서 지원됩니다.
다음과 같이 할 수 있습니다.
var foo = {
a: 5,
b: 6,
init: function() {
this.c = this.a + this.b;
return this;
}
}.init();
이것은 객체의 일종의 일회성 초기화입니다.
실제로의 반환 값을 init()
에 할당하고 foo
있으므로 return this
.
명확하고 간단한 대답이 누락되었으므로 완전성을 위해 :
그러나 인 객체 리터럴의 속성 값을 가질 수있는 방법이 선언 이전이 다른 속성에 따라 달라집니다?
아니요. 여기에있는 모든 솔루션은 개체가 생성 된 후 (다양한 방식으로) 세 번째 속성을 할당 할 때까지이를 연기합니다. 가장 간단한 방법은이 작업을 수행하는 것입니다 :
var foo = {
a: 5,
b: 6
};
foo.c = foo.a + foo.b;
다른 모든 것은 동일한 작업을 수행하는 더 간접적 인 방법입니다. (Felix는 특히 영리하지만 임시 함수를 만들고 파괴하여 복잡성을 추가해야합니다. 객체에 추가 속성을 남기거나 [ 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);
익명 함수를 인스턴스화하기 만하면됩니다.
var foo = new function () {
this.a = 5;
this.b = 6;
this.c = this.a + this.b;
};
이제 ES6에서 지연 캐시 된 속성을 만들 수 있습니다. 처음 사용할 때 속성은 한 번 평가되어 일반 정적 속성이됩니다. 결과 : 두 번째로 수학 함수 오버 헤드를 건너 뜁니다.
마법은 게터에 있습니다.
const foo = {
a: 5,
b: 6,
get c() {
delete this.c;
return this.c = this.a + this.b
}
};
화살표에서 getter this
는 주변 어휘 범위를 선택합니다 .
foo // {a: 5, b: 6}
foo.c // 11
foo // {a: 5, b: 6 , c: 11}
일부 종결은 이것을 처리해야합니다.
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()
.
당신은 이렇게 할 수 있습니다
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를 인쇄 함수를 포함하는 개체로 정의하면 함수가 해당 개체를 참조 할 수 있습니다. 즉, 다른 곳에 전달해야하는 경우 인쇄 기능을 객체에 '바인딩'할 필요가 없습니다.
대신 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가 항상 동일한 객체를 반환하도록 보장합니다. 위의 코드는 .NET의 자체 버전을 사용할 때 잘 실행되고 0, 1, 2 및 3을 기록 createMyObject()
합니다.
완성을 위해 ES6에는 클래스가 있습니다 (이를 작성하는 시점에는 최신 브라우저에서만 지원되지만 Babel, TypeScript 및 기타 트랜스 파일러에서 사용 가능)
class Foo {
constructor(){
this.a = 5;
this.b = 6;
this.c = this.a + this.b;
}
}
const foo = new Foo();
모듈 패턴을 사용하여 할 수 있습니다. 처럼:
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 객체를 인스턴스화 할 수 있습니다.
이를 수행하는 방법에는 여러 가지가 있습니다. 이것이 내가 사용할 것입니다.
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: function(){return 5}(),
b: function(){return 6}(),
c: function(){return this.a + this.b}
}
console.log(foo.c())
위의 더 나은 답변도 있습니다 . 이것이 내가 질문 한 예제 코드를 수정 한 방법입니다.
최신 정보:
var foo = {
get a(){return 5},
get b(){return 6},
get c(){return this.a + this.b}
}
// console.log(foo.c);
객체 리터럴에 새 함수를 만들고 생성자를 호출하는 것은 원래 문제에서 근본적으로 벗어난 것처럼 보이며 불필요합니다.
개체 리터럴 초기화 중에는 형제 속성을 참조 할 수 없습니다.
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
이 모든 것의 핵심은 SCOPE 입니다.
정의하려는 속성의 "부모"(상위 개체)를 자체 인스턴스화 된 개체로 캡슐화 한 다음 키워드를 사용하여 형제 속성에 대한 참조를 만들 수 있습니다. 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)
};
여기에 게시 된 다른 답변이 더 좋지만 다음과 같은 대안이 있습니다.
- 초기화시 값을 설정합니다 (게터 또는 파생 등이 아님).
init()
개체 리터럴 외부의 코드 또는 유형이 필요하지 않습니다.- 개체 리터럴이며 팩토리 기능이나 다른 개체 생성 메커니즘이 아닙니다.
- 성능에 영향을주지 않아야합니다 (초기화시 제외).
자체 실행 익명 기능 및 창 저장
var foo = {
bar:(function(){
window.temp = "qwert";
return window.temp;
})(),
baz: window.temp
};
주문이 보장됩니다 ( bar
이전 baz
).
window
당연히 오염 이 되긴하지만, window.temp
끈질긴 스크립트를 작성하는 사람은 상상할 수 없습니다 . tempMyApp
편집증이라면 아마도 .
추악하지만 때때로 유용합니다. 예를 들어 엄격한 초기화 조건으로 API를 사용하고 리팩토링을 원하지 않아 범위 지정이 정확할 때입니다.
물론 건조합니다.
객체가 객체를 반환하는 함수로 작성되고 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);
다음 코드를 대안으로 사용하고 작동합니다. 그리고 변수도 배열이 될 수 있습니다. (@ 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]
다음은 깔끔한 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]);
이 솔루션은 배열이있는 중첩 된 객체에서도 작동합니다.
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');
이 정확한 시나리오를 다루지 않았기 때문에 옵션을 던졌습니다. 업데이트 또는 업데이트를 원하지 않는 경우 ES6 IIFE가 잘 작동합니다.c
a
b
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에 매개 변수로 전달합니다.
Other approach would be to declare the object first before assigning properties into it:
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
With that, you can use the object variable name to access the already assigned values.
Best for config.js
file.
Note: This solution uses Typescript (you can use the vanilla JS which TS compiles to if needed)
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();
Here were using class expressions to get the nested object literal interface we'd want. This is the next best thing IMHO to being able to reference the properties of an object during creation.
Main thing to note is while using this solution, you have exact same interface as you'd have had from an object literal. And the syntax is pretty close to an object literal itself (vs using a function, etc).
Compare the following
Solution I've proposed
class asd {
def = new class {
ads= 'asd';
qwe= this.ads + '123';
};
Solution if object literals would've sufficed
var asd = {
def : {
ads:'asd',
qwe: this.ads + '123';, //ILLEGAL CODE; just to show ideal scenario
}
}
Another example
Here in this class, you can combine multiple relative path among themselves, which is not possible with an object literal.
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';
};
}
}
If you want to use native JS, the other answers provide good solutions.
But if you'd prefer to write self-referencing objects like:
{
a: ...,
b: "${this.a + this.a}",
}
I wrote an npm library called self-referenced-object that supports that syntax and returns a native object.
참고URL : https://stackoverflow.com/questions/4616202/self-references-in-object-literals-initializers
'developer tip' 카테고리의 다른 글
.BAT 파일 내에서 여러 .BAT 파일을 실행하는 방법 (0) | 2020.10.02 |
---|---|
정규식 : AND 연산자가 있습니까? (0) | 2020.10.02 |
버튼을 클릭 할 때 대화 상자가 닫히지 않도록하는 방법 (0) | 2020.09.30 |
DataFrame 열의 순서를 변경하는 방법은 무엇입니까? (0) | 2020.09.30 |
개수 (*) 대 개수 (1)-SQL Server (0) | 2020.09.30 |