博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
call,apply,bind的模拟实现
阅读量:5876 次
发布时间:2019-06-19

本文共 4055 字,大约阅读时间需要 13 分钟。

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

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

完整的实现:

Function.prototype.call = function(context){    // 对context 做处理    context = context?Object(context):window;    // 第一步,挂载函数    //这里的this就是代调用的函数    context.fn = this;        // 第二步,准备参数,然后让函数执行    let args = [];    for(let i=1;i

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

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);// undefinedconsole.log(say.habit)// 上班划水复制代码

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

console.log(say.friend) // undefinedconsole.log(say.gender);// undefinedconsole.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

你可能感兴趣的文章
vue-04-组件
查看>>
Golang协程与通道整理
查看>>
解决win7远程桌面连接时发生身份验证错误的方法
查看>>
C/C++ 多线程机制
查看>>
js - object.assign 以及浅、深拷贝
查看>>
python mysql Connect Pool mysql连接池 (201
查看>>
Boost在vs2010下的配置
查看>>
一起谈.NET技术,ASP.NET伪静态的实现及伪静态的意义
查看>>
20款绝佳的HTML5应用程序示例
查看>>
string::c_str()、string::c_data()及string与char *的正确转换
查看>>
11G数据的hive初测试
查看>>
如何使用Core Text计算一段文本绘制在屏幕上之后的高度
查看>>
==和equals区别
查看>>
2010技术应用计划
查看>>
XML 节点类型
查看>>
驯服 Tiger: 并发集合 超越 Map、Collection、List 和 Set
查看>>
Winform开发框架之权限管理系统改进的经验总结(3)-系统登录黑白名单的实现...
查看>>
Template Method Design Pattern in Java
查看>>
MVC输出字符串常用四个方式
查看>>
LeetCode – LRU Cache (Java)
查看>>