call,apply,bind的模拟实现

很多人都知道这三种方法怎用,但是都不知道他们的原理

call

  • 定义
call() 方法调用一个函数,其具有一个指定的this值和分别地提供的参数(参数的列表)。
复制代码

let name='憨蛋';

let person={
    name:'romin'
}

function say(){
    console.log('my name is'+this.name)
}

say.call(person);
// my name is romin
复制代码

call 改变了say函数的this指向,同时say函数执行。

那么我们就来实现它

  • 1、改变this指向,直接把 say 函数放进 person 对象中即可。
  • 2、函数执行,调用 person 中的 say 函数
let person={
    name:'romin',
    say:function(){
        console.log(this.name)
    }
}
person.say();
复制代码

由于上面的方法改变了 person 对象的结构,需要需要多余的 say 函数删除掉,要执行 delete person.say 的操作。

这时候,我们已经明确了该做的事情:

1、把待执行的函数(call前面的函数)放入指定的对象(call的第一个参数)中;

2、执行该函数;

3、删除对象中添加的函数;

Function.prototype.call = function(context){
    // 第一步
    //这里的this就是代调用的函数
    context.fn = this;
    // 第二步
    context.fn();
    // 第三步
    delete context.fn;
}
复制代码

call 中的第一个参数也可能是null,也可能是字符串,我们需要对 context 进行处理,否则 字符串上没法挂载 函数 fn

// 对context 做处理
context = context?Object(context):window;
复制代码

以上面的为例,call 中可能会有多个参数,除了context,剩余的参数都要交给 say 来处理的。

let name='憨蛋';

let person={
    name:'romin'
}

function say(animal,age){
    console.log(animal)
    console.log(age)
    console.log('my name is '+this.name+',我属'+animal);
}

say.call(person,'?',15);
// my name is romin,我属?
复制代码

call里的参数该怎么交给 say ,并且让它执行呢?使用 arguments 的话,它是类数组,和 call 的要求不一致,call 要求 一个个传参的。 拼出一个参数字符串来。

let args = [];
for(let i=1;i<arguments.length;i++){
    args.push('arguments['+i+']')
}

// ['arguments[1]','arguments[2]']

// args 和 字符串拼接,调用了它的 toString()  ,相当于 context.fn(arguments[1],arguments[1])
eval('context.fn('+ args +')')
复制代码

完整的实现:

Function.prototype.call = function(context){
    // 对context 做处理
    context = context?Object(context):window;
    // 第一步,挂载函数
    //这里的this就是代调用的函数
    context.fn = this;
    
    // 第二步,准备参数,然后让函数执行
    let args = [];
    for(let i=1;i<arguments.length;i++){
        args.push('arguments['+i+']')
    }
    let result = eval('context.fn('+ args +')')
    // 第三步
    delete context.fn;
    // 注意可能有返回值
    return result;
}

复制代码

apply

  • 定义
apply() 方法调用一个具有给定this值的函数,以及作为一个数组(或类似数组对象)提供的参数。
复制代码

applycall 类似,只是传参的不同而已,可以直接拿上面的代码进行改造

Function.prototype.apply = function(context,arr){
    // 对context 做处理
    context = context?Object(context):window;
    // 第一步,挂载函数
    //这里的this就是代调用的函数
    context.fn = this;
    let result;
    if(!arr){
        result = context.fn();
    }else{
        let args = [];
        for(let i=0;i<arr.length;i++){
            args.push('arr['+i+']')
        }
       result = eval('context.fn('+ args +')') 
    }
    // 第三步
    delete context.fn;
    // 注意可能有返回值
    return result;
}
复制代码

bind

  • 定义:
`bind()`方法创建一个新的函数,在调用时设置`this`关键字为提供的值。并在调用新函数时,将给定参数列表作为原函数的参数序列的前若干项。
复制代码

特点:改变 this 指向,同时返回一个函数(高阶函数),可以传入参数;

let name='憨蛋';

let person={
    name:'romin'
}

function say(animal,age){
    console.log(animal)
    console.log(age)
    console.log('my name is '+this.name+',我属'+animal);
}

