(十六)call、apply、bind介绍、区别和实现

函数中的this指向:

函数中的this指向是在函数被调用的时候确定的,也就是执行上下文被创建时确定的。在一个执行上下文中,this由调用者 提供,由调用函数的方式来决定。

类数组对象arguments:

arguments只在函数(除了箭头函数)中存在的类数组参数对象,储存了我们传入的所有参数。

一、call

  • call(this, 参1, 参2, ...),第一个参数为this,后面是函数的一系列参数
  • 当第一个this参数为null、undefind时,默认this指向window
  • 函数立即调用
  • 原理:实际就是把函数放到call传入的第一个参数上,然后再调用该函数。
  • 实现:
javascript 复制代码
/**
 * 手写call
 * @param {*} context
 * @param  {...any} args
 * @returns
 */
Function.prototype.mycall = function (context, ...args) {
  if (typeof this !== "function") {
    throw new TypeError("Error");
  }
  //context不传,默认window
  let _this = context || window;

  //假如原来的context上存在fn属性会产生冲突,暂存一下。
  let temp = null;
  if (_this.fn) {
    temp = _this.fn;
  }

  _this.fn = this;
  // 调用函数
  let res = _this.fn(...args);

  if (temp) {
    //恢复_this对象上的原来的fn属性
    _this.fn = temp;
  } else {
    //删除_this对象上的fn属性
    delete _this.fn;
  }

  return res;

// 测试
let num = 1;
let obj = {
  num: 2,
  fn: "this is obj.fn",
};
function test(a, b, c, d) {
  let num = 1;
  console.log(this.num, "test参数", a, b, c, d);
}
test(4, 3, 2, 1);
// 调用myCall函数
test.mycall(obj, 4, 3, 2, 1);

// 检查obj本身的fn是否被修改
console.log(obj.fn);
};

二、apply

  • applyl(this, arr),第一个参数为this,第二个参数是一个参数数组
  • 当第一个this参数为null、undefind时,默认this指向window
  • 函数立即调用
  • 原理:和call类似,区别就是参数不同,call方法接受的参数是一个参数列表,而apply接受的是一个包含多个参数的数组。
  • 实现:
javascript 复制代码
// 和myCall的不同之处1:参数
Function.prototype.myApply=function(context){
  	if(typeof this!== 'function'){
        throw new TypeError('type error')
    }
  	console.log("myApply", arguments, context, this);
  	let _this = context || window;

  	let temp = null;
  	if (_this.fn) {
    	temp = _this.fn;
  	}
  	_this.fn = this;
  	let res;
  	//   判断是否存在第二个参数
  	if (arguments[1]) {
    	res = _this.fn(...arguments[1]);
  	} else {
    	res = _this.fn();
  	}
 // 删除context对象上的fn属性
  	if (temp) {
    	_this.fn = temp;
  	} else {
    	delete _this.fn;
  	}
 	 return res;
}

// 测试
let num = 1;
let obj = {
  num: 2,
  fn: "this is obj.fn",
};
function test(a, b, c, d) {
  let num = 1;
  console.log(this.num, "test参数", a, b, c, d);
}
test(4, 3, 2, 1);
// 调用myCall函数
test.myApply(obj, [4, 3, 2, 1]);

// 检查obj本身的fn是否被修改
console.log(obj.fn);

结果:

三、bind

  • bind(this, 参1, 参2, ...),第一个参数为this,后面是函数的一系列参数
  • 当第一个this参数为null、undefind时,默认this指向window
  • bind方法的返回值是函数,不会立即调用
  • 原理:(1)bind返回的函数作为构造函数使用的时候,bind绑定的this会失效,到那时参数有效。(2)如何判断bind 是正常使用还是当构造函数,根据this。当为构造函数时,this指向实例对象(this的prototype在该构造函数上)
  • 实现:
javascript 复制代码
Function.prototype.myBind = function (_this) {
  if (typeof this !== "function") {
    throw new TypeError("_this must be a function");
  }
  //获取参数
  let args0 = [...arguments].slice(1);
  //保存this,如果作为构造函数使用,此时this会指向实例
  let that = this;
  let context = _this||window;
  return function Fn(...args) {
    // 如果是new的形式来使用绑定函数的
    if (this instanceof Fn) {
      return new that(...args0, ...args);
    } else {
      return that.call(context , ...args0, ...args);
    }
  };
};

//测试:
function Point(x, y) {
  this.x = x;
  this.y = y;
}

// 情况1:正常调用bind函数
let testObj = {};
let MyPoint = Point.myBind(testObj, 0);
MyPoint(1);
console.log(testObj);
//情况2:bind返回的函数作为构造函数
let newObj = new MyPoint(2);
console.log(newObj);

结果:

四、区别:

1、相同点:

(1) call、apply、bind 都有改变this指向的作用。

(2)都可以给参数传参

2、不同点:

(1)call、bind 的第二个、后续参数是多个;而apply接收的第二参数是数组。

(2)call、apply会立即执行参数;而bind不会立即执行,得再调用才能执行。

参考:

1、https://blog.csdn.net/weixin_51472145/article/details/132566180

2、https://blog.csdn.net/weixin_40856652/article/details/124293144?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522171348813116800178533534%2522%252C%2522scm%2522%253A%252220140713.130102334...%2522%257D&request_id=171348813116800178533534&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-124293144-null-null.142^v100^pc_search_result_base1&utm_term=call%E3%80%81apply%E5%92%8Cbind%E7%9A%84%E5%8C%BA%E5%88%AB&spm=1018.2226.3001.4187

3、https://blog.csdn.net/sinat_41904410/article/details/104396112

相关推荐
逆旅行天涯3 分钟前
【Threejs】从零开始(六)--GUI调试开发3D效果
前端·javascript·3d
Buleall5 分钟前
期末考学C
java·开发语言
重生之绝世牛码7 分钟前
Java设计模式 —— 【结构型模式】外观模式详解
java·大数据·开发语言·设计模式·设计原则·外观模式
小蜗牛慢慢爬行13 分钟前
有关异步场景的 10 大 Spring Boot 面试问题
java·开发语言·网络·spring boot·后端·spring·面试
Algorithm157623 分钟前
云原生相关的 Go 语言工程师技术路线(含博客网址导航)
开发语言·云原生·golang
shinelord明32 分钟前
【再谈设计模式】享元模式~对象共享的优化妙手
开发语言·数据结构·算法·设计模式·软件工程
Monly2139 分钟前
Java(若依):修改Tomcat的版本
java·开发语言·tomcat
boligongzhu40 分钟前
DALSA工业相机SDK二次开发(图像采集及保存)C#版
开发语言·c#·dalsa
Eric.Lee202140 分钟前
moviepy将图片序列制作成视频并加载字幕 - python 实现
开发语言·python·音视频·moviepy·字幕视频合成·图像制作为视频
7yewh42 分钟前
嵌入式Linux QT+OpenCV基于人脸识别的考勤系统 项目
linux·开发语言·arm开发·驱动开发·qt·opencv·嵌入式linux