call()
call()方法用来改变函数的this指向,它接收多个参数,第一个参数为执行作用域,第二个及以后的参数是传递给函数的参数
call方法定义到函数原型对象上,以便所有的函数使用:
// 全局添加一个_call方法
Function.prototype._call = function(obj) {
}
它接收一个参数obj,这个obj就是函数的执行作用域。如果_call()方法没有传递任何参数,则默认执行作用域为window对象
// 全局添加一个_call方法
Function.prototype._call = function(obj) {
// 参数是否存在,如果存在则转为Object类型,否则直接取window对象为默认对象
var _obj = obj ? Object(obj) : window //新增
}
让函数的执行作用域为参数obj
// 全局添加一个_call方法
Function.prototype._call = function(obj) {
// 参数是否存在,如果存在则转为Object类型,否则直接取window对象为默认对象
var _obj = obj ? Object(obj) : window
_obj.fn = this //新增
}
获取其他函数参数,可以采用ES6的数组方法遍历arguments,最后,要把obj对象中的fn方法移除
// 全局添加一个$call方法
Function.prototype._call = function (obj) {
// 参数是否存在,如果存在则转为Object类型,否则直接取window对象为默认对象
var _obj = obj ? Object(obj) : window
// 把调用$call()方法的那个函数保存在目标对象中
_obj.fn = this
// 保存参数的数组
var argArr = Array.from(arguments).slice(1) //新增
// 执行obj的fn方法,把arg拆分
_obj.fn(...argArr) //新增
// 执行完之后删除这个方法
delete _obj.fn //新增
}
让我们来测试一下
<script>
window.c = 1314
function add(a,b){
console.log(this);
return a+b+this.c
}
let obj = {
c:520
}
console.log('-------1',add._call(obj,10,20));
console.log('-------2',add._call(null,10,20));
</script>
apply()
有了上面_call()的实现,那么_apply()就很好编码了
Function.prototype._apply = function(obj, argArr) {
// 如果obj不存在则默认window对象
var _obj = obj ? obj : window
// 给_obj添加fn方法
_obj.fn = this
// 获取第二个数组参数
var args = arguments[1]
// 当这个参数数组不存在或者为空时,直接执行函数,否则把数组扩展运算后传递给函数并执行
if (!args || args.length == 0) {
_obj.fn()
} else {
// 执行obj的fn方法,把arg拆分
_obj.fn(...args)
}
// 移除这个方法
delete _obj.fn
}
测试
结果也是没问题的
bind()
bind()依然用来改变函数this指向,但是它不会像call()与apply()方法那样立即执行这个函数,而是返回一个新的函数给外部,外部用一个变量去接收这个新函数并执行。新函数内部的this指向方法的第一个函数,后面逐个列举的任意个数参数将作为目标函数的参数一一对应传入。之后执行新函数相当于执行了目标函数。
bind()方法返回的那个函数不仅仅可以作为普通函数调用,还可以作为一个构造函数被调用
bind()方法的参数具有一个特性,就是函数柯里化,简单来说就是保留一个参数的位置,再第二次传参的时候自动把参数存入到这个位置中
Function.prototype._bind = function (obj) {
// 如果obj不存在则默认window对象
var _obj = obj ? obj : window
// 截取传给函数的参数
var args = Array.from(arguments).slice(1) //新增
// 保存这个函数,以便后续使用
var _fn = this //新增
// 创建一个待会儿返回出去的函数,这个函数会赋到外部变量中
var bindFn = function () {
// _bind方法会返回一个函数,这里获取_bind方法返回的函数的参数保存起来
var newArgs = Array.prototype.slice.call(arguments)
// 通过call去改变this指向,使新函数内部的this指向方法的第一个函数
// 后面逐个列举的任意个数参数将作为目标函数的参数一一对应传入,实现函数柯里化
return _fn.call(_obj, ...args, ...newArgs) //新增
}
//返回一个新的函数给外部
return bindFn //新增
}
bind()返回的函数可以作为构造函数,那么它得继承调用它的那个函数的原型对象以及属性,这里创建一个媒介函数,用来实现寄生组合式继承:
Function.prototype._bind = function (obj) {
// 如果obj不存在则默认window对象
var _obj = obj ? obj : window
// 截取传给函数的参数
var args = Array.from(arguments).slice(1)
// 保存这个函数,以便后续使用
var _fn = this
// 创建一个待会儿返回出去的函数,这个函数会赋到外部变量中
var bindFn = function () {
// 获取_bind方法会返回一个函数,这里获取_bind方法返回的函数的参数
var newArgs = Array.prototype.slice.call(arguments)
// 通过call去改变this指向,使新函数内部的this指向方法的第一个函数
// 后面逐个列举的任意个数参数将作为目标函数的参数一一对应传入,实现函数柯里化
return _fn.call(_obj, ...args, ...newArgs)
}
// 创建一个中介函数,以便实现原型继承
var ProtoFn = function () { } //新增
ProtoFn.prototype = _fn.prototype //新增
bindFn.prototype = new ProtoFn() //新增
// 返回bindFn的函数给外部
return bindFn //新增
}