原生实现call、apply、bind

原生实现call、apply、bind

call和apply都是使用一个指定的this值和对应的参数调用函数或方法,两者的区别就是传参的方式不一样,call的参数是一个个传入,而apply是传入一个数组
实现的原理:函数或方法调用的时候才决定this具体的指向。函数可以作为某个对象的方法调用,那么this就指向那个上级调用对象

实现的思路:
1、先判断绑定的this类型
2、this对象上的fn属性唯一
3、 执行原函数
4、删除挂载在this对象上的fn属性
5、返回执行后的结果

call实现

// this上挂载的fn属性的唯一

function uniqueFn(ctx) {
	var unique_fn = 'fn'
	if(ctx.hasOwnProperty(unique_fn)) {
		unique_fn =  unique_fn + Math.random()
	}
	return unique_fn
}
  1. es5方式(eval)
Function.prototype.call1 = function (ctx) {
	 ctx = ctx || window
	 var unique_fn = uniqueFn(ctx)
	 ctx[unique_fn] = this  // 保存执行的函数的引用
	 
	 var args = [], result
	 for(var i = 1; i < arguments.length; i++) {
		args.push('arguments['+i'+]')
	}
	result = eval('ctx[unique_fn] ('+args+')')
	delete ctx[unique_fn]
	return result
}
  1. es6方式
Function.prototype.call2 = function (ctx) {
	ctx = ctx || window
	let unique_fn = Symbol()
	ctx[unique_fn] = this
	
	let args = [].slice.call(arguments, 1)
	let result = ctx[unique_fn] (...args)
	delete ctx[unique_fn]
	return result
}

apply实现

apply的实现和call基本类似,只是传参的形式不一样,call是一个一个参数列出来,apply是参数组成数组进行传递

  1. es5方式(eval)
Function.prototype.apply1 = function (ctx, param) {
	 ctx = ctx || window
	 var unique_fn = uniqueFn(ctx)
	 ctx[unique_fn] = this  // 保存执行的函数的引用
	 
	 var args = [], result
	 for(var i = 0; i < param.length; i++) {
		args.push('param['+i'+]')
	}
	result = eval('ctx[unique_fn] ('+args+')')
	delete ctx[unique_fn]
	return result
}
  1. es6方式
Function.prototype.apply2 = function (ctx, param) {
	ctx = ctx || window
	let unique_fn = Symbol()
	ctx[unique_fn] = this
	
	let result = ctx[unique_fn] (...param)
	delete ctx[unique_fn]
	return result
}

bind实现

bind跟call和apply不一样,call和apply调用会立即执行,而bind会返回已绑定this的函数,bind返回的函数可以当做构造函数使用,如果是当构造函数使用,那么它绑定的this会失效

// 简易版bind

function bind(context, ...outerArgs) {
    const fn = this;
	return function  (...innerArgs) {
		fn.apply(context,  [...outerArgs, ...innerArgs])
	}
}

// 终极版bind,考虑到bind返回的函数可能当做构造函数使用

Function.prototype.bind = function (ctx) {
	// 先判读调用bind的是否是个函数
	if (typeof this !== 'function') {
		return new Error('this should is a function ')
	}
	var args = [].slice.call(arguments, 1)
	var self = this
	
	var fBound = function () {
		var bindArgs = [].slice.call(arguments)
		// 判断bind函数是当做构造函数使用还是普通函数使用
		return self.apply(this instanceof self ? this : ctx, args.concat(bindArgs))
	}
	// 实现对self函数原型继承
	var fNOP = function (){}
	fNOP.prototype = this.prototype
	fBound.prototype = new fNOP()
	return fBound
}

顺带说下this指向的问题:
this永远指向最后调用它的对象,可以通过以上的call,apply,bind三种方式改变this的指向.
说下三者的区别前下说下三者用来干吗?
call、apply、bind就是用来改变函数中的上下文,通俗的说就是改变函数内部this指向。那三者有什么区别呢?这三者的作用差不多,干的事也差不多,那到底有什么差异呢?

1. call和apply改变函数内部this指向后会执行函数,而bind改变函数内部this指向后会返回一个改变上下文指向后的函数
2. call和apply唯一的不同点就是参数的传递的方式,call函数第一个参数就是要改变上下文的对象,从第二个参数开始以参数列表形式展示;而apply函数的第一个参数也是要改变上下文的对象,第二个参数就是数组

参考资源:https://github.com/Abiel1024/blog/issues/16

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值