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

나는 이 질문을 읽고 있었고 함수 래퍼 방법이 아닌 별칭 방법을 시도하고 싶었지만 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에서 작동합니다.



답변

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

>>> $ = 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();">


답변

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

별칭을 사용할 수없는 이유를 알아보기 전에 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. 🙂


답변

이것은 짧은 대답입니다.

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

window.myAlias = document.getElementById

대안은 다음과 같습니다.

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

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


답변

래핑 / 앨리어싱 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 도 참조하십시오 .


답변

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

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

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

또는

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


답변

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

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


답변