JavaScript 함수 별칭이 작동하지 않는 것 같습니다.


85

나는 이 질문을 읽고 있었고 함수 래퍼 방법이 아닌 별칭 방법을 시도하고 싶었지만 Firefox 3 또는 3.5beta4 또는 Google Chrome에서 디버그 창과 테스트 웹 페이지에서.

개똥 벌레:

>>> window.myAlias = document.getElementById
function()
>>> myAlias('item1')
>>> window.myAlias('item1')
>>> document.getElementById('item1')
<div id="item1">

웹 페이지에 넣으면 myAlias를 호출하면 다음 오류가 발생합니다.

uncaught exception: [Exception... "Illegal operation on WrappedNative prototype object" nsresult: "0x8057000c (NS_ERROR_XPC_BAD_OP_ON_WN_PROTO)" location: "JS frame :: file:///[...snip...]/test.html :: <TOP_LEVEL> :: line 7" data: no]

Chrome (명확성을 위해 >>>이 삽입 됨) :

>>> window.myAlias = document.getElementById
function getElementById() { [native code] }
>>> window.myAlias('item1')
TypeError: Illegal invocation
>>> document.getElementById('item1')
<div id=?"item1">?

그리고 테스트 페이지에서 동일한 "불법 호출"을 얻습니다.

내가 뭘 잘못하고 있니? 다른 사람이 이것을 재현 할 수 있습니까?

또한 이상하게도 IE8에서 작동합니다.


1
IE8에서 — 일종의 마법의 기능이나 무언가가 될 수 있습니다.
Maciej Łebkowski

stackoverflow.com/questions/954417 에서 잘못된 답변에 대한 의견을 추가 하고 여기로 안내했습니다.
Grant Wagner

1
Function.prototype.bind 메소드를 지원하는 브라우저의 경우 : window.myAlias ​​= document.getElementById.bind (document); MDN 문서를 검색하면 바인드 심이 있습니다.
Ben Fleming

답변:


39

해당 메서드를 문서 개체에 바인딩해야합니다. 보기:

>>> $ = document.getElementById
getElementById()
>>> $('bn_home')
[Exception... "Cannot modify properties of a WrappedNative" ... anonymous :: line 72 data: no]
>>> $.call(document, 'bn_home')
<body id="bn_home" onload="init();">

간단한 별칭을 수행 할 때 함수는 문서 개체가 아닌 전역 개체에서 호출됩니다. 이 문제를 해결하려면 클로저라는 기술을 사용하십시오.

function makeAlias(object, name) {
    var fn = object ? object[name] : null;
    if (typeof fn == 'undefined') return function () {}
    return function () {
        return fn.apply(object, arguments)
    }
}
$ = makeAlias(document, 'getElementById');

>>> $('bn_home')
<body id="bn_home" onload="init();">

이렇게하면 원본 개체에 대한 참조가 손실되지 않습니다.

2012 년에는 bindES5 의 새로운 방법이 있습니다.이를 통해 더 멋진 방식으로이를 수행 할 수 있습니다.

>>> $ = document.getElementById.bind(document)
>>> $('bn_home')
<body id="bn_home" onload="init();">

4
새 함수를 반환하기 때문에 실제로 래퍼입니다. Kev의 질문에 대한 대답은 위에서 설명한 이유 때문에 불가능하다는 것입니다. 여기서 설명하는 방법이 아마도 가장 좋은 방법 일 것입니다.
jiggy 2009-06-17

의견을 보내 주셔서 감사합니다. 그렇다면 다른 질문에 대한 답변의 구문은 완전히 가짜입니까? 흥미로운 ...
Kev

188

이 특정 행동을 이해하기 위해 깊이 파고 들었고 좋은 설명을 찾은 것 같습니다.

별칭을 사용할 수없는 이유를 알아보기 전에 document.getElementByIdJavaScript 함수 / 객체의 작동 방식을 설명하려고합니다.

JavaScript 함수를 호출 할 때마다 JavaScript 인터프리터는 범위를 결정하고이를 함수에 전달합니다.

다음 기능을 고려하십시오.

function sum(a, b)
{
    return a + b;
}

sum(10, 20); // returns 30;

이 함수는 Window 범위에서 선언되며이를 호출 this하면 sum 함수 내부의 값이 전역 Window개체가됩니다.

'sum'함수의 경우 'this'가 사용하지 않기 때문에 값이 무엇인지는 중요하지 않습니다.


다음 기능을 고려하십시오.

function Person(birthDate)
{
    this.birthDate = birthDate;    
    this.getAge = function() { return new Date().getFullYear() - this.birthDate.getFullYear(); };
}

