欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

如何实现call,apply,bind方法?

程序员文章站 2022-07-14 14:27:34
...

很久前写过一篇call,apply,bind的作用,最近学习的过程中看了一篇手写这三个方法的文章。才想起这么多年其实一直知其然不知所以然,还是要更深入原理才好,接下来整理一下我自己的写法。

实现call

首先我们看call的用法,比如fn.call(obj,a,b,...)。从中可以看书,call是函数的一个方法,所以我们把它加在Function.prototype上,传入上下文context,首先判断this是否是function,不是就提示错误。另外当context不传时默认为window。然后再处理传入的参数即可,代码如下:

Function.prototype.myCall = (context) {
	if(typeOf this !== 'function') {
		throw new TypeError('Error');
	}
	context = context || window;
	// 我们需要暂时将this挂载在context上
	// 为了确保不因为属性名冲突而污染context对象
	// 我们使用Symble来获取独一无二的值fn
	const fn = Symble('fn');
	// 然后挂载
	context[fn] = this;
	// 获取可能存在的后边的参数,传入fn执行
	const args = [...arguments].slice(1);
	const result = context.fn(...args);
	// 执行完删掉fn,保证context跟原来一样
	delete context[fn];
	// 返回结果
  	return result;
}

实现apply

实现了call方法之后,apply方法就很简单了,他俩的区别只在于参数而已,代码如下:

Function.prototype.myApply = (context) {
	if(typeOf this !== 'function') {
		throw new TypeError('Error');
	}
	context = context || window;
	const fn = Symble('fn');
	context[fn] = this;
	// 获取可能存在的后边的参数,传入fn执行
	const args = arguments[1];
	let result;
	if(args) {
		result = context.fn(...args);
	} else {
		result = context.fn();
	}
	delete context[fn];
  	return result;
}

实现bind

bind和之前两者的区别在于,他不会立即执行,只是绑定了上下文

  • 前几步和之前的实现大相径庭,就不赘述了
  • bind 返回了一个函数,对于函数来说有两种方式调用,一种是直接调用,一种是通过 new 的方式,我们先来说直接调用的方式
  • 对于直接调用来说,这里选择了 apply 的方式实现,但是对于参数需要注意以下情况:因为 bind 可以实现类似这样的代码f.bind(obj, 1)(2),所以我们需要将两边的参数拼接起来,于是就有了这样的实现args.concat(...arguments)
  • 最后来说通过 new 的方式,在之前的章节中我们学习过如何判断 this,对于 new 的情况来说,不会被任何方式改变 this,所以对于这种情况我们需要忽略传入的 this

所以代码如下:

Function.prototype.myBind = (context) {
	if(typeOf this !== 'function') {
		throw new TypeError('Error');
	}
	const fn = this
  	const args = [...arguments].slice(1);
  	return function F() {
		// 因为返回了一个函数,我们可以 new F(),所以需要判断
	    if (this instanceof F) {
	    	// F在this的原型链上,所以是new出来的
	    	// new 出来的新函数,他的this定在new的时候,此时忽略这个context
	    	return new fn(...args, ...arguments);
	    }
	    // 正常调用bind的时候,再给他apply返回
	    return fn.apply(context, args.concat(...arguments));
	}
}