前言
apply(thisArg, [])
call(thisSrg, arg1,arg2,...)
函数作用:改变this
的指向
函数区别:功能完全一样,接收的参数形式不一样,但性能有些许差别
call比apply出来的早一些,而且在算法步骤中,apply多了CreateListFromArrayLike的调用,其他的操作几乎是一样的(甚至apply仍然多了点操作)。从草案的算法描述来看,call性能 > apply性能。参数大于3个,call>apply,小于则忽略不计。
call
Function.prototype.myCall = function (context = window)
let _context = context // 第一个参数不传默认为window
_context.fn = this // 将foo作为target的私有方法, this改变
const args = [...arguments].slice(1) // 截取下标从1开始的参数 {'param1', 'param2'}
const result = _context.fn(...args) // 立即执行
delete _context.fn // 删除fn 不改变target
return result
}
apply
Function.prototype.myApply = function (context = window, arr) {
let _context = context// 第一个参数不传默认为window
_context.fn = this // 将fn作为target的私有方法, this改变
const result = arr.length ? _context.fn(...arr) : _context.fn() // 判断是否传入arr
delete _context.fn // 删除fn 不改变target
return result
}
bind
bind函数作用:改变this指向
注意一旦函数通过bind传递了有效的this对象,则该函数在运行期的this将指向这个对象,即使通过call或apply来试图改变this的指向也是徒劳的。
- 作为普通函数时
function func(...arg){
console.log(this);
console.log(...arg);
}
func.prototype.con = function(){
console.log('con');
};
let newFunc = func.bind({a: 1},1,2,3,4);
newFunc();
// 输出:
// { a: 1 }
// 1 2 3 4
- 作为构造函数
function func(...arg){
console.log(this);
console.log(...arg);
}
func.prototype.con = function(){
console.log('con');
};
let newFunc = func.bind({a: 1},1,2,3,4);
let fn = new newFunc();
// 输出:
// func {}
// 1 2 3 4
基于bind这些特性,来分析原生写bind
//原生实现bind
Function.prototype.myBind = function (context) {
// 判断调用对象是否为函数
if (typeof this !== "function") {
throw new Error("Type error");
}
// 获取参数
const args = [...arguments].slice(1);
const _this = this; // 保存this的值,代表调用bind的函数
//返回一个函数,此函数可以被作为构造函数调用,也可以作为普通函数调用
const Fn = function () {
// 根据调用方式,传入不同绑定值
// 当作为构造函数时,this 指向实例,不会被任何方式改变 this,要忽略传入的context上下文
return _this.apply(
this instanceof Fn ? this : context,
// bind可以分开传递参数(如f.bind(obj, 1)(2)),所以需要将参数拼接,这里使用apply,参数拼接成一个数组
args.concat(...arguments)//当前的这个 arguments 是指 Fn 的参数,也可以用剩余参数的方式
);
};
//对于构造函数,要保证原函数的原型对象上的属性不能丢失
Fn.prototype = Object.create(_this.prototype);
return Fn;
};