call()、appy()、bind()を手動で実現します.
3712 ワード
この文章は簡単にcall()、appy()、bind()を実現する構想を紹介しています.
callを実現します.
ターゲット関数のthisは、導入された最初のオブジェクトに向けられ、パラメータは一定の長さであり、直ちに実行されます.
構想を実現するは、オブジェクトの属性 として、オブジェクト関数を変更することができます.は、argmentsクラスの配列オブジェクトを利用してパラメータ不定長 を実現する.はオブジェクトの属性を増やすことができませんので、最後にdelete が必要です.
不安定なパラメータを関数にどうやって伝えますか?3つの方法があります.eval、apply、ES 6の構文です.
appyを実現する()
call()との違いは一つしかないです.appyの2番目のパラメータは配列です.
調整された関数と同じ関数を持つ新しい関数を返します.また、この新しい関数はnew演算子を使用することもできます.
構想を実現するは、新しい関数を返します. bit()着信パラメータの長さは一定ではなく、関数内蔵のアーグメンツオブジェクト配列を使用して、アラy.prototype.slice.call(argments,1)を利用して配列 に変換することができます.によって返された新しい関数では、appyを使用して、呼び出された関数のthis指向を変更し、argmentsから変換された配列をappyの2番目のパラメータ として使用する.戻る新しい関数もnewオペレータを使用することができますので、新しい関数の内部でnewオペレータが使用されているかどうかを判断する必要があります.使用すると、appyの最初のパラメータを新たに作成されたオブジェクトに設定します.もしない場合は、bind()を呼び出した時に入ってきたオブジェクトに設定します.. 注意したいのは、newオペレータが使われているかどうかをどう判断するかです.この問題を解決する前に、newオペレータを使う時に具体的に何をしましたか?以下はnewオペレータの簡単な実現過程です.
newオペレータの実現過程の第三段階では、構造関数constructorのthisをobjに向け、直ちに構造関数内部の動作を実行すると、関数内部の動作を実行する時にnewが使用されているかどうかを判断しないと、構造関数constructorの中のthisをobjに向けてしまうというプロセスが無効になります.具体的な原因は下記の模倣を見て、bind()のコードを実現してください.
また、上のbind()を実現したコードの中でappyを使用しているところは、元のコードに変えられます.
callを実現します.
ターゲット関数のthisは、導入された最初のオブジェクトに向けられ、パラメータは一定の長さであり、直ちに実行されます.
構想を実現する
不安定なパラメータを関数にどうやって伝えますか?3つの方法があります.eval、apply、ES 6の構文です.
eval('obj.fn('+args+'));
obj.fn.apply(obj,args);
obj.fn(...args);
Function.prototype.mycall = function(obj){
var args = Array.prototype.slice.apply(arguments,[1]);
obj.fn = this;
obj.fn(...args);//es6 , obj.fn.apply(obj,args);
delete obj.fn;
}
evalを使用すると、evalは一つの文字列を変数として解析しますので、もし入ってきたパラメータが文字列なら、xxx is not definedを報告します.解決方法は以下の通りです.Function.prototype.mycall = function(obj){
obj = obj||window;
var args = [];
for(var i = 1 ; i < arguments.length; i++) {
args.push('arguments[' + i + ']');
}
obj.fn = this;
eval('obj.fn('+args+'));
delete obj.fn;
}
appyを実現する()
call()との違いは一つしかないです.appyの2番目のパラメータは配列です.
Function.prototype.myapply = function(obj,arr){
obj.fn = this;
if(!arr){
obj.fn();
}else{
var args = [];
for(var i = 0; i < arr.length; i++) {
args.push('arr[' + i + ']');
}
eval('obj.fn('+args+')');
}
delete obj.fn;
}
bindを実現する()調整された関数と同じ関数を持つ新しい関数を返します.また、この新しい関数はnew演算子を使用することもできます.
構想を実現する
// new
function newFunc(constructor){
// : obj
var obj = {};
// : constructor obj
obj.__proto__ = constructor.prototype;
// : constructor this obj,
constructor.apply(obj);
// :
return obj;
}
newオペレータのプロセスは継承に相当し、新しく作成した構造関数の例は構造関数のプロトタイプチェーンにアクセスできる.newオペレータの実現過程の第三段階では、構造関数constructorのthisをobjに向け、直ちに構造関数内部の動作を実行すると、関数内部の動作を実行する時にnewが使用されているかどうかを判断しないと、構造関数constructorの中のthisをobjに向けてしまうというプロセスが無効になります.具体的な原因は下記の模倣を見て、bind()のコードを実現してください.
Function.prototype.testBind = function(object){
var that = this,
args = Array.prototype.slice.call(arguments,1),
bound = function(){
return that.apply(this instanceof fNOP?this:object||window,
args.concat.apply(Array.from(arguments)));
};
// fNOP, bound
var fNOP = function(){};
fNOP.prototype= that.prototype;
bound.prototype= new fNOP();
return bound;
}
ポイント:中継関数fNOPを作成して、boundに間接的にターゲット関数の原型を継承させます. bound.prototype=that.prototypeは直接的に値を割り当てた後、bound.prototypeとthat.prototypeは同じ内容を指しています.bound.prototypeを変えると、that.prototypeに直接影響を及ぼします.乗り換え関数を使います. bound.prototype=new fNOP()はbound.prototypeの_u uぽろぽろfnoP.prototypeを指し、その後fNOP.prototype= that.prototypeなので、bound.prototypeを変えることはthat.prototypeに影響しません.また、上のbind()を実現したコードの中でappyを使用しているところは、元のコードに変えられます.