一、call和apply的特點
- 可以改變我們當前函式的this指向
- 還會讓當前函式執行
var person = {
value : 1
}
function bar() {
console.log(this.value)
}
// 如果不對this進行繫結執行bar() 會返回undefined,this指向window
bar.call(person) // 1複製程式碼
試想一下當呼叫call的時候也就類似於
var person = { value: 1,
bar: function() {
console.log(this.value)
}
}
person.bar() // 1複製程式碼
這樣就把 this 指向到了 person上,但是這樣給 person 物件加了一個屬性,不太合適,不過不要緊,執行完刪除這個屬性就可以實現了。
也就是說步驟其實是這樣的
- 將函式設為物件的屬性
- 執行這個函式
- 刪除這個函式
1、call模擬實現
Function.prototype.call = function(context){
context = context ? Object(context) : window;//不傳遞context預設為window
context.fn = this;//this也就是呼叫call的函式
let args = [...arguments].slice(1);
let r = context.fn(...args);
delete context.fn;
return r;
}複製程式碼
2、apply模擬實現
apply
的方法和 call
方法的實現類似,只不過是如果有引數,以陣列形式進行傳。
Function.prototype.apply= function(context,args){
context = context ? Object(context) : window;//不傳遞context預設為window
context.fn = this;
if(!args){
return context.fn();
}
let r = context.fn(...args);
delete context.fn;
return r;
}複製程式碼
二、bind的特點
- bind方法可以繫結this指向 繫結引數
- bind方法返回一個繫結後的函式(高階函式)
- 呼叫繫結後的方法,會讓原方法執行
- 如果繫結的函式被new了,當前函式的this就是當前的例項
- new出來的結果,可以找到原有類的原型
1、bind方法模擬實現第一步
用法:
let obj = {
name:'gjf'
}
function fn(){
console.log(this.name)
}
let bindFn = fn.bind(obj); //返因一個繫結後的方法
findFn() //用繫結後的方法,讓原方法執行複製程式碼
實現:
Function.prototype.bind = function(context){
let that = this;
return function(){
return that.apply(context);
}
}複製程式碼
這樣實現了最簡單的改變this指向的bind,但是這樣還遠遠不夠,因為bind還可以繫結引數;
2、bind方法模擬實現第二步
方法傳參可以分兩批傳,一批可以先在bind方法裡面先繫結好,另一批在呼叫的時候傳參,例如以下示例;
用法:
let obj = {
name:'gjf'
}
function fn(name,age){
console.log(this.name+'養了一隻'+name+age+'歲了')
}
let bindFn = fn.bind(obj,'貓'); //返因一個繫結後的方法
findFn(8) //用繫結後的方法,讓原方法執行複製程式碼
實現:
Function.prototype.bind = function(context){
let that = this;
let bindArgs = Array.prototype.slice.call(argument,1)//['貓']
return function(){
let args = Array.prototype.slice.call(argument);
return that.apply(context,bindArgs.concat(args));
}
}複製程式碼
3、bind方法模擬實現第三步
呼叫bind返回的函式除了可以直接呼叫,還可以把函式當成一個類來呼叫;原函式上繫結了屬性,new出來的例項上能否訪問。
用法:
fn.prototype.flag = '哺乳類'; //原函式上繫結了屬性
let findFn = fn.bind(obj,'貓');
let instance = new findFn(8);//如果繫結的函式被new了,當前函式的this就是當前的例項
console.log(instance.flag) //undefined複製程式碼
實現:
Function.prototype.bind = function(context){
let that = this;
let bindArgs = Array.prototype.slice.call(argument,1)//['貓']
function Fn(){} //Object.create的原理
function fBound(){
let args = Array.prototype.slice.call(argument);
return that.apply(this instanceof fBound ? this:context,bindArgs.concat(args));
}
Fn.prototype = this.prototype;
fBound.prototype = new Fn();
return fBound;
}複製程式碼
這裡,我們js裡的三種改變this指向的方法就實現啦。。。。。