# JS 中如何实现 call/apply
更多描述
在 JS 中如何实现 call/apply?
相关问题:
Issue
欢迎在 Gtihub Issue 中回答此问题: Issue 674 (opens new window)
Author
const call = (fn, thisObj, ...args) => {
thisObj.fn = fn;
const r = thisObj.fn(...args);
delete thisObj.fn;
return r;
};
Author
bind/softBind/apply/call 都是 this 显式绑定的方法
- bind 会返回一个硬绑定的新函数,新函数会使用指定的第一个 thisCtx 去调用原始函数,并将其它参数传给原始函数。 硬绑定会降低函数的灵活性,在绑定之后不能通过显式或硬绑定的方式改变 this,只能通过 new 改变
- softBind 会对指定的函数进行封装,首先检查调用时的 this,如果 this 绑定到全局对象或者 undefined,那就用指定的 thisCtx 去调用函数,否则不会修改 this
- apply 和 call 功能相同,都是以指定的 thisCtx 和参数去执行方法,并返回原方法的返回值,只是 apply 中参数以数组传递
Function.prototype.myBind = function (ctx = globalThis) {
const fn = this;
const args = Array.from(arguments).slice(1);
function bound() {
if (this instanceof bound) {
fn.apply(this, args);
} else {
fn.apply(ctx, args);
}
}
bound.prototype = fn.prototype;
return bound;
};
Function.prototype.mySoftBind = function (ctx = globalThis) {
const fn = this;
const args = Array.from(arguments).slice(1);
function bound() {
if (!this || this === globalThis) {
fn.apply(ctx, args);
} else {
fn.apply(this, args);
}
}
bound.prototype = fn.prototype;
return bound;
};
Function.prototype.myCall = function (ctx = globalThis) {
const args = Array.from(arguments).slice(1);
const key = Symbol("key");
ctx[key] = this;
const res = ctx[key](...args);
delete ctx[key];
return res;
};
Function.prototype.myApply = function (ctx = globalThis) {
const args = arguments[1];
const key = Symbol("key");
ctx[key] = this;
const res = ctx[key](...args);
delete ctx[key];
return res;
};
Author
Function.prototype.call = function call(arm, ...args) {
let fun = this;
if (typeof fun !== "function") throw TypeError("must is function");
let armObj = arm;
if (typeof arm !== "object") {
armObj = Object(arm);
}
let symbolKey = Symbol("tempKey");
armObj[symbolKey] = fun;
let result = armObj[symbolKey](...args);
delete armObj[symbolKey];
return result;
};
Function.prototype.apply = function call(arm, ...args) {
let fun = this;
if (typeof fun !== "function") throw TypeError("must is function");
let armObj = arm;
if (typeof arm !== "object") {
armObj = Object(arm);
}
let symbolKey = Symbol("tempKey");
armObj[symbolKey] = fun;
let result = armObj[symbolKey](args);
delete armObj[symbolKey];
return result;
};
// TODO 完善bind 这里其实还有很多问题
Function.prototype.bind = function aBind(that, ...args) {
let armFun = this;
if (typeof armFun !== "function") throw TypeError("must a function");
function BoundFun(...other) {
if (new.target) {
return new armFun(...args, ...other);
} else {
return armFun.call(that, ...args, ...other);
}
}
BoundFun.__proto__ = armFun.__proto__;
BoundFun.prototype = undefined;
return BoundFun;
};
Author
let person1 = {
name: "Tom",
sayHi(to, ...args) {
console.log(
`Hi,${to}, my name is ${this.name}。${args && args.toString()}`
);
},
};
person1.sayHi();
let person2 = {
name: "Jerry",
};
// call
person1.sayHi.call(person2, "Heydi");
// apply
person1.sayHi.apply(person2, ["Heydi"]);
// bind
let sayHiToJark = person1.sayHi.bind(person2, "Heydi"); // 柯里化
sayHiToJark("Wellcom to you");
// my call
Function.prototype.myCall = function (ctx, ...args) {
let fn = this;
if (typeof fn !== "function") throw TypeError("must is fucntion");
let thisObj = ctx;
if (typeof ctx !== "object") {
thisObj = Object(ctx);
}
const key = Symbol("key");
thisObj[key] = fn;
const res = thisObj[key](...args);
delete thisObj[key];
return res;
};
person1.sayHi.myCall(person2, "Tim");
// my apply
Function.prototype.myApply = function (ctx, args) {
let fn = this;
if (typeof fn !== "function") throw TypeError("must is fucntion");
let thisObj = ctx;
if (typeof ctx !== "object") {
thisObj = Object(ctx);
}
if (!Array.isArray(args)) throw TypeError("must is array");
const key = Symbol("key");
thisObj[key] = fn;
const res = thisObj[key](...args);
delete thisObj[key];
return res;
};
person1.sayHi.myApply(person2, ["Tim"]);
// my bind
Function.prototype.myBind = function (ctx, ...args) {
const fn = this;
return function (...args2) {
const key = Symbol("key");
ctx[key] = fn;
const res = ctx[key](...args, ...args2);
delete ctx[key];
return res;
};
};
let sayHiToMary = person1.sayHi.bind(person2, "Mary");
sayHiToMary("Wellcom to you");
Author
Function.prototype.myCall = function (target) {
const args = [].slice.apply(arguments, [1]);
const fnName = Symbol("fn");
target[fnName] = this;
Object.defineProperty(target, fnName, { enumerable: false });
let res;
eval(`res = target[fnName](${args.join(",")})`);
delete target[fnName];
return res;
};
Author
Function.prototype.myCall = function (context) {
//! 说明:node环境根作用域this 就是globalthis, browser 环境就是window
if (context) {
//! 参数:可能不为对象,所以需要利用Object包裹一层
if (typeof context !== "object") {
context = Object(context);
}
} else {
context = globalThis;
}
//! 说明:由于第一个参数为context,后面的才为调用函数参数,所以需要slice(1)
const args = Array.from(arguments).slice(1);
//! f1 调用的myCall方法, 此时this就是调用的函数本身
context.fn = this;
let ret = context.fn(...args);
//! 说明:不应该改变了this指向,就给调用方法的对象添加一个方法属性,所以调用完后需要删除
delete context.fn;
return ret;
};
function f1() {
console.log("f1, this:", this, ",arguments:", arguments);
}
f1.myCall("hello", "123");