一部のJavascriptコード(具体的にはnode.js)では、コンテキストを変更せずに、不明な引数のセットを使用して関数を呼び出す必要があります。例えば:
function fn() {
var args = Array.prototype.slice.call(arguments);
otherFn.apply(this, args);
}
上記の問題は、apply
を呼び出すときに、最初の引数としてthis
を渡すことによってコンテキストを変更することです。呼び出されている関数にargs
を渡したいなし呼び出されている関数のコンテキストを変更します。私は本質的にこれをしたい:
function fn() {
var args = Array.prototype.slice.call(arguments);
otherFn.apply(<otherFn's original context>, args);
}
編集:私の特定の質問に関する詳細を追加します。接続に関連する他の情報の中でも、ソケット(socket.io)オブジェクトを含むClientクラスを作成しています。クライアントオブジェクト自体を介してソケットのイベントリスナーを公開しています。
class Client
constructor: (socket) ->
@socket = socket
@avatar = socket.handshake.avatar
@listeners = {}
addListener: (name, handler) ->
@listeners[name] ||= {}
@listeners[name][handler.clientListenerId] = wrapper = =>
# append client object as the first argument before passing to handler
args = Array.prototype.slice.call(arguments)
args.unshift(this)
handler.apply(this, args) # <---- HANDLER'S CONTEXT IS CHANGING HERE :(
@socket.addListener(name, wrapper)
removeListener: (name, handler) ->
try
obj = @listeners[name]
@socket.removeListener(obj[handler.clientListenerId])
delete obj[handler.clientListenerId]
clientListenerId
は、基本的に ここにある答え と同じカスタムの一意の識別子プロパティであることに注意してください。
私があなたを正しく理解している場合:
_ changes context
| n | y |
accepts array n | func() | func.call() |
of arguments y | ???????? | func.apply() |
_
PHPには、このための関数_call_user_func_array
_があります。残念ながら、JavaScriptはこの点で欠けています。 eval()
を使用してこの動作をシミュレートしているようです。
_Function.prototype.invoke = function(args) {
var i, code = 'this(';
for (i=0; i<args.length; i++) {
if (i) { code += ',' }
code += 'args[' + i + ']';
}
eval(code + ');');
}
_
はい、知っています。 eval()
が好きな人はいません。遅くて危険です。ただし、この状況では、少なくともすべての変数が関数内に含まれているため、クロスサイトスクリプティングについて心配する必要はありません。本当に、JavaScriptにこのためのネイティブ関数がないのは残念ですが、eval
があるのはこのような状況のためだと思います。
それが機能することの証明:
_function showArgs() {
for (x in arguments) {console.log(arguments[x]);}
}
showArgs.invoke(['foo',/bar/g]);
showArgs.invoke([window,[1,2,3]]);
_
Firefoxコンソールの出力:
_--
[12:31:05.778] "foo"
[12:31:05.778] [object RegExp]
[12:31:05.778] [object Window]
[12:31:05.778] [object Array]
_
'this
' is関数のコンテキストへの参照。それが本当にポイントです。
このような別のオブジェクトのコンテキストでそれを呼び出すことを意味する場合:
otherObj.otherFn(args)
次に、そのオブジェクトをコンテキストの代わりに使用します。
otherObj.otherFn.apply(otherObj, args);
それはそれであるはずです。
簡単に言えば、これを希望するものに割り当てるだけです。つまり、otherFn:です。
function fn() {
var args = Array.prototype.slice.call(arguments);
otherFn.apply(otherFn, args);
}
関数をオブジェクトにバインドし、バインドされた関数をどこでも使用する場合は、nullを指定してapplyを呼び出すことができますが、それでも正しいコンテキストを取得できます。
var Person = function(name){
this.name = name;
}
Person.prototype.printName = function(){
console.log("Name: " + this.name);
}
var bob = new Person("Bob");
bob.printName.apply(null); //window.name
bob.printName.bind(bob).apply(null); //"Bob"
最近は使用できます restparameters :
function fn(...args) {
otherFn(...args);
}
唯一の欠点は、fn
で特定のパラメータを使用する場合は、args
から抽出する必要があることです。
function fn(...args) {
let importantParam = args[2]; //third param
// ...
otherFn(...args);
}
試してみる例を次に示します(ESの次のバージョンで短くします)。
// a one-line "sum any number of arguments" function
const sum = (...args) => args.reduce((sum, value) => sum + value);
// a "proxy" function to test:
var pass = (...args) => sum(...args);
console.log(pass(1, 2, 15));
関数が呼び出されたときにJavaScriptで発生する可能性のあるコンテキストの変更を回避する方法のひとつは、this
が実行されないコンテキストで操作できるようにする必要がある場合、オブジェクトのコンストラクターの一部であるメソッドを使用することです。元のthis
識別子を格納するローカルプライベート変数を効果的に作成することにより、親オブジェクトを意味します。
JavaScriptでのスコープに関するほとんどの議論と同様に、これは完全には明確ではないことを認めます。そのため、これをどのように行ったかの例を次に示します。
function CounterType()
{
var counter=1;
var self=this; // 'self' will now be visible to all
var incrementCount = function()
{
// it doesn't matter that 'this' has changed because 'self' now points to CounterType()
self.counter++;
};
}
function SecondaryType()
{
var myCounter = new CounterType();
console.log("First Counter : "+myCounter.counter); // 0
myCounter.incrementCount.apply(this);
console.log("Second Counter: "+myCounter.counter); // 1
}
Javascript1.8.5で定義されているbind
関数を使用し、バインド関数を渡した元のthis
オブジェクトを取得できるようにしたいようですので、再定義することをお勧めします。 Function.prototype.bind
関数:
Function.prototype.bind = function (oThis) {
if (typeof this !== "function") {
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function () {},
fBound = function () {
return fToBind.apply(this instanceof fNOP && oThis
? this
: oThis,
aArgs.concat(Array.prototype.slice.call(arguments)));
};
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
/** here's the additional code **/
fBound.getContext = function() {
return oThis;
};
/**/
return fBound;
};
これで、bind
関数を呼び出した元のコンテキストを次のコマンドで取得できます。
function A() {
return this.foo+' '+this.bar;
}
var HelloWorld = A.bind({
foo: 'hello',
bar: 'world',
});
HelloWorld(); // returns "hello world";
HelloWorld.getContext(); // returns {foo:"hello", bar:"world"};
久しぶりにこの質問を思い出しました。今振り返ってみると、ここで本当に達成しようとしていたのは、Reactライブラリが自動バインドで機能する方法に似たものだったと思います。
基本的に、各関数は呼び出されるラップされたバインドされた関数です。
function SomeClass() {
};
SomeClass.prototype.whoami = function () {
return this;
};
SomeClass.createInstance = function () {
var obj = new SomeClass();
for (var fn in obj) {
if (typeof obj[fn] == 'function') {
var original = obj[fn];
obj[fn] = function () {
return original.apply(obj, arguments);
};
}
}
return obj;
};
var instance = SomeClass.createInstance();
instance.whoami() == instance; // true
instance.whoami.apply(null) == instance; // true
私はまだもっと適切なものを望んでいるので、これを答えとして受け入れるつもりはありません。しかし、これまでのところ、この質問に対するフィードバックに基づいて、現在使用しているアプローチは次のとおりです。
_Client.prototype.addListener
_または_Client.prototype.removeListener
_を呼び出すクラスについては、コンストラクターに次のコードを追加しました。
_class ExampleClass
constructor: ->
# ...
for name, fn of this
this[name] = fn.bind(this) if typeof(fn) == 'function'
message: (recipient, body) ->
# ...
broadcast: (body) ->
# ...
_
上記の例では、message
とbroadcast
は、インスタンス化されるときに常に新しいExampleClass
プロトタイプオブジェクトにバインドされ、元の質問のaddListener
コードを許可します。働くために。
なぜ私が次のようなことをしなかったのか疑問に思っている方もいらっしゃると思います。
_example = new ExampleClass
client.addListener('message', example.bind(example))
# ...
client.removeListener('message', example.bind(example))
_
問題は、.bind( )
が呼び出されるたびに、それが新しいオブジェクトになることです。つまり、次のことが当てはまります。
_example.bind(example) != example.bind(example)
_
そのため、removeListener
は正常に機能しないため、オブジェクトがインスタンス化されたときにメソッドを1回バインドします。