JS 中如何实现 call/apply
更多描述 在 JS 中如何实现 call/apply?
相关问题:
Issue 欢迎在 Gtihub Issue 中回答此问题: Issue 674
Author 回答者: shfshanyue
const call = (fn, thisObj, ...args) => {
thisObj.fn = fn;
const r = thisObj.fn(...args);
delete thisObj.fn;
return r;
};
Author 回答者: wussss
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 回答者: heretic-G
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 回答者: iceycc
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 回答者: Vi-jay
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 回答者: yxw007
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");
Author 回答者: zhangtuo1999
Function.prototype.myCall = function (ctx) {
ctx ??= globalThis;
ctx = Object(ctx);
const args = [...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) {
ctx ??= globalThis;
ctx = Object(ctx);
const args = arguments[1];
const key = Symbol("key");
ctx[key] = this;
const res = ctx[key](...args);
delete ctx[key];
return res;
};
Author 回答者: Yeti-xxx
实现一个call:
function person(a, b) {
return {
name: this.name,
sum: a + b
}
}
const yeti = {
name: 'yeti'
}
// 实现一个call
Function.prototype.newCall = function (obj, ...args) {
const window = { windwo: 'window' }
if (!obj) {
obj = window
}
obj.p = this //此处的this是person函数 相当于在obj中添加了一个person方法
const resCall = obj.p(...args) //由于函数可能会返回值,所以将执行的结果保存并返回
delete obj.p
return resCall
}
const res = person.newCall(yeti, 1, 2)
console.log(res);
Author 回答者: Yeti-xxx
实现一个apply:
function person(a, b, c) {
console.log(Math.max.apply(null, [...arguments]));
console.log(Math.max.newApplay(null, [...arguments]));
return {
name: this.name,
sum: a + b + c
}
}
const yeti = {
name: 'yeti'
}
// 实现一个apply
Function.prototype.newApplay = function (obj, arr) {
const window = { windwo: 'window' }
if (!obj) {
obj = window
}
obj.p = this
if (!arr) { //如果为传入参数数组,直接执行
const resnewApply = obj.p()
delete obj.p
return resnewApply
} else {
const resnewApply = obj.p(...arr)
delete obj.p
return resnewApply
}
}
const resApplay = person.newApplay(yeti, [1, 2, 6])
console.log(resApplay);
Author 回答者: algok-876
如果contxt中原本就有同名的fn属性呢,调用完你这个call之后,原本的fn属性就消失了 @yxw007