programing

자바스크립트에서 동적 게터/세터를 구현할 수 있습니까?

cafebook 2023. 10. 20. 14:20
반응형

자바스크립트에서 동적 게터/세터를 구현할 수 있습니까?

다음과 같은 작업을 수행하여 이름이 이미 알고 있는 속성에 대한 게터 및 세터를 만드는 방법을 알고 있습니다.

// A trivial example:
function MyObject(val){
    this.count = 0;
    this.value = val;
}
MyObject.prototype = {
    get value(){
        return this.count < 2 ? "Go away" : this._value;
    },
    set value(val){
        this._value = val + (++this.count);
    }
};
var a = new MyObject('foo');

alert(a.value); // --> "Go away"
a.value = 'bar';
alert(a.value); // --> "bar2"

자, 제 질문은, 이런 일종의 캐치올 게터와 세터를 정의하는 것이 가능한가 하는 것입니다.즉, 아직 정의되지 않은 속성 이름에 대한 getter 및 setter를 만듭니다.

그 개념은 PHP에서 가능합니다.__get()그리고.__set()마법 방법(이것들에 대한 정보는 PHP 설명서 참조), 그래서 나는 정말로 이것들과 동등한 자바스크립트가 있는지 묻고 싶습니다.

말할 것도 없이, 저는 이상적으로 크로스 브라우저 호환 솔루션을 원합니다.

이것은 ES2015(일명 "ES6") 사양에 따라 변경되었습니다: 자바스크립트는 이제 프록시가 있습니다.프록시를 사용하면 다른 개체의 실제 프록시인 개체를 만들 수 있습니다.검색 시 문자열인 속성 값을 모든 상한으로 변경하고 반환하는 간단한 예는 다음과 같습니다."missing"대신에undefined존재하지 않는 속성의 경우:

"use strict";
if (typeof Proxy == "undefined") {
    throw new Error("This browser doesn't support Proxy");
}
let original = {
    example: "value",
};
let proxy = new Proxy(original, {
    get(target, name, receiver) {
        if (Reflect.has(target, name)) {
            let rv = Reflect.get(target, name, receiver);
            if (typeof rv === "string") {
                rv = rv.toUpperCase();
            }
            return rv;
        }
        return "missing";
      }
});
console.log(`original.example = ${original.example}`); // "original.example = value"
console.log(`proxy.example = ${proxy.example}`);       // "proxy.example = VALUE"
console.log(`proxy.unknown = ${proxy.unknown}`);       // "proxy.unknown = missing"
original.example = "updated";
console.log(`original.example = ${original.example}`); // "original.example = updated"
console.log(`proxy.example = ${proxy.example}`);       // "proxy.example = UPDATED"

재정의하지 않는 작업에는 기본 동작이 적용됩니다.위에서, 우리가 무시하는 것은get, 연결할 수 있는 모든 작전 목록이 있습니다

get핸들러 함수의 인수 목록:

  • target프록시되는 개체(original, 우리의 경우).
  • name는 검색할 속성의 이름이며, 일반적으로 문자열이지만 기호일 수도 있습니다.
  • receiver는 다음과 같이 사용되어야 하는 객체입니다.this속성이 데이터 속성이 아닌 접근자인 경우 getter 함수에서.일반적인 경우 이것은 프록시 또는 프록시로부터 상속되는 것이지만 트랩이 다음에 의해 트리거될 수 있기 때문에 무엇이든 가능합니다.Reflect.get.

이렇게 하면 원하는 catch-all getter 및 setter 기능으로 개체를 만들 수 있습니다.

"use strict";
if (typeof Proxy == "undefined") {
    throw new Error("This browser doesn't support Proxy");
}
let obj = new Proxy({}, {
    get(target, name, receiver) {
        if (!Reflect.has(target, name)) {
            console.log("Getting non-existent property '" + name + "'");
            return undefined;
        }
        return Reflect.get(target, name, receiver);
    },
    set(target, name, value, receiver) {
        if (!Reflect.has(target, name)) {
            console.log(`Setting non-existent property '${name}', initial value: ${value}`);
        }
        return Reflect.set(target, name, value, receiver);
    }
});

