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

函数中的this指向:

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

类数组对象arguments:

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

一、call

  • call(this, 参1, 参2, ...),第一个参数为this,后面是函数的一系列参数
  • 当第一个this参数为null、undefind时,默认this指向window
  • 函数立即调用
  • 原理:实际就是把函数放到call传入的第一个参数上,然后再调用该函数。
  • 实现:
/**
 * 手写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);
};

在这里插入图片描述

注意:这个 undefind,是由于我使用 let 声明的变量。var 命令和 function 命令声明的全局变量,依旧是顶层对象的属性。let 命令、const 命令、class 命令声明的全局变量,不属于顶层对象的属性,而是在一般声明环境 declsEnv 中。

从ES6开始,全局变量和顶层对象的属性开始分离,这意味着使用let和const声明的全局变量不再属于顶层对象的属性,如window对象。这是为了提供更好的模块化和封装,避免全局命名空间的污染。

二、apply

  • applyl(this, arr),第一个参数为this,第二个参数是一个参数数组
  • 当第一个this参数为null、undefind时,默认this指向window
  • 函数立即调用
  • 原理:和call类似,区别就是参数不同,call方法接受的参数是一个参数列表,而apply接受的是一个包含多个参数的数组。
  • 实现:
// 和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在该构造函数上)
  • 实现:
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-2alltop_positive~default-1-124293144-null-null.142v100pc_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

  • 19
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值