자바 스크립트의 고유 개체 식별자


120

몇 가지 실험을해야하고 자바 스크립트의 개체에 대한 고유 식별자를 알아야하므로 동일한 지 확인할 수 있습니다. 동등 연산자를 사용하고 싶지 않습니다. 파이썬의 id () 함수와 같은 것이 필요합니다.

이와 같은 것이 존재합니까?


2
조금 궁금합니다. 왜 등호 연산자를 피하고 싶습니까?
CMS

22
나는 단순한 것을 원하고 숫자를보고 싶기 때문입니다. 이 언어는 새끼 고양이를 울게 만듭니다. 나는 이미 충분히 싸우고 있습니다.
Stefano Borini

4
완전 항 등 연산자 (===)는 객체에 대해 요청한 작업을 수행하며 (숫자 / 문자열 / 등을 비교하는 경우 동일하지 않음) 모든 객체에 비밀 고유 ID를 구축하는 것보다 간단합니다.
Ben Zotto 2010 년

4
@CMS @Ben 고유 ID를 갖는 것은 IdentitySet과 같은 것을 디버깅하거나 구현하는 데 유용 할 수 있습니다.
Alex Jasmin

9
나는 내가 자바 스크립트 이해에 돌파구를 주었다고 전하고 싶다. 일종의 시냅스를 닫았습니다. 이제 모든 것이 명확합니다. 나는 물건을 보았다. 나는 자바 스크립트 프로그래머 수준을 얻었습니다.
Stefano Borini

답변:


68

업데이트 아래 내 원래 답변은 6 년 전에 시대와 이해에 맞는 스타일로 작성되었습니다. 댓글의 일부 대화에 대한 응답으로 이에 대한보다 현대적인 접근 방식은 다음과 같습니다.

(function() {
    if ( typeof Object.id == "undefined" ) {
        var id = 0;

        Object.id = function(o) {
            if ( typeof o.__uniqueid == "undefined" ) {
                Object.defineProperty(o, "__uniqueid", {
                    value: ++id,
                    enumerable: false,
                    // This could go either way, depending on your 
                    // interpretation of what an "id" is
                    writable: false
                });
            }

            return o.__uniqueid;
        };
    }
})();

var obj = { a: 1, b: 1 };

console.log(Object.id(obj));
console.log(Object.id([]));
console.log(Object.id({}));
console.log(Object.id(/./));
console.log(Object.id(function() {}));

for (var k in obj) {
    if (obj.hasOwnProperty(k)) {
        console.log(k);
    }
}
// Logged keys are `a` and `b`

구식 브라우저 요구 사항이있는 경우 여기 에서 Object.defineProperty.

비교가 가치 있다고 생각하기 때문에 원래 답변은 (변경 내역이 아닌) 아래에 보관됩니다.


다음과 같은 스핀을 줄 수 있습니다. 또한 생성자 또는 다른 곳에서 객체의 ID를 명시 적으로 설정할 수있는 옵션도 제공합니다.

(function() {
    if ( typeof Object.prototype.uniqueId == "undefined" ) {
        var id = 0;
        Object.prototype.uniqueId = function() {
            if ( typeof this.__uniqueid == "undefined" ) {
                this.__uniqueid = ++id;
            }
            return this.__uniqueid;
        };
    }
})();

var obj1 = {};
var obj2 = new Object();

console.log(obj1.uniqueId());
console.log(obj2.uniqueId());
console.log([].uniqueId());
console.log({}.uniqueId());
console.log(/./.uniqueId());
console.log((function() {}).uniqueId());

고유 ID를 내부적으로 저장하는 데 사용하는 구성원이 자동으로 생성 된 다른 구성원 이름과 충돌하지 않도록주의하십시오.


1
@Justin Object.prototype에 속성을 추가하는 것은 ECMAScript 3에서 문제가 있습니다. 이러한 속성은 모든 개체에서 열거 할 수 있기 때문입니다. 따라서 Object.prototype.a 를 정의 하면 for (prop in {}) alert (prop) 를 수행 할 때 "a"가 표시됩니다 . 따라서 Object.prototype늘리는 것과 for..in 루프를 사용하여 객체와 같은 레코드를 반복 할 수 있는 것 사이에 타협을해야합니다 . 이것은 도서관에 심각한 문제입니다
Alex Jasmin

29
타협이 없습니다. 루프를 사용할 object.hasOwnProperty(member)때 항상 사용하는 것이 모범 사례로 오랫동안 고려되어 왔습니다 for..in. 이것은 잘 문서화 된 관행이며 jslint에 의해 시행되는 관행입니다
Justin Johnson

3
적어도 모든 개체에 대해이 작업을 권장하지 않을 것입니다. 처리하는 개체에 대해 동일한 작업을 수행 할 수 있습니다. 안타깝게도 대부분의 경우 외부 자바 스크립트 라이브러리를 사용해야하며, 안타깝게도 모든 라이브러리가 잘 프로그래밍 된 것은 아니므로 웹 페이지에 포함 된 모든 라이브러리를 완전히 제어 할 수 없거나 적어도 잘 처리된다는 것을 알고있는 경우가 아니면이를 피하십시오. .
쓸모

