Function.prototype.apply = function (context, rest) {
if (!context) {
//context为null或者是undefined时,设置默认值
context = typeof window === 'undefined' ? global : window;
}
context.fn = this;
let result;
if (rest === undefined || rest === null) {
//undefined 或者 是 null 不是 Iterator 对象,不能被 ...
result = context.fn(rest);
} else if (typeof rest === 'object') {
result = context.fn(...rest);
}
delete context.fn;
return result;
}
bind
bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
const module = {
x: 42,
getX: function() {
return this.x;
}
};
const unboundGetX = module.getX;
console.log(unboundGetX()); // The function gets invoked at the global scope
// expected output: undefined
const boundGetX = unboundGetX.bind(module);
console.log(boundGetX());
// expected output: 42
bind() 函数会创建一个新的绑定函数(bound function,BF)。绑定函数是一个 exotic function object(怪异函数对象,ECMAScript 2015 中的术语),它包装了原函数对象。调用绑定函数通常会导致执行包装函数。
模拟实现:
Function.prototype.bind = function(context) {
if (typeof this !== 'function') {
throw new TypeError('not a function');
}
let self = this;
let args = [...arguments].slice(1);
function Fn() {};
Fn.prototype = this.prototype;
let bound = function() {
let res = [...args, ...arguments]; //bind传递的参数和函数调用时传递的参数拼接
context = this instanceof Fn ? this : context || this;
return self.apply(context, res);
}
bound.prototype = new Fn();
return bound;
}
var name = 'Jack';
function person(age, job, gender){
console.log(this.name , age, job, gender);
}
var Yve = {name : 'Yvette'};
let result = person.bind(Yve, 22, 'enginner')('female');
另附MDN(原理一样):
Function.prototype.bind = function() {
var slice = Array.prototype.slice;
var thatFunc = this, thatArg = arguments[0];
var args = slice.call(arguments, 1);
if (typeof thatFunc !== 'function') {
throw new TypeError('Function.prototype.bind what is trying to be bound is not callable');
}
return function() {
var funcArgs = args.concat(slice.call(arguments));
return thatFunc.apply(thatArg, funcArgs);
}
}
主要区别
call,apply的功能相同,区别在于传参的方式不同,就是 call() 方法接受的是一个参数列表,而 apply() 方法接受的是一个包含多个参数的数组。:
apply() 方法调用一个具有给定this值的函数,以及作为一个数组(或类似数组对象)提供的参数func.apply(thisArg, [argsArray])
call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。
function.call(thisArg, arg1, arg2, ...)
call核心
将函数设为传入参数的属性
指定this到函数并传入给定参数执行函数
如果不传入参数或者参数为null,默认指向window/global
删除参数上的函数
来看一道题,思考一下:
fn1.call(fn2);
按照上面的理解
所以调用的是 fn1 ,此时fn1中的 this 指向的是 fn2。 但是这个方法里面并没有使用this,而是直接输出了1。
fn1.call.call(fn2);
按照上面的理解
所以调用的是 fn2(这里有些绕,多念叨念叨琢磨琢磨),此时fn1.call中的this指向的是fn2。 它改变了call方法(Function.prototype原型上的call)的this指向。 此处调用了call方法中的this,即调用了fn2,输出了2。
apply模拟实现
apply与call实现类似,区别在于两者的参数不同,apply的第二个参数为数组或者类数组。
bind
模拟实现:
另附MDN(原理一样):
参考: