Daily-Question
Daily-Question copied to clipboard
【Q656】JS 中如何实现 call/apply
const call = (fn, thisObj, ...args) => {
thisObj.fn = fn;
const r = thisObj.fn(...args);
delete thisObj.fn;
return r;
}
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
};
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
}
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')
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;
};
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");
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
}
实现一个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);
实现一个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);
如果contxt中原本就有同名的fn属性呢,调用完你这个call之后,原本的fn属性就消失了 @yxw007