每个函数都有2个继承而来的方法:apply call
作用:在特定作用域中调用函数(扩充函数赖以运行的作用域),设置函数体内this对象的值
好处:对象不需要与方法有任何耦合关系
call
接收多个参数,第一个为函数上下文this,后边参数为函数本身的参数(call参数必须逐个列举)
call实现:在传入的对象上添加这么一个方法,为了保持对象,执行完后再把这个方法删除
call模拟步骤可分为:
第一步:将函数设为传入对象的属性
第二步:执行该函数
第三步:删除该函数
Function.prototype.call= function(obj, ...arg){
obj.fn = this; //此处this是指调用call的function
let result = obj.fn(...arg);
delete obj.fn;
return result;
}
需注意:指定传入的this不一定是该函数执行时真正的this。如果这个函数处于非严格模式,则指定为null和undefined的this值会自动指向全局对象(浏览器中就是window对象);值为原始值(数字,字符串,布尔值)的this会指向该原始值的自动包装对象。
Function.prototype.mycall=function(thisArg,...args){
// your code here
thisArg=thisArg ?? window;// thisArg can be null or undefined
thisArg=Object(thisArg);// transform primitive value
let func=Symbol();// create a unique property
thisArg[func]=this;// assign the function to a unique method created on the context
let res=thisArg[func](...args)// call the method with passed args
delete thisArg[func];// delete this unique method so as to not cause any side effects
return res;
}
apply(运行函数的作用域,参数数组)
argsArray: 一个数组或者类数组对象,其中的数组元素将作为单独的参数传给fun函数。如果该参数的值为null或undefined,则表示不需要传入任何参数。从ECMAScript5开始可以使用类数组对象。
Function.prototype.apply = function(obj=window, arr){
obj[fn] = this;
let result = obj[fn](...arr);
delete obj[fn];
return result;
}
The bind() method
Calling f.bind(someObject) creates a new function with the same body and scope as f, but the value of this is permanently bound to the first argument of bind, regardless of how the function is being called.
function f() {
return this.a;
}
const g = f.bind({ a: "azerty" });
console.log(g()); // azerty
const h = g.bind({ a: "yoo" }); // bind only works once!
console.log(h()); // azerty
const o = { a: 37, f, g, h };
console.log(o.a, o.f(), o.g(), o.h()); // 37 37 azerty azerty
bind:第一个参数是函数上下文,返回值是一个函数的实例(不会立即执行),该函数实例的this值会被永久绑定到传给bind函数的上下文。
https://www.smashingmagazine.com/2014/01/understanding-javascript-function-prototype-bind/
Function.prototype.bind=function(obj,...args1){
const fn=this;
return function F(...args2){
if(fn instanceof F){return new fn(...args1,...args2)} //判断是否用于构造函数
return fn.apply(obj,args1.concat(args2));
}
}
区别总结:
当使用一个函数需要改变this指向的时候才会用到call,apply,bind
如果要传递的参数不多,则可以使用fn.call(thisObj, arg1, arg2 ...)
如果要传递的参数很多,则可以用数组将参数整理好调用fn.apply(thisObj, [arg1, arg2 ...])
如果想生成一个新的函数长期绑定某个函数给某个对象使用,则可以使用:
const newFn = fn.bind(thisObj);
newFn(arg1, arg2...)
注意:绑定函数(bind函数返回的新函数)不可以再通过apply和call改变其this指向,即当绑定函数调用apply和call改变其this指向时,并不能达到预期效果。