刚开始写技术笔记的时候,很浅显的写了一篇this的指向问题,现在看起来不能说错误百出,但也确实是没什么技术水平。今天分享自己对于call、apply、bind新的认识,并手写一个自己的call、apply、bind。
三个方法的语法大体一样:
fnction fn() {}
fn.call(thisArg, arg1, arg2, ...)
fn.apply(thisArg, [arg1,arg2,...])
fn.bind(thisArg, arg1, arg2, ...)
call和bind的参数一样,apply的参数是一个数组(a开头,Array),call和apply返回的是fn执行的结果,bind返回的是fn的拷贝并指定this值和参数(bind不执行,需要调用)。
thisArg是可选的,fn的this指向是thisArg对象。
非严格模式thisArg指定为null、undefined,fn的this指向就是window:
function fn() {
console.log(this);//window
}
fn.call()
严格模式fn的this指向是undefined:
"use strict"
function fn() {
console.log(this);//undefined
}
fn.call()
如果thisArg是数字、字符串、布尔值,this指向原始值的对象:
function fn() {
console.log(this);//String||Number||Boolean
}
fn.call('' || 5 || true)
call、apply和bind是挂在Function对象上的方法,只有函数才能调用。
说真的,这三个方法开发业务的时候并不常用,有用也是用来装一装,面面试。三个方法最主要的就是借助别人的方法,减少重复代码,节省内存。比如fn1方法和fn2方法,fn2需要用到fn1的方法,这时候直接用fn1的方法而不是自己声明一个方法。bind方法比较不一样,bind返回的是一个函数,所以还可以用来做闭包等。
比如求一个数组的最大最小值:
var arr = [5, 6, 2, 8, 1];
console.log(Math.max.apply(Math, arr));//8
console.log(Math.min.apply(Math, arr));//1
实现一个call::
Function.prototype.myCall = function (context) {
if(context === undefined || context === null){
context = window;
}else{
context = Object(context);
};
context.fn = this;
let result = context.fn([...arguments].slice(1));
delete context.fn;
return result;
};
这边需要注意的一个就是context,不能context = context || window;因为这样会把原始数值都绑定到window,但是上面也说了,String是绑定String的。还有就是直接context.fn也是不够严谨,可以使用symbol就绝对不会冲突,只能说fn这个需要用特殊的不会命名冲突的去实现,考虑兼容的话symbol好像也不是非常合适。
实现一个apply:
Function.prototype.myApply = function (context) {
if(context === undefined || context === null){
context = window;
}else{
context = Object(context);
};
context.fn = this;
let result;
if(arguments[1] instanceof Array){
result = context.fn(...arguments[1]);
}else{
result = context.fn();
};
delete context.fn;
return result;
};
跟call差不多,就是一个参数的判断不一样,其他的跟call的注意点一样。
实现一个bind:
Function.prototype.mybind = function (context) {
let that = this;
if(context === undefined || context === null){
context = window;
}else{
context = Object(context);
};
let args = [...arguments].slice(1);
return function Fn() {
if(this instanceof Fn){
return new that(...args, ...arguments)
}else{
return that.apply(context, args.concat(...arguments));
}
}
}
因为bind返回的是一个函数,所以思路是一样的,不同的是需要判断,bind之后是否是直接new这个函数,如果是new,那么this就是这个构造函数。
function Fn() {
console.log(this);//Fn
}
new Fn();
(完)