Domanda

Stavo solo leggendo questa domanda e volevo provare alias piuttosto che il metodo wrapper funzione, ma non riesco a farlo funzionare in Firefox 3 o 3.5beta4 o Google Chrome, sia nelle finestre di debug che in una pagina Web di prova.

Firebug:

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

Se lo inserisco in una pagina Web, la chiamata a myAlias ??mi dà questo errore:

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 (con > > > inserito per maggiore chiarezza):

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

E nella pagina di prova ottengo la stessa "quotazione illegale".

Sto facendo qualcosa di sbagliato? Qualcun altro può riprodurlo?

Inoltre, stranamente, ho appena provato e funziona in IE8.

È stato utile?

Soluzione

Devi associare quel metodo all'oggetto documento. Guardate:

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

Quando stai facendo un semplice alias, la funzione viene chiamata sull'oggetto globale, non sull'oggetto documento. Usa una tecnica chiamata chiusure per risolvere questo problema:

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

In questo modo non perdi il riferimento all'oggetto originale.

Nel 2012 esiste il nuovo metodo bind di ES5 che ci consente di farlo in modo più elaborato:

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

Altri suggerimenti

Ho scavato in profondità per capire questo comportamento particolare e penso di aver trovato una buona spiegazione.

Prima di capire perché non sei in grado di alias document.getElementById , cercherò di spiegare come funzionano le funzioni / gli oggetti JavaScript.

Ogni volta che invochi una funzione JavaScript, l'interprete JavaScript determina un ambito e lo passa alla funzione.

Valuta la seguente funzione:

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

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

Questa funzione è dichiarata nell'ambito di Window e quando la invochi, il valore di this all'interno della funzione di somma sarà l'oggetto globale Window .

Per la funzione 'sum', non importa quale sia il valore di 'this' in quanto non lo sta usando.


Valuta la seguente funzione:

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.

Quando chiami la funzione dave.getAge, l'interprete JavaScript vede che stai chiamando la funzione getAge sull'oggetto dave , quindi imposta this su dave e chiama la funzione getAge . getAge () restituirà correttamente 100 .


Potresti sapere che in JavaScript puoi specificare l'ambito usando il metodo apply . Proviamolo.

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.

Nella riga sopra, invece di lasciare che JavaScript decida l'ambito, si passa l'ambito manualmente come oggetto bob . getAge ora restituirà 200 anche se hai "pensato" di aver chiamato getAge sull'oggetto dave .


Qual è il punto di tutto quanto sopra? Le funzioni sono "liberamente" associate ai tuoi oggetti JavaScript. Per esempio. puoi fare

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

Facciamo il prossimo passo.

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

dave.getAge(); //returns 100;
ageMethod(); //returns ?????
L'esecuzione di

?? ageMethod genera un errore! Che cosa è successo?

Se leggi attentamente i miei punti precedenti, noterai che il metodo dave.getAge è stato chiamato con dave come oggetto questo mentre JavaScript potrebbe non determina lo 'scope' per l'esecuzione di ageMethod . Quindi ha passato 'Window' globale come 'this'. Ora poiché window non ha una proprietà birthDate , l'esecuzione di ageMethod fallirà.

Come risolvere questo problema? Semplice,

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

Tutto quanto sopra aveva senso? In tal caso, sarai in grado di spiegare perché non sei in grado di alias document.getElementById :

var $ = document.getElementById;

$('someElement'); 

$ viene chiamato con finestra come this e se l'implementazione di getElementById prevede this essere documento , fallirà.

Ancora una volta per risolvere questo problema, puoi farlo

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

Quindi perché funziona in Internet Explorer?

Non conosco l'implementazione interna di getElementById in IE, ma un commento nell'origine jQuery (implementazione del metodo inArray ) dice che in IE, window == documento . In tal caso, aliasing document.getElementById dovrebbe funzionare in IE.

Per illustrare ulteriormente questo, ho creato un esempio elaborato. Dai un'occhiata alla funzione Person di seguito.

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

}

Per la funzione Person sopra, ecco come si comporteranno i vari metodi getAge .

Creiamo due oggetti usando la funzione 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.

Semplicemente, il metodo getAge ottiene l'oggetto yogi come this e genera 100 .


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

L'interprete JavaScript imposta l'oggetto window come this e il nostro metodo getAge restituirà -1 .


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

Se impostiamo l'ambito corretto, puoi usare il metodo ageAlias ??.


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

Se passiamo un oggetto di un'altra persona, calcolerà comunque l'età correttamente.

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

La funzione ageSmarter ha catturato l'oggetto this originale, quindi ora non devi preoccuparti di fornire l'ambito corretto.


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

Il problema con ageSmarter è che non puoi mai impostare l'ambito su qualche altro oggetto.


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

La funzione ageSmartest utilizzerà l'ambito originale se viene fornito un ambito non valido.


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

Sarai comunque in grado di passare un altro oggetto Person a getAgeSmartest . :)

Questa è una risposta breve.

Quanto segue crea una copia di (un riferimento a) la funzione. Il problema è che ora la funzione è sull'oggetto window quando è stata progettata per vivere sull'oggetto document .

window.myAlias = document.getElementById

Le alternative sono

  • per usare un wrapper (già citato da Fabien M & # 233; nager)
  • oppure puoi usare due alias.

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

Un'altra breve risposta, solo per il wrapping / aliasing console.log e metodi di registrazione simili. Si aspettano tutti di trovarsi nel contesto console .

Questo è utilizzabile quando si racchiudono console.log con alcuni fallback, nel caso in cui tu o i tuoi utenti avete riscontrato problemi quando . Tuttavia, questa non è una soluzione completa a questo problema, poiché deve essere espansa i controlli e un fallback - il tuo chilometraggio può variare.

Esempio di avvertenze

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

Quindi usalo come al solito

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

Se preferisci vedere i tuoi argomenti di registrazione racchiusi in un array (lo faccio, la maggior parte delle volte), sostituisci .apply (...) con .call (...) .

Dovrebbe funzionare con console.log () , console.debug () , console.info () , console. warn () , console.error () . Vedi anche console su MDN .

Oltre ad altre ottime risposte, esiste un semplice metodo jQuery $ .proxy .

Puoi alias in questo modo:

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

o

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

In realtà non puoi " puro alias " una funzione su un oggetto predefinito. Pertanto, il più vicino all'alias che puoi ottenere senza wrapping è rimanere nello stesso oggetto:

>>> document.s = document.getElementById;
>>> document.s('myid');
<div id="myid">
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top