var dave = new Person(new Date(1909, 1, 1)); 
dave.getAge(); //returns 100.

당신이 dave.getAge 함수를 호출 할 때, 자바 스크립트 인터프리터는 당신이에 getAge 함수를 호출하는 것을보고 dave는 설정 때문에, 객체 thisdave하고, 호출 getAge기능. getAge()올바르게 반환 100됩니다.


JavaScript에서 apply메서드를 사용하여 범위를 지정할 수 있음을 알 수 있습니다 . 시도해 봅시다.

var dave = new Person(new Date(1909, 1, 1)); //Age 100 in 2009
var bob = new Person(new Date(1809, 1, 1)); //Age 200 in 2009

dave.getAge.apply(bob); //returns 200.

위 줄에서 JavaScript가 범위를 결정하도록하는 대신 수동으로 범위를 bob객체 로 전달 합니다. getAge이제 개체 에 대해 200'생각'했음에도 불구하고 반환됩니다 .getAgedave


위의 모든 것의 요점은 무엇입니까? 함수는 JavaScript 객체에 '느슨하게'연결됩니다. 예를 들어 할 수 있습니다.

var dave = new Person(new Date(1909, 1, 1));
var bob = new Person(new Date(1809, 1, 1));

bob.getAge = function() { return -1; };

bob.getAge(); //returns -1
dave.getAge(); //returns 100

다음 단계로 넘어 갑시다.

var dave = new Person(new Date(1909, 1, 1));
var ageMethod = dave.getAge;

dave.getAge(); //returns 100;
ageMethod(); //returns ?????

ageMethod실행하면 오류가 발생합니다! 어떻게 된 거예요?

주의 깊게 내 위의 포인트를 읽는다면, 당신은주의 할 dave.getAge메소드가 호출되었습니다 davethis자바 스크립트의 '범위'판별 할 수 없습니다 반면 개체를 ageMethod실행합니다. 그래서 글로벌 '창'을 'this'로 전달했습니다. 이제 속성 window이 없으므로 실행이 실패합니다.birthDateageMethod

이 문제를 해결하는 방법? 단순한,

ageMethod.apply(dave); //returns 100.

위의 모든 내용이 이해가 되었습니까? 그렇다면 별칭을 사용할 수없는 이유를 설명 할 수 있습니다 document.getElementById.

var $ = document.getElementById;

$('someElement'); 

$로 호출 windowthis하고있는 경우 getElementById구현이 기대 thisdocument, 그것은 실패합니다.

다시이 문제를 해결하려면 다음을 수행 할 수 있습니다.

$.apply(document, ['someElement']);

그렇다면 Internet Explorer에서 작동하는 이유는 무엇입니까?

getElementByIdIE 의 내부 구현을 모르지만 jQuery 소스 ( inArray메서드 구현) 의 주석은 IE에서 window == document. 이 경우 앨리어싱 document.getElementById은 IE에서 작동합니다.

이것을 더 설명하기 위해 나는 정교한 예를 만들었습니다. Person아래 기능을 살펴보십시오 .

function Person(birthDate)
{
    var self = this;

    this.birthDate = birthDate;

    this.getAge = function()
    {
        //Let's make sure that getAge method was invoked 
        //with an object which was constructed from our Person function.
        if(this.constructor == Person)
            return new Date().getFullYear() - this.birthDate.getFullYear();
        else
            return -1;
    };

    //Smarter version of getAge function, it will always refer to the object
    //it was created with.
    this.getAgeSmarter = function()
    {
        return self.getAge();
    };

    //Smartest version of getAge function.
    //It will try to use the most appropriate scope.
    this.getAgeSmartest = function()
    {
        var scope = this.constructor == Person ? this : self;
        return scope.getAge();
    };

}

Person위 의 기능에 대해 다양한 getAge메서드가 작동 하는 방식은 다음과 같습니다.

Person함수를 사용하여 두 개의 객체를 만들어 보겠습니다 .

var yogi = new Person(new Date(1909, 1,1)); //Age is 100
var anotherYogi = new Person(new Date(1809, 1, 1)); //Age is 200

console.log(yogi.getAge()); //Output: 100.

곧바로 getAge 메소드는 yogi객체를로 가져오고 this출력 100합니다.


var ageAlias = yogi.getAge;
console.log(ageAlias()); //Output: -1

자바 스크립트 인터프리터는 window객체를로 설정 this하고 getAge메서드는 -1.


console.log(ageAlias.apply(yogi)); //Output: 100

올바른 범위를 설정하면 ageAlias방법 을 사용할 수 있습니다 .


console.log(ageAlias.apply(anotherYogi)); //Output: 200

다른 사람 개체를 전달해도 나이를 올바르게 계산합니다.

