欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

js手写方法之--call/apply/bind

程序员文章站 2022-07-14 14:27:10
...

js手写方法之--call/apply/bind

call的实现

其一:使用ES6的语法

/**
 * 核心思想:
 * 1. 绑定当前方法到 context 对象上
 * 这样,执行context.fn()时,内部的this就指向了context
 * 2. 使用ES6新特性,展开运算符
 */
Function.prototype.mycall (context, ...args) {
	// 若context不存在,则取 context = window
	context = Object(context) || window
	
	// 绑定当前方法 this 到 context上
	// 此时context.fn(...args) === this.call(context, ...args)
	context.fn = this
	let result = context.fn(...args)
	
	// 方法执行完成后,删除context.fn
	delete context.fn
	return result
}

其二:不使用ES6语法

/**
 * 不使用ES6语法的写法,核心就是eval()
 * 构造字符串 "context.fn(arguments[1], arguments[2])"
 * 用eval调用
 */
 Function.prototype.mycall = function (context) {
    context = Object(context) || window;
    let _args = [];

    context.fn = this;
    for (let i = 1, l = arguments.length; i < l; i++) {
        _args.push('arguments[' + i + ']');
    }
    // result = eval('context.fn(arguments[1], arguments[2])')
    let result = eval('context.fn(' + _args.join(',') + ')');
    delete context.fn;
    return result;
}

apply的实现

/**
 * 核心思想和call一致
 */
Function.prototype.myapply = function (context, args = []) {
	context = Object(context) || window
	context.fn = this
	let result = context.fn(...args)
	delete context.fn
	return result
}

bind的实现

/**
 * bind稍微复杂点,需要实现两点
 * 1. 作为方法使用 bindFn(),this 指向 context
 * 2. 作为构造函数调用 new bindFn(),this 不指向 context
 */
Function.prototype.mybind = function (context) {
	context= Object(context) || window;
    let self = this;
    // 初始传入的参数
    let _args = [].slice.call(arguments, 1);
    
    let _bind = function () {
    	// 作为方法调用 _bind() 时,this指向他的调用者,window 或是 其他
    	// 例如:
    	// myfn = function (){};
    	// mybindfn = myfn.mybind(context, arg1) === _bind
    	// mybindfn() ===> _bind()
    	// 作为构造函数调用时, myobj = new _bind();
    	// myobj.__proto__ === _bind.prototype === new Fn() === Fn.prototype
    	// 此时_bind 中的 this 就是 myobj
    	// 故而 myobj instanceof Fn === true
        let _this = this instanceof Fn ? this : context;
        return self.apply(_this, _args.concat([].slice.call(arguments)))
    }
    // 新建一个空方法 Fn,使 Fn原型链与当前方法的原型链一致
    let Fn = function () {};
    Fn.prototype = self.prototype;
    
    _bind.prototype = new Fn();
    return _bind;
}