console.log(`[before] obj.example = ${obj.example}`);
obj.example = "value";
console.log(`[after] obj.example = ${obj.example}`);

위의 출력은 다음과 같습니다.

존재하지 않는 속성 '예' 가져오기[이전] obj. example = 정의되지 않음존재하지 않는 속성 '예', 초기값 설정: 값[이후] obj. example = 값

검색을 시도할 때 "존재하지 않음" 메시지가 표시되는 방법을 주목하십시오.example그것이 아직 존재하지 않을 때, 그리고 우리가 그것을 만들 때, 그러나 그 이후에는 존재하지 않을 때.


2011년 이후의 답변(위에서 폐지되었지만 Internet Explorer와 같은 ES5 기능으로 제한된 환경과 여전히 관련이 있음):

아니요, 자바스크립트에는 캐치올 속성 기능이 없습니다.사용 중인 접근자 구문은 사양 11.1.5 섹션에서 다루며 와일드카드 같은 것은 제공하지 않습니다.

물론 기능을 구현할 수도 있겠지만, 아마 사용하고 싶지 않으실 겁니다.f = obj.prop("example");보다는f = obj.example;그리고.obj.prop("example", value);보다는obj.example = value;(함수가 알 수 없는 속성을 처리하는 데 필요합니다.)

FWIW, getter 함수(세터 논리에 신경쓰지 않았습니다)는 다음과 같습니다.

MyObject.prototype.prop = function(propName) {
    if (propName in this) {
        // This object or its prototype already has this property,
        // return the existing value.
        return this[propName];
    }

    // ...Catch-all, deal with undefined property here...
};

하지만 다시 말하지만, 여러분이 정말로 그렇게 하고 싶어할지는 상상할 수 없습니다. 왜냐하면 그것이 여러분이 물체를 어떻게 사용하는지를 변화시키기 때문입니다.

서문:

T.J. 크라우더의 대답은Proxy, OP가 요구한 대로 존재하지 않는 속성에 대한 캐치올 게터/세터에 필요합니다.동적 게터/세터로 실제로 원하는 동작이 무엇인지에 따라,Proxy그러나 실제로 필요하지 않을 수도 있습니다. 또는 잠재적으로 당신은 a의 조합을 사용하기를 원할 수도 있습니다.Proxy제가 아래에서 보여드릴 것과 함께.

한 적이 .)Proxy최근 리눅스의 파이어폭스에서 철저하게 작업을 해보니 매우 능력이 뛰어나지만 작업하기가 다소 혼란스럽고 정확하기가 어렵다는 것을 알게 되었습니다.더 중요한 것은, 저는 또한 그것이 꽤 느리다는 것을 발견했습니다 (적어도 요즘 자바스크립트가 얼마나 최적화된 경향이 있는지와 관련하여) - 저는 데카-배수의 영역에서 더 느리다는 것을 말하고 있습니다.)


동적으로 생성된 getter 및 setter를 구체적으로 구현하려면 또는 를 사용할 수 있습니다.이것 또한 꽤 빠릅니다.

핵심은 다음과 같이 객체에 게터 및/또는 설정기를 정의할 수 있다는 것입니다.

let obj = {};
let val = 0;
Object.defineProperty(obj, 'prop', { //<- This object is called a "property descriptor".
  //Alternatively, use: `get() {}`
  get: function() {
    return val;
  },
  //Alternatively, use: `set(newValue) {}`
  set: function(newValue) {
    val = newValue;
  }
});

//Calls the getter function.
console.log(obj.prop);
let copy = obj.prop;
//Etc.

//Calls the setter function.
obj.prop = 10;
++obj.prop;
//Etc.