1
@JustinJohnson : ES5에는 타협이 있습니다 defineProperty(…, {enumerable:false}). 그리고 그 uid자체가 방법 에 있어야 Object어쨌든 네임 스페이스
BERGI

@JustinJohnson 개체 ID가 4어떻게 ID 4를 가진 obj를 변수에 할당하면 속성에 액세스하는 것과 같이 작업을 수행 할 수 있습니까?
Sir

50

내 관찰이 진행되는 한 여기에 게시 된 답변은 예기치 않은 부작용을 일으킬 수 있습니다.

ES2015 호환 환경에서 WeakMap 을 사용하여 부작용을 피할 수 있습니다 .

const id = (() => {
    let currentId = 0;
    const map = new WeakMap();

    return (object) => {
        if (!map.has(object)) {
            map.set(object, ++currentId);
        }

        return map.get(object);
    };
})();

id({}); //=> 1

1
왜 안돼 return currentId?
Gust van de Wal

WeakMap반대 Map입니까? 이 함수는 문자열이나 숫자를 제공하면 충돌합니다.
Nate Symer

35

최신 브라우저는 Object.prototype을 확장하는 더 깨끗한 방법을 제공합니다. 이 코드는 속성 열거에서 속성을 숨 깁니다 (p in o).

defineProperty를 구현 하는 브라우저의 경우 다음 과 같이 uniqueId 속성을 구현할 수 있습니다.

(function() {
    var id_counter = 1;
    Object.defineProperty(Object.prototype, "__uniqueId", {
        writable: true
    });
    Object.defineProperty(Object.prototype, "uniqueId", {
        get: function() {
            if (this.__uniqueId == undefined)
                this.__uniqueId = id_counter++;
            return this.__uniqueId;
        }
    });
}());

자세한 내용은 https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/defineProperty를 참조 하십시오.


2
"최신 브라우저"에는 Firefox 3.6이 포함되지 않은 것 같습니다. (예, 최신 버전의 Firefox에서 업그레이드 경쟁에서 탈퇴하고 있습니다. 저 뿐만이 아닙니다. 게다가 FF3.6은 겨우 1 년이되었습니다.)
bart

7
이 스포츠에서 1 살은 "오직"이 아닙니다. 웹은 동적으로 진화합니다. 그것은 좋은 일입니다. 최신 브라우저에는이를 허용하기위한 자동 업데이트 프로그램이 있습니다.
Kos

1
몇 년 후, 이것은 받아 들인 대답보다 더 잘 작동하는 것 같습니다.
Fitter Man

11

실제로 object프로토 타입 을 수정하고 거기에 함수를 추가 할 필요가 없습니다 . 다음은 귀하의 목적에 적합합니다.

var __next_objid=1;
function objectId(obj) {
    if (obj==null) return null;
    if (obj.__obj_id==null) obj.__obj_id=__next_objid++;
    return obj.__obj_id;
}

4
nocase, snake_case camelCase는 세 가지 모두 6 줄 코드 조각으로 만들어졌습니다. 당신은 그것을 매일 보지 않는다
Gust van de Wal

이것은 object해킹 보다 훨씬 더 설득력있는 메커니즘처럼 보입니다 . OP의 개체 일치를 원할 때만 실행하고 나머지 시간 동안에는 방해가되지 않습니다.
JL Peyret

6

Object.defineProperty()메서드를 구현하는 브라우저의 경우 아래 코드는 소유 한 모든 개체에 바인딩 할 수있는 함수를 생성하고 반환합니다.

이 방법은 확장하지 않는 장점이 Object.prototype있습니다.

코드는 주어진 객체에 __objectID__속성 이 있는지 확인하고, 그렇지 않은 경우 숨겨진 (열거 불가능) 읽기 전용 속성으로 정의하여 작동합니다.

따라서 읽기 전용 obj.__objectID__속성을 정의한 후 변경하거나 재정의하려는 시도에 대해 안전하며 조용히 실패하는 대신 지속적으로 멋진 오류를 발생시킵니다.

마지막으로, __objectID__주어진 객체에 이미 다른 코드가 정의되어있는 매우 극단적 인 경우이 값은 단순히 반환됩니다.

var getObjectID = (function () {

    var id = 0;    // Private ID counter

    return function (obj) {

         if(obj.hasOwnProperty("__objectID__")) {
             return obj.__objectID__;

         } else {

             ++id;
             Object.defineProperty(obj, "__objectID__", {

                 /*
                  * Explicitly sets these two attribute values to false,
                  * although they are false by default.
                  */
                 "configurable" : false,
                 "enumerable" :   false,

                 /* 
                  * This closure guarantees that different objects
                  * will not share the same id variable.
                  */
                 "get" : (function (__objectID__) {
                     return function () { return __objectID__; };
                  })(id),

                 "set" : function () {
                     throw new Error("Sorry, but 'obj.__objectID__' is read-only!");
                 }
             });

             return obj.__objectID__;

         }
    };

})();

4

jQuery 코드는 data()이러한 ID로 자체 메서드를 사용합니다 .

