programing

JavaScript 함수 앨리어싱이 작동하지 않는 것 같습니다.

nasanasas 2020. 10. 4. 11:29
반응형

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 년에는 bind더 멋진 방식으로이를 수행 할 수있는 ES5 의 새로운 방법이 있습니다.

>>> $ = 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 범위에서 선언되며 호출 할 때 thissum 함수 내부의 값이 전역 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에서 작동하는 이유는 무엇입니까?

I don't know the internal implementation of getElementById in IE, but a comment in jQuery source (inArray method implementation) says that in IE, window == document. If that's the case, then aliasing document.getElementById should work in IE.

To illustrate this further, I have created an elaborate example. Have a look at the Person function below.

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();
    };

}

For the Person function above, here's how the various getAge methods will behave.

Let's create two objects using Person function.

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.

Straight forward, getAge method gets yogi object as this and outputs 100.


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

JavaScript interepreter sets window object as this and our getAge method will return -1.


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

If we set the correct scope, you can use ageAlias method.


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

If we pass in some other person object, it will still calculate age correctly.

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

The ageSmarter function captured the original this object so now you don't have to worry about supplying correct scope.


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

The problem with ageSmarter is that you can never set the scope to some other object.


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

The ageSmartest function will use the original scope if an invalid scope is supplied.


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

You will still be able to pass another Person object to getAgeSmartest. :)


This is a short answer.

The following makes a copy of (a reference to) the function. The problem is that now the function is on the window object when it was designed to live on the document object.

window.myAlias = document.getElementById

The alternatives are

  • to use a wrapper (already mentioned by Fabien Ménager)
  • or you can use two aliases.

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

Another short answer, just for wrapping/aliasing console.log and similar logging methods. They all expect to be in the console context.

This is usable when wrapping console.log with some fallbacks, in case you or your users have run into trouble when using a browser that doesn't (always) support it. This is not a full solution to that problem though, as it needs to be expanded checks and a fallback - your mileage may vary.

Example using warnings

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

Then use it as usual

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

If you prefer to see your logging arguments wrapped in an array (I do, most of the time), substitute .apply(...) with .call(...).

Should work with console.log(), console.debug(), console.info(), console.warn(), console.error(). See also console on MDN.


In addition to other great answers, there's simple jQuery method $.proxy.

You can alias like this:

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

Or

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

You actually can't "pure alias" a function on a predefined object. Therefore, the closest to aliasing you can get without wrapping is by staying within the same object:

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

참고URL : https://stackoverflow.com/questions/1007340/javascript-function-aliasing-doesnt-seem-to-work

반응형