여기서 주의해야 할 몇 가지 사항:

  • 사용할 수 없습니다.value속성 설명자의 속성(에 표시되지 않음)과 동시에get및/set; 문서에서 다음을(를)

    객체에 존재하는 속성 설명자는 데이터 설명자와 접근자 설명자의 두 가지 주요한 맛이 있습니다.데이터 디스크립터는 쓰기 가능한 값 또는 그렇지 않은 값을 가진 속성입니다.접근자 기술자는 게터-세터 함수 쌍에 의해 기술되는 속성입니다.설명자는 이 두 가지 맛 중 하나여야 하며 둘 다 될 수 없습니다.

  • 따라서, 당신은 내가 그들을 만들어냈다는 것을 주목할 것입니다.val외부의 재산Object.defineProperty()전화/property 설명자.이것이 표준 행동입니다.
  • 여기 오류에 따라 설정하지 마십시오.writable.true사용할 경우 속성 설명자에서get아니면set.
  • 설정을 고려해 볼 수 있습니다.configurable그리고.enumerable, 그러나 문서에서 원하는 내용에 따라 다음을 수행할 수 있습니다.

    구성 가능한

    • true속성 설명자의 유형이 변경될 수 있고 해당 개체에서 속성이 삭제될 수 있는 경우에만 해당됩니다.

    • 기본값은 false입니다.


    열거할 수 있는

    • true해당 개체의 속성을 열거하는 동안 이 속성이 나타나는 경우에만 해당됩니다.

    • 기본값은 false입니다.


이와 관련하여 다음 사항도 관심 대상이 될 수 있습니다.

이 문제에 대한 독창적인 접근 방식은 다음과 같습니다.

var obj = {
  emptyValue: null,
  get: function(prop){
    if(typeof this[prop] == "undefined")
        return this.emptyValue;
    else
        return this[prop];
  },
  set: function(prop,value){
    this[prop] = value;
  }
}

사용하려면 속성을 문자열로 전달해야 합니다.다음은 작동 방식의 예입니다.

//To set a property
obj.set('myProperty','myValue');

//To get a property
var myVar = obj.get('myProperty');

편집: 제가 제안한 내용을 바탕으로 개선된 객체 지향적 접근 방식은 다음과 같습니다.

function MyObject() {
    var emptyValue = null;
    var obj = {};
    this.get = function(prop){
        return (typeof obj[prop] == "undefined") ? emptyValue : obj[prop];
    };
    this.set = function(prop,value){
        obj[prop] = value;
    };
}

var newObj = new MyObject();
newObj.set('myProperty','MyValue');
alert(newObj.get('myProperty'));

여기서 작동하는 것을 볼 수 있습니다.

뭔가를 찾다가 스스로 알아냈어요

/*
    This function takes an object and converts to a proxy object.
    It also takes care of proxying nested objectsa and array.
*/
let getProxy = (original) => {

    return new Proxy(original, {

        get(target, name, receiver) {
            let rv = Reflect.get(target, name, receiver);
            return rv;
        },

        set(target, name, value, receiver) {

            // Proxies new objects 
            if(typeof value === "object"){
                value = getProxy(value);
            }

            return Reflect.set(target, name, value, receiver);
        }
    })
}

let first = {};
let proxy = getProxy(first);

/*
    Here are the tests
*/

proxy.name={}                               // object
proxy.name.first={}                         // nested object
proxy.name.first.names=[]                   // nested array 
proxy.name.first.names[0]={first:"vetri"}   // nested array with an object

/*
    Here are the serialised values
*/
console.log(JSON.stringify(first))  // {"name":{"first":{"names":[{"first":"vetri"}]}}}
console.log(JSON.stringify(proxy))  // {"name":{"first":{"names":[{"first":"vetri"}]}}}
var x={}
var propName = 'value' 
var get = Function("return this['" + propName + "']")
var set = Function("newValue", "this['" + propName + "'] = newValue")
var handler = { 'get': get, 'set': set, enumerable: true, configurable: true }
Object.defineProperty(x, propName, handler)

이것은 나에게 알맞습니다.

언급URL : https://stackoverflow.com/questions/7891937/is-it-possible-to-implement-dynamic-getters-setters-in-javascript

반응형