前言
第一版是一年前发的,现在再看一遍实现方法,感觉有些地方没有考虑周全,真是每看一遍收获都不一样啊~现在就在原来的基础上更改一下。
这三个方法都是改变其函数this指向的,首先给出call/apply/bind
方法的区别:
bind
:不会立即执行函数,并且返回了一个函数,如果形参有多个的情况下,实参还可以分开传送。call
:立即执行函数,传参形式为参数序列apply
:立即执行函数,传参形式为数组
基本思路
bind
运用柯里化函数的思想,执行bind()之后,返回了一个新的函数,也是闭包的一个运用场景。
注意:
-
当 bind 返回的函数作为构造函数的时候,让这个返回的函数指向特定的this不可行,应该是指向那个实例的,所以要考虑。
-
因为bing是返回的一个函数,所以这个返回的函数就得考虑到原型的问题,不然原函数经过bind一顿操作,原型上的属性都不见了....,这是个细节的地方,需要注意一下。
-
call
主要是搞清楚执行call
时,干了啥,1. 把this
方法执行,这是的this
就是那个调用call
方法的函数,这里假设为fn1
。 2. 将这个fn1
的this
指向改为你指定的那个。
重要:
-
bar.call(obj):想让bar中的this指向obj,直接在obj对象里面创建一个bar函数。
-
apply
apply
和call
方法思路相同,只是传的参数类型不同。
上代码
let obj = {
fn(x, y) {
console.log(this, x, y)
}
}
运用自执行函数形成闭包,以免影响全局,保护了方法。
~function anonymous(proto) {
function bind(context) {
context = context == null ? window : context // 考虑特殊情况null和undefined
let _this = this
// bind函数可以分批次传参
let args = [].slice.call(arguments, 1)
//这里运用es5来处理,将类数组转换为数组。
//也可以写Array.prototype.slice.call(arguments, 1)
// console.log(args) [10,20]
return function proxy() {
var arg1 = [].slice.call(arguments, 0)
//判断返回的函数是否是一个构造函数,因为构造函数,可以通过new操作符,重定向this指向,则bind指定的this无效。
_this.apply(this instanceof proxy ? this : context, args.concat(arg1))
proxy.prototype = _this.prototype
}
}
function call(context, ...args) {
/* 干了三件事:
1. 将this执行,假设为fn1
2. 将fn1中的this指向context
3. 要考虑到特殊情况,如果context不是引用类型的话,则需要自己转换
*/
context = context == null ? window : context
const type = typeof context
if (type !== 'object' && type !== 'function' && type !== 'symble') {
//基本数据类型
switch (type) {
case ('number'): context = new Number(context); break;
case ('string'): context = new String(context); break;
case ('Boolean'): context = new Boolean(context); break;
}
}
context.$fn = this
const res = context.$fn(...args)
return res
}
function apply(context, args) {
context = context == null ? window : context
const type = typeof context
if (type !== 'object' && type !== 'function' && type !== 'symble') {
//基本数据类型
switch (type) {
case ('number'): context = new Number(context); break;
case ('string'): context = new String(context); break;
case ('Boolean'): context = new Boolean(context); break;
}
}
context.$fn = this
const res = context.$fn(...args) //展开
return res
}
proto.bind = bind
proto.call = call
proto.apply = apply
}(Function.prototype)
测试:
setTimeout(obj.fn.bind(window, 10, 20), 1000)
obj.fn.call('4', 10, 20) // String {'4', $fn: ƒ} 10 20
obj.fn.apply('4', [10, 20]) // String {'4', $fn: ƒ} 10 20