模拟js中的call、apply和bind的实现

一、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. 将函数设为对象的属性
  2. 执行这个函数
  3. 删除这个函数

1、call模拟实现

Function.prototype.call = function(context){
    context = context ? Object(context) : window;//不传递context默认为window
    context.fn = this;//this也就是调用call的函数
    let args = [...arguments].slice(1);
    //利用数组toString的特性
    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(); 
    } 
    //利用数组toString的特性 
    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

实现:

iFunction.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指向的方法就实现啦。。。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值