手写自己的call,我们要先通过call的使用,了解都需要完成些什么功能?

call()进行了调用,是个方法,已知是建立在原型上的,使用了多个参数(绑定的对象+传递的参数)。

我们把手写的函数起名为myCall,obj作为形参承载传过来的第一个参数(即绑定的对象)。

Function.prototype.myCall = function(obj){}

call的调用对this的指向进行了改变,而this是函数,这是前提(对this进行判断)。

Funtion.prototype.myCall = function(obj){
  // 判断调用对象是否为函数
  if(typeof this !== 'function'){
    console.error('type error')
  }
}

同理应当判断是否传入了参数,如果没有传入参数,则绑定的对象设置为window。

Funtion.prototype.myCall = function(obj){
  if(typeof this !== 'function'){
    console.error('type error')
}
  // 判断绑定的对象
  obj = obj || window;
}

要调用这个this方法,我们可以先将其作为对象的属性方法,然后调用。

Function.prototype.myCall = function(obj){
  if(typeof this !== 'function'){
    console.error('type error!');
  }
  obj = obj || window;
  // 添加属性方法,并调用
  obj.fn = this;
  obj.fn();
}

call调用完后,拥有使用完方法后的返回值,所以肯定要将方法的执行结果保存并返回。

Function.prototype.mycall = function(obj){
  if(typeof this !== 'function'){
    console.error('type error!')
  }
  obj = obj || window;
  obj.fn = this;
  // 将执行结果保存,并返回
  let result = obj.fn();
  return result;
}

在原对象中,并没有obj.fn属性,所以我们要将其进行删除。

Function.prototype.mycall = function(obj){
  if(typeof this !== 'function'){
    console.error('type error!')
  }
  obj = obj || window;
  obj.fn = this;
  let result = obj.fn();
  // 删除context.fn属性
  delete obj.fn;
  return result;
}

最后考虑下方法中使用的参数(从传递过来的第二个参数开始),通过slice()进行切割,并拼凑为数组。

Function.prototype.mycall = function(obj){
  if(typeof this !== 'function'){
    console.error('type error!')
  }
  // 获取正确参数
  let args = [...arguments].slice(1)
  obj = obj || window;
  obj.fn = this;
  let result = obj.fn(...args);
  delete obj.fn;
  return result;
}

最后通过一个例子,来验证是否达到call()的功能要求。

Function.prototype.myCall = function(obj) {
  if (typeof this !== 'function') {
    console.error('type error!');
  }
  obj = obj || window;
  let args = [...arguments].slice(1);
  obj.fn = this;
  let result = obj.fn(...args)
  delete obj.fn;
  return result;
}
let dog = {
  name: '狗',
  eat(food1, food2) {
    console.log(this.name + '爱吃' + food1 + food2);
  }
}
let cat = {
  name: '猫',
}
dog.eat.call(cat, '鱼', '肉'); // 猫爱吃鱼肉
dog.eat.myCall(cat, '鱼', '肉'); // 猫爱吃鱼肉

另外两篇:'对apply()函数的分析' 和 '对bind()函数的分析' 。

