call
函数通过call调用时,函数体内的this指向第一个参数,后面可以传多个参数来传入原函数argument中。
function setDetails(name, color) {
this.name = name;
this.color = color;
}
Function.prototype.call2 = function (context) {
// 定义一个唯一的属性名
function mySymbol(obj) {
let unique = (Math.random() + new Date());
// 如果冲突就递归调用
return obj.hasOwnProperty(unique) ? mySymbol(obj) : unique;
}
let uniqueName = mySymbol(context);
// 参数列表
let args = [];
for (let i = 1; i < arguments.length; i++) {
args.push('arguments['+ i +']');
}
context[uniqueName] = this;
let result = eval('context[uniqueName](' + args.join(',') + ')');
// 删除临时方法
delete context[uniqueName];
return result;
}
let cat1 = {};
setDetails.call2(cat1, '大毛', '橘色');
// {name: "大毛", color: "橘色"}
apply
call与apply不同之处在于call接收的是一个参数列表,而apply只接收两个实参,第一个实参是指向的this,第二个是参数数组。
使用ES6方法改写:
function setDetails(name, color) {
this.name = name;
this.color = color;
}
Function.prototype.apply2= function (context, args) {
// 定义一个唯一的属性名
let uniqueName = Symbol(context);
context[uniqueName] = this;
let result = context[uniqueName](...args);
// 删除临时方法
delete context[uniqueName];
return result;
}
let cat1 = {};
setDetails.apply2(cat1, ['大毛', '橘色',5]);
bind
bind与call和apply的功能相似,但有不同,bind不会立即调用函数,只做this绑定,并且返回一个新的函数,这个函数运行的逻辑与原函数一致,但是this会指向之前绑定的对象。
原生用法:
function setDetails(name, color) {
this.name = name;
this.color = color;
}
let cat1 = {};
let res = setDetails.bind(cat1);
res('大毛', '橘色');
// bind函数可以在立即调用的时候传入一个参数,然后再去调用返回的函数再去传另一个
let res = setDetails.bind(cat1, '大毛');
res('橘色');
自己实现:
function setDetails(name, color) {
this.name = name;
this.color = color;
}
Function.prototype.bind2 = function (context) {
let originFn = this;
let args = Array.from(arguments).slice(1);
return function () {
let bindArgs = Array.from(arguments);
return originFn.apply(context, args.concat(bindArgs));
}
}
let cat1 = {};
let res = setDetails.bind2(cat1);
res('大毛', '橘色');
但是如果使用构造函数的方式去调用bind返回的函数,那么返回函数的this就会指向实例对象。因此只需要判断 this instanceof 返回的函数,另外,返回的函数要继承原函数(将原型链连接起来)
function setDetails(name, color) {
this.name = name;
this.color = color;
}
Function.prototype.bind2 = function (context) {
let originFn = this;
let args = Array.from(arguments).slice(1);
function fBind() {
let bindArgs = Array.from(arguments);
// 判断是否作为构造函数调用
return originFn.apply(this instanceof fBind ? this : context , args.concat(bindArgs));
}
// 将fBind原型链连接起来
// fBind.prototype.__proto__ = this.prototype;
fBind.prototype = Object.create(this.prototype);
return fBind;
}
let cat1 = {};
let Res = setDetails.bind2(cat1);
let res2 = new Res('大毛', '橘色');
// fBind {name: "大毛", color: "橘色"}
网上的资料深浅不一,本人也是处在学习的过程中的总结,如果发现错误,欢迎留言指出~
扫码加入前端群,分享技术难题~
、