var ageSmarterAlias = yogi.getAgeSmarter;    
console.log(ageSmarterAlias()); //Output: 100

ageSmarter함수는 원래 this객체를 캡처 하므로 이제 올바른 범위를 제공하는 것에 대해 걱정할 필요가 없습니다.


console.log(ageSmarterAlias.apply(anotherYogi)); //Output: 100 !!!

문제 ageSmarter는 범위를 다른 개체로 설정할 수 없다는 것입니다.


var ageSmartestAlias = yogi.getAgeSmartest;
console.log(ageSmartestAlias()); //Output: 100
console.log(ageSmartestAlias.apply(document)); //Output: 100

ageSmartest유효 범위가 공급되면 함수는 원래의 범위를 이용할 것이다.


console.log(ageSmartestAlias.apply(anotherYogi)); //Output: 200

여전히 다른 Person개체를 에 전달할 수 있습니다 getAgeSmartest. :)


7
IE가 작동하는 이유는 +1입니다. 나머지는 약간 주제에서 벗어 났지만 일반적으로 여전히 도움이됩니다. :)
Kev

44
훌륭한 대답입니다. 나는 그것이 어떻게 주제에서 벗어 났는지 보지 못합니다.
Justin Johnson

참으로 아주 좋은 대답입니다. 여기에 좀 더 짧은 버전이 작성되어 있습니다 .
Marcel Korpel 2010 년

8
내가 stackoverflow에서 본 최고의 답변 중 하나입니다.
warbaker 2011-06-30

IE에서 작동하는 이유는 무엇입니까? 왜냐하면 : stackoverflow.com/questions/6994139/…따라서이 메서드 구현은 실제로 {return window [id]} 일 수 있으므로 모든 컨텍스트에서 작동합니다
Maciej Łebkowski

3

이것은 짧은 대답입니다.

다음은 함수의 복사본 (참조)을 만듭니다. 문제는 이제 window객체에 살도록 설계되었을 때 기능이 객체에 있다는 document것입니다.

window.myAlias = document.getElementById

대안은 다음과 같습니다.

  • 포장지 사용 (이미 Fabien Ménager에 의해 언급 됨)
  • 또는 두 개의 별칭을 사용할 수 있습니다.

    window.d = document // A renamed reference to the object
    window.d.myAlias = window.d.getElementById
    

2

래핑 / 앨리어싱 console.log및 유사한 로깅 방법에 대한 또 다른 짧은 대답 입니다. 그들은 모두 console맥락 에 있기를 기대합니다 .

이것은 (항상) 지원하지 않는 브라우저를 사용할 때 console.log귀하 또는 귀하의 사용자가 문제를 겪는 경우 일부 폴백으로 래핑 할 때 사용할 수 있습니다 . 하지만 이것은 확장 된 수표와 폴 백이 필요하기 때문에 그 문제에 대한 완전한 해결책은 아닙니다. 마일리지는 다를 수 있습니다.

경고를 사용한 예

var warn = function(){ console.warn.apply(console, arguments); }

그런 다음 평소대로 사용하십시오.

warn("I need to debug a number and an object", 9999, { "user" : "Joel" });

당신이 배열에 싸여 로깅 인수를 참조하려는 경우 (내가 대부분의 시간), 대신 .apply(...).call(...).

작업을해야 console.log(), console.debug(), console.info(), console.warn(), console.error(). consoleMDN 도 참조하십시오 .


1

다른 훌륭한 답변 외에도 간단한 jQuery 메서드 $ .proxy가 있습니다.

다음과 같이 별칭을 지정할 수 있습니다.

myAlias = $.proxy(document, 'getElementById');

또는

myAlias = $.proxy(document.getElementById, document);

+1 가능한 솔루션이기 때문입니다. 그러나 엄밀히 말하면,이면에는 $.proxy실제로 래퍼가 있습니다.
pimvdb

-6

실제로 미리 정의 된 개체의 함수를 "순수한 별칭"으로 만들 수 없습니다 . 따라서 래핑하지 않고 얻을 수있는 앨리어싱에 가장 가까운 것은 동일한 객체 내에 머무르는 것입니다.

>>> document.s = document.getElementById;
>>> document.s('myid');
<div id="myid">

5
이것은 약간 부정확합니다. 함수 구현에서 'this'를 사용하는 경우 함수를 '순수 별칭'으로 사용할 수 있습니다. 따라서 상황에 따라 다릅니다.
SolutionYogi

죄송합니다. getElementById의 맥락에서 의미했습니다. 네가 옳아. 나는 이것을 반영하기 위해 대답을 약간 변경했습니다 ...
Kev
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.