まず、4種類の thisの縛りを明確にしましょう。
1.デフォルトバインディング
スタンドアロン関数が呼び出されると、thisはグローバル・オブジェクトを指します。 ストリクト・モードを使用している場合、グローバル・オブジェクトはデフォルトのバインディングを使用できず、thisはundefinedにバインドされてエラーをスローします。
2.暗黙のバインディング
関数が参照プロパティとしてオブジェクトに追加されると、暗黙のバインディングルールは、関数呼び出しの thisをこのコンテキストオブジェクトにバインドします。
3.バインディング表示
関数を呼び出す際に thisをバインドする、つまり呼び出される関数の thisの値を指定するには、apply callメソッドを使用します。
4.新しいバインディング
これは、new 演算子を使うときの this バインディングです。
以上の4つのルールは、上から下へと優先順位が上がっていきます。
次に、バインド機能には3つのファンクションポイントがあります。
1.元の関数の thisポインティングを変更します。つまり、コンテキストをバインドして、元の関数のコピーを返します。
2.bind関数が呼び出されると、bindするための追加引数が実引数としてboundメソッドに渡されます。
3.バインドされた関数は、 new 演算子を使ってもオブジェクトを生成することができ、元の関数がコンストラクタであるかのように振る舞い、thisArgパラメータは無効であることに注意してください。つまり、new演算子はthisポインタの変更よりも優先されます。
//関数のプロトタイプにこのメソッドを定義して、第1引数としてコンテキスト・オブジェクトを受け取る。
Function.prototype.mybind=function(context){
// 関数が型であるかどうかの判定は、直接的に終了するわけではない。
if(typeOf this!=='function')return false;
let _self=this;
//最初のコンテキスト引数以外の引数を取得する
let arg=[...arguments].slice(1);
//新しい関数を定義すると、applyメソッドまたはcallメソッドを使用して、そのthisがcontextに変更される。
let fn=function(){
//ここでの引数はbind return関数の引数で、上の引数とつなぎ合わせて完全な引数セットになる。
let arg2=arg.concat([...arguments])
//bindがnewに遭遇すると、bindで指定されたthisは無効になるが、渡された引数は有効なままである。
//new演算子に遭遇したかどうかは、fnメソッドのプロトタイプ・チェインをチェックして判断する必要がある。
if(this instanceof fn){
return _self.apply(_self,arg2)
}else{
return _self.apply(context,arg2)
}
}
// 戻り関数のプロトタイプを束縛関数のプロトタイプに変更することで、インスタンスは束縛関数のプロトタイプの値を継承することができる。
fn.prototype=_self.prototype
return fn
}
let foo={value:'fooValue'}
function fa1(name,age){
console.log(this.value)
console.log(name,age)
}
fa1.prototype.friend='李磊'
let ffa1=fa1.mybind(foo,' ')
ffa1('100')
//'fooValue' thisfooを指す
//' '
//'100'
//new
let gg=new ffa1('gg ')
console.log(gg.friend)
//undefined これはggを指すので、ggの下に値はない。
//' '
//'gg '
//'李磊'