この質問 を読んでいて、関数ラッパーメソッドではなくエイリアスメソッドを試してみたかったのですが、Firefox 3または3.5beta4、またはGoogle Chromeで動作させることができなかったようです。 、デバッグウィンドウとテストWebページの両方で。
Firebug:
>>> window.myAlias = document.getElementById
function()
>>> myAlias('item1')
>>> window.myAlias('item1')
>>> document.getElementById('item1')
<div id="item1">
Webページに配置すると、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年には、ES5の新しいbind
メソッドがあり、これをより手の込んだ方法で実行できます。
>>> $ = document.getElementById.bind(document)
>>> $('bn_home')
<body id="bn_home" onload="init();">
この特定の動作を理解するために深く掘り下げたところ、良い説明を見つけたと思います。
_document.getElementById
_をエイリアスできない理由を理解する前に、JavaScript関数/オブジェクトがどのように機能するかを説明します。
JavaScript関数を呼び出すたびに、JavaScriptインタープリターがスコープを決定し、それを関数に渡します。
次の機能を検討してください。
_function sum(a, b)
{
return a + b;
}
sum(10, 20); // returns 30;
_
この関数はWindowスコープで宣言されており、呼び出すと、sum関数内のthis
の値はグローバルな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関数を呼び出すと、JavaScriptインタープリターはdave
オブジェクトでgetAge関数を呼び出していることを認識するため、this
をdave
に設定し、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
は、getAge
オブジェクトでdave
を呼び出したときに「_200
_」を返すようになりました。
上記のすべてのポイントは何ですか?関数は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
をthis
オブジェクトとして_dave.getAge
_メソッドが呼び出されたのに対し、JavaScriptはageMethod
実行の「スコープ」を決定できなかったことに注意してください。したがって、グローバルな「ウィンドウ」を「this」として渡しました。現在、window
にはbirthDate
プロパティがないため、ageMethod
の実行は失敗します。
これを修正する方法は?シンプル、
_ageMethod.apply(dave); //returns 100.
_
上記のすべてが理にかなっていますか?存在する場合は、_document.getElementById
_をエイリアスできない理由を説明できます。
_var $ = document.getElementById;
$('someElement');
_
_$
_がwindow
としてthis
として呼び出され、getElementById
実装がthis
がdocument
であると予期している場合、失敗します。
再度これを修正するには、次のようにします
_$.apply(document, ['someElement']);
_
では、なぜInternet Explorerで機能するのでしょうか?
IEでのgetElementById
の内部実装はわかりませんが、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
関数を使用して2つのオブジェクトを作成しましょう。
_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
_
JavaScriptインタープリターは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
選択肢は
または、2つのエイリアスを使用できます。
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()
で動作するはずです。 MDNの console
も参照してください 。
他の優れた答えに加えて、単純なjQueryメソッド $。proxy があります。
次のようにエイリアスできます:
myAlias = $.proxy(document, 'getElementById');
または
myAlias = $.proxy(document.getElementById, document);