var id = $.data(object);

백 스테이지에서 메서드 data는 다음과 같은 고유 ID 스트림의 다음 ID를 넣어 object호출 하는 매우 특별한 필드를 만듭니다."jQuery" + now()

id = elem[ expando ] = ++uuid;

John Resig가 JavaScript에 대한 모든 것을 분명히 알고 있으며 그의 방법은 모든 지식을 기반으로하는 것과 동일한 방법을 사용하는 것이 좋습니다.


5
jQuery의 data방법에는 결함이 있습니다. 예를 들어 stackoverflow.com/questions/1915341/…을 참조하십시오 . 또한 John Resig는 JavaScript에 대해 알아야 할 모든 것을 결코 알지 못하며 그가 JavaScript 개발자로서 당신을 돕지 않을 것이라고 믿습니다.
Tim Down

1
@Tim, 내부적으로 거의 동일하기 때문에 여기에 제시된 다른 방법과 마찬가지로 고유 ID를 얻는 측면에서 많은 결함이 있습니다. 그리고 예, 저는 John Resig가 저보다 더 많이 알고 있다고 믿으며 그가 Douglas Crockford가 아니더라도 그의 결정에서 배워야합니다.
vava

AFAIK $ .data 는 JavaScript 개체에서 작동하지 않고 DOM 요소에서만 작동합니다.
mb21 2014-06-12

4

@justin answer의 Typescript 버전, ES6 호환, Symbols를 사용하여 키 충돌을 방지하고 편의를 위해 전역 Object.id에 추가되었습니다. 아래 코드를 복사하여 붙여 넣거나 가져올 ObjecId.ts 파일에 넣으십시오.

(enableObjectID)();

declare global {
    interface ObjectConstructor {
        id: (object: any) => number;
    }
}

const uniqueId: symbol = Symbol('The unique id of an object');

export function enableObjectID(): void {
    if (typeof Object['id'] !== 'undefined') {
        return;
    }

    let id: number = 0;

    Object['id'] = (object: any) => {
        const hasUniqueId: boolean = !!object[uniqueId];
        if (!hasUniqueId) {
            object[uniqueId] = ++id;
        }

        return object[uniqueId];
    };
}

사용 예 :

console.log(Object.id(myObject));

1

다음과 같은 코드를 사용하여 개체가 고유 한 문자열로 문자열 화됩니다.

Object.prototype.__defineGetter__('__id__', function () {
    var gid = 0;
    return function(){
        var id = gid++;
        this.__proto__ = {
             __proto__: this.__proto__,
             get __id__(){ return id }
        };
        return id;
    }
}.call() );

Object.prototype.toString = function () {
    return '[Object ' + this.__id__ + ']';
};

__proto__비트가 유지되어 __id__물체에 표시되지 게터. 이것은 firefox에서만 테스트되었습니다.


__defineGetter__비표준 임을 명심하십시오 .
Tomáš Zato-Monica 복원 2015-09-29

1

Object.prototype을 수정하지 말라는 조언에도 불구하고 이것은 제한된 범위 내에서 테스트에 여전히 유용 할 수 있습니다. 수락 된 답변의 작성자가 변경했지만 여전히 설정 중이며 Object.id이는 나에게 의미가 없습니다. 다음은 작업을 수행하는 스 니펫입니다.

// Generates a unique, read-only id for an object.
// The _uid is generated for the object the first time it's accessed.

(function() {
  var id = 0;
  Object.defineProperty(Object.prototype, '_uid', {
    // The prototype getter sets up a property on the instance. Because
    // the new instance-prop masks this one, we know this will only ever
    // be called at most once for any given object.
    get: function () {
      Object.defineProperty(this, '_uid', {
        value: id++,
        writable: false,
        enumerable: false,
      });
      return this._uid;
    },
    enumerable: false,
  });
})();

function assert(p) { if (!p) throw Error('Not!'); }
var obj = {};
assert(obj._uid == 0);
assert({}._uid == 1);
assert([]._uid == 2);
assert(obj._uid == 0);  // still

1

나는 같은 문제에 직면했고 여기에 ES6로 구현 한 해결책이 있습니다.

code
let id = 0; // This is a kind of global variable accessible for every instance 

class Animal {
constructor(name){
this.name = name;
this.id = id++; 
}

foo(){}
 // Executes some cool stuff
}

cat = new Animal("Catty");


console.log(cat.id) // 1 

1

두 개체를 비교하기위한 가장 간단한 방법은 개체를 비교할 때 개체 중 하나에 고유 한 속성을 추가하고 다른 개체에 속성이 있는지 확인한 다음 다시 제거하는 것입니다. 이것은 오버라이드 프로토 타입을 저장합니다.

function isSameObject(objectA, objectB) {
   unique_ref = "unique_id_" + performance.now();
   objectA[unique_ref] = true;
   isSame = objectB.hasOwnProperty(unique_ref);
   delete objectA[unique_ref];
   return isSame;
}

object1 = {something:true};
object2 = {something:true};
object3 = object1;

console.log(isSameObject(object1, object2)); //false
console.log(isSameObject(object1, object3)); //true
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.