let rominsay = say.bind(person,'?');
rominsay(15);
复制代码

它的模拟实现,如果不带参数列表:

Function.prototype.bind = function(context){
    let that = this;
    return function(){
        // 同样要注意可能有返回值
        return that.apply(context);
    }
}
复制代码

第二版,如果有参数:

Function.prototype.bind = function(context){
    let that = this;
    // 取到参数列表
    let bindArgs = Array.prototype.slice.call(arguments,1);
    return function(){
        // 取到调用时候的参数列表
        let args = Array.prototype.slice.call(arguments);
        // 同样要注意可能有返回值
        return that.apply(context,bindArgs.concat(args));
    }
}
复制代码

接下来请看,下面的例子使用原生的 bind 方法:

let person={
    name:'romin',
    gender:'男',
}

function Say(name,age){
    this.habit = '上班划水';
    console.log(this.gender);
    console.log(name);
    console.log(age);
}

Say.prototype.friend = '憨蛋';

let RominSay = Say.bind(person,'romin');

let say = new RominSay('15');

console.log(say.friend) // 憨蛋
console.log(say.gender);// undefined
console.log(say.habit)// 上班划水

复制代码

如果使用自己实现的方法,那么结果是

console.log(say.friend) // undefined
console.log(say.gender);// undefined
console.log(say.habit)// undefined
复制代码

那么就会有两个疑惑:

  • 1、原生的方法中gender 失效了
  • 2、我自己写的friend 属性没有继承成功;
  • 3、我自己写的habit 也没有取到;

但是请注意下面的一个问题:

如果被绑定的函数被new ,那么 返回的函数中的this 是当前函数的实例
复制代码

套用上面的话,RominSaynew 出了一个 say, 那么 this 就是 当前 RominSay 的实例say(而不应该是 person 了),那么就能通过原型链找到 friend 属性,

对上面的实现方法进行改造

Function.prototype.bind = function(context){
    let that = this;
    // 取到参数列表
    let bindArgs = Array.prototype.slice.call(arguments,1);
    function newFun(){
        // 取到调用时候的参数列表
        let args = Array.prototype.slice.call(arguments);
        //当newFun作为构造函数时,this 指向实例,如果不是,this还指向 context
        return that.apply(this instanceof newFun ?this:context,bindArgs.concat(args));
    }
    // 修改返回函数的 prototype 为绑定函数的 prototype,实例就可以继承绑定函数的原型中的值
    newFun.prototype = this.prototype;
    return newFun;
}

复制代码

转载于:https://juejin.im/post/5cb56ce36fb9a0688225a411

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
智慧校园建设方案旨在通过融合先进技术,如物联网、大数据、人工智能等,实现校园的智能化管理与服务。政策的推动和技术的成熟为智慧校园的发展提供了基础。该方案强调了数据的重要性,提出通过数据的整合、开放和共享,构建产学研资用联动的服务体系,以促进校园的精细化治理。 智慧校园的核心建设任务包括数据标准体系和应用标准体系的建设,以及信息化安全与等级保护的实施。方案提出了一站式服务大厅和移动校园的概念,通过整合校内外资源,实现资源共享平台和产教融合就业平台的建设。此外,校园大脑的构建是实现智慧校园的关键,它涉及到数据中心化、数据资产化和数据业务化,以数据驱动业务自动化和智能化。 技术应用方面,方案提出了物联网平台、5G网络、人工智能平台等新技术的融合应用,以打造多场景融合的智慧校园大脑。这包括智慧教室、智慧实验室、智慧图书馆、智慧党建等多领域的智能化应用,旨在提升教学、科研、管理和服务的效率和质量。 在实施层面,智慧校园建设需要统筹规划和分步实施,确保项目的可行性和有效性。方案提出了主题梳理、场景梳理和数据梳理的方法,以及现有技术支持和项目分级的考虑,以指导智慧校园的建设。 最后,智慧校园建设的成功依赖于开放、协同和融合的组织建设。通过战略咨询、分步实施、生态建设和短板补充,可以构建符合学校特色的生态链,实现智慧校园的长远发展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值