三者区别:
1、三者都可以显式绑定函数的this指向
2、三者第一个参数都是this要指向的对象,若该参数为undefined或null,this则默认指向全局window
3、传参不同:apply是数组、call是参数列表,而bind可以分为多次传入,实现参数的合并
4、call、apply是立即执行,bind是返回绑定this之后的函数,如果这个新的函数作为构造函数被调用,那么this不再指向传入给bind的第一个参数,而是指向新生成的对象
手写call :
Function.prototype.Call = function(context){
//判断传入的this对象为null或者是undefined时要赋值为window或global
if(!context){
typeof window === 'undefined' ? global : window;
}
// 利用Symbol创建一个唯一的key值,防止新增加的属性与obj中的属性名重复
let fn = Symbol();
// this指向调用call的函数
context[fn] = this;
let rest = [...arguments].slice(1);//获取除了this指向对象以外的参数, 空数组slice后返回的仍然是空数组
let result = context.fn(...rest); //隐式绑定,当前函数的this指向了context.
delete context.fn;
return result;
}
手写apply :
Function.prototype.Apply = function (context, rest) {
if (!context) {
//context为null或者是undefined时,设置默认值
context = typeof window === 'undefined' ? global : window;
}
let fn = Symbol();
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:
Function.prototype.Bind = function(context) {
if (!context || context === null) {
context = window;
}
let fn = this;
let args = [...arguments].slice(1);
let f = Symbol();
const result = function(...args1) {
let res = [...args, ...args1]; //bind传递的参数和函数调用时传递的参数拼接
if (this instanceof fn) {
// result如果作为构造函数被调用,this指向的是new出来的对象
// this instanceof fn,判断new出来的对象是否为fn的实例
this[f] = fn;
this[f](res);
delete this[f];
} else {
// bind返回的函数作为普通函数被调用时
context[f] = fn;
context[f](res );
delete context[f];
}
};
// 如果绑定的是构造函数 那么需要继承构造函数原型属性和方法
// 实现继承的方式: 使用Object.create
result.prototype = Object.create(fn.prototype);
return result;
};
扩展:手写new
function selfNew(fn, ...args) {
// 创建一个instance对象,该对象的原型是fn.prototype
let instance = Object.create(fn.prototype);
// 调用构造函数,使用apply,将this指向新生成的对象
let res = fn.apply(instance, args);
// 如果fn函数有返回值,并且返回值是一个对象或方法,则返回该对象,否则返回新生成的instance对象
return typeof res === "object" || typeof res === "function" ? res : instance;
}
扩展:手写instanceof
function instanceOf(obj, fn) {
let proto = obj.__proto__;
if (proto) {
//判断原型对象
if (proto === fn.prototype) {
return true;
} else {
//递归判断
return instanceOf(proto, fn);
}
} else {
return false;
}
}