手撕算法(4)——修改函数this指向

48 阅读2分钟

|| 运算符在左侧操作数为真时,返回左侧操作数;否则,返回右侧操作数。

context = context || window;

箭头函数没有自己的 this,它捕获了定义它时的上下文 this。

箭头函数的这一特性意味着在实现 call 和 apply 方法时,它们不会影响箭头函数的 this 绑定。

这也是为什么我们通常不会在箭头函数上使用 call 和 apply 方法,因为这些方法的主要作用是改变函数调用时的 this 指向,而箭头函数已经在定义时锁定了 this。

Function.prototype.myCall = function(context, ...args) {
    if (typeof context !== 'object' || context === null) {
        console.log('Wrong argment!');
        return;
    }
    const key = Symbol();
    context[key] = this;
    const res = context[key](...args);
    delete context[key];
    return res;
}

Function.prototype.myApply = function(context, argsArr) {
    if (typeof context !== 'object' || context === null) {
        console.log('Wrong argment!');
        return;
    }
    const key = Symbol();
    context[key] = this;
    const res = context[key](...argsArr);
    delete context[key];
    return res;
}

Function.prototype.myBind = function(context, ...args) {
    if (typeof context !== 'object' || context === null) {
        console.log('Wrong argment!');
        return;
    }
    const fn = this;
    return function (...newArgs) {
        const key = Symbol();
        context[key] = fn;
        const res = context[key](...args, ...newArgs);
        delete context[key];
        return res;
    }
}

Function.prototype.myBind_call = function(context, ...args) {
    if (typeof context !== 'object' || context === null) {
        console.log('Wrong argment!');
        return;
    }
    const fn = this;
    return function (...newArgs) {
        return fn.call(context, ...args, ...newArgs);
    }
}

Function.prototype.myBind_apply = function(context, ...args) {
    if (typeof context !== 'object' || context === null) {
        console.log('Wrong argment!');
        return;
    }
    const fn = this;
    return function (...newArgs) {
        return fn.apply(context, args.concat(newArgs));
        // return fn.apply(context, [...args, ...newArgs]);
    }
}
// 用例:
const obj1 = {
    name: 'John',
    greet: function(greeting) {
        console.log(greeting + ', ' + this.name);
    }
};

function greet(greeting) {
    console.log(greeting + ', ' + this.name);
}

obj1.greet.myCall({ name: 'Alice' }, 'Hello');
greet.myCall({ name: 'Bob' }, 'Hi');

obj1.greet.myApply({ name: 'Alice' }, ['Hello']);
greet.myApply({ name: 'Bob' }, ['Hi']);

const boundGreet1 = obj1.greet.myBind_apply({ name: 'Alice' });
const boundGreet2 = greet.myBind_apply({ name: 'Bob' });
const boundGreet3 = obj1.greet.myBind_call({ name: 'Carole' });
const boundGreet4 = greet.myBind_call({ name: 'Decare' });

boundGreet1('Hello');
boundGreet2('Hi');
boundGreet3('Hello');
boundGreet4('Hi');

const obj2 = {
    name: 'John',
    greet: function(greeting, punctuation) {
        console.log(greeting + ', ' + this.name + punctuation);
    }
};

obj2.greet.myCall({ name: 'Alice' }, 'Hello', '!');
greet.myCall({ name: 'Bob' }, 'Hi');

obj2.greet.myApply({ name: 'Alice' }, ['Hello', '!']);
greet.myApply({ name: 'Bob' }, ['Hi']);

const boundGreet = obj2.greet.myBind({ name: 'Alice' }, 'Hello', '!');
boundGreet();

const partialBoundGreet = obj2.greet.myBind({ name: 'Bob' }, 'Hi');
partialBoundGreet('!');

const partialBoundGreet2 = obj2.greet.myBind({ name: 'Bob' }, undefined);
partialBoundGreet2('!');


// 一个普通对象
const person = {
    name: 'Alice',
    regularFunction: function() {
        console.log(this.name); // this 指向 person 对象
    },
    arrowFunction: () => {
        console.log(this.name); // this 指向定义时的上下文(这里是全局对象或 undefined)  // 在全局上下文中定义的箭头函数,this 是全局对象(浏览器中是 window,在严格模式下是 undefined)。
    }
};

// 正常调用
person.regularFunction(); // 输出: Alice
person.arrowFunction(); // 输出: undefined 或抛出错误

// 使用 call 方法
person.regularFunction.call({ name: 'Bob' }); // 输出: Bob
person.arrowFunction.call({ name: 'Bob' }); // 输出: undefined 或抛出错误  // 使用 call 方法调用箭头函数并不会改变其 this 值,因为箭头函数不会重新绑定 this。