理解并手写 call() 函数的更多相关文章

  1. 理解并手写 bind() 函数

    有了对call().apply()的前提分析,相信bind()我们也可以手到擒来. 参考前两篇:'对call()函数的分析' 和 '对apply()函数的分析',我们可以先得到以下代码: Functi ...

  2. 理解并手写 apply() 函数

    apply()函数,在功能上类似于call(),只是传递参数的格式有所不同. dog.eat.call(cat, '鱼', '肉'); dog.eat.apply(cat, ['鱼', '肉']); ...

  3. WPF启动流程-自己手写Main函数

    WPF一般默认提供一个MainWindow窗体,并在App.Xaml中使用StartupUri标记启动该窗体.以下通过手写实现WPF的启动. 首先先介绍一下VS默认提供的App.Xaml的结构,如下图 ...

  4. python 精华梳理(已理解并手写)--全是干货--已结

    基础部分 map,reduce,filter,sort,推导式,匿名函数lambda , 协程,异步io,上下文管理 自定义字符串转数字方法一不使用reduce import re def str2i ...

  5. 前端面试题整理——手写bind函数

    var arr = [1,2,3,4,5] console.log(arr.slice(1,4)) console.log(arr) Function.prototype.bind1 = functi ...

  6. 手写bind函数

    实现bind函数 参考MDN提供的Polyfill方案 Function.prototype.myBind = function(context){ //这里对调用者做一个判断,如果不是函数类型,直接 ...

  7. C++之手写strlen函数

    代码: int strlen(const char *str){ assert(str!=NULL); intlen=; while((*str++)!='\0') len++; return len ...

  8. js面试题之手写节流函数和防抖函数

    函数节流:不断触发一个函数后,执行第一次,只有大于设定的执行周期后才会执行第二次 /* 节流函数:fn:要被节流的函数,delay:规定的时间 */ function throttle(fn,dela ...

  9. 【OpenCV学习笔记】之六 手写图像旋转函数---万丈高楼平地起

    话说,平凡之处显真格,这一点也没错!  比如,对旋转图像进行双线性插值,很简单吧?  可,对我,折腾了大半天,也没有达到预期效果!  尤其是三个误区让我抓瞎好久: 1,坐标旋转公式.   这东西,要用 ...

随机推荐

  1. Java中float、double、long类型变量赋值添加f、d、L尾缀问题

    展开1. 添加尾缀说明 我们知道Java在变量赋值的时候,其中float.double.long数据类型变量,需要在赋值直接量后面分别添加f或F.d或D.l或L尾缀来说明.  其中,long类型最好以 ...

  2. webpack4 mini-css-extract-plugin

    在使用webpack的extract-text-webpack-plugin插件提取单独打包css文件时,报错,说是这个插件要依赖webpack3的版本. webpack4得使用mini-css-ex ...

  3. NoSQL 之 Redis配置与优化

    NoSQL 之 Redis配置与优化 1.关系数据库与非关系型数据库概述 2.关系数据库与非关系型数据库区别 3.非关系型数据库产生背景 4.Redis简介 5.Redis安装部署 6.Redis 命 ...

  4. shell脚本命令(sotr/unip/tr/cut/eval)与正则表达式

    shell脚本命令(sotr/unip/tr/cut/eval)与正则表达式 1.sort命令 概述: Linux sort命令用于将文本文件内容加以排序. sort命令可针对文本文件的内容,以行为单 ...

  5. go基础——goto语法

    package main import "fmt" func main() { a := 10 LOOP: for a < 20 { if a == 15 { a += 1 ...

  6. Kinect v2 + WPF获取RGB与Depth图像

    date: 2017-09-04 14:51:07 Kinect V2的Depth传感器采用的是「Time of Flight(TOF)」的方式, 通过从投射的红外线反射后返回的时间来取得Depth信 ...

  7. 技术管理进阶——谁能成为Leader,大Leader该做什么

    原创不易,求分享.求一键三连 两个故事 谁能成为Leader 之前接手了一块产品业务线,于是与原Leader说了下分工,大概意思是: 我是过来学习的,也能给团队带来更多的资源,团队内的工作你继续管理, ...

  8. Azure KeyVault(三)通过 Microsoft.Azure.KeyVault 类库在 .NET Core 上获取 Secrets

    一,引言 上一篇文章,我们介绍了 Azure Key Vault 在实际项目中的用途,Azure Key Vault 作为密钥管理的服务,我们可以很轻松的利用它创建和控制用于加密的密钥,和管理证书和机 ...

  9. Docker 镜像 层结构理解

    镜像到底是什么.镜像的层结构又是什么 通过docker history命令进行分析,镜像是一种其他镜像+文件+命令的组合. 这些镜像的加载.文件导入创建.命令是存在顺序关系的,所以也引出了层的概念. ...

  10. 【Kotlin】初识Kotlin(二)

    [Kotlin]初识Kotlin(二) 1.Kotlin的流程控制 流程控制是一门语言中最重要的部分之一,从最经典的if...else...,到之后的switch,再到循环控制的for循环和while ...