Skip to content

bind&call&apply的实现

js
// call、apply特性:
// 1、如果传入null、undefined则指向window;传入原始值(Number、String、Boolean)则指向原始对象
// 2、避免函数名与上下文(context)的属性发生冲突,使用Symbol类型作为唯一值
// 3、立即执行,返回其函数结果

Function.prototype.myCall = function(context, ...args) {
	// context = (context ?? window) || new Object(context)
    context = (context ? context : window) || new Object(context)
	let temp = Symbol() // 它的功能是标识唯一性的ID
	context[temp] = this // this指向需要改变指向的方法
	let res = context[temp](...args) // 通过context来调用原方法即可做到更改里边的this指向
	delete context[temp] // 移除临时属性
	return res
}

Function.prototype.myApply = function (context, arr) {
	if(!(arr instanceof Array)) throw '参数为数组!'
	// context = (context ?? window) || new Object(context)
    context = (context ? context : window) || new Object(context)
	let temp = Symbol()
	context[temp] = this
	let	res = context[temp](...arr)
	delete context[temp]
	return res
}

// 简易版bind: 利用闭包缓存this【高阶函数】
// Function.prototype.myBind = function (context, ...arg) {
// 	return () => this.myApply(context, arg) // 闭包缓存this
// }

// 进阶版bind
// 如果还回的函数被new了,this指向实例对象
// 保留原函数的原型链上的方法和属性

Function.prototype.myBind = function (context, ...args) {
    let self = this
    context = (context ? context : window) || new Object(context)
    let fn = function () {
        let obj = this instanceof self ? this : context // 绑定bind后被new了,new的优先级更高。是否new:this instanceof self
        self.apply(obj, args)
    }
    fn.prototype = Object.create(self.prototype) // 继承原函数的原型方法(注意1:先被bind后再被new了,才能调用。注意2: 这也是this instanceof self为true的条件)
    return fn
}

let obj = {
    name: 'chaos'
}
function Fn(age) {
    console.log(this)
}
Fn.prototype.getName = function () {
    console.log('进来了~')
    return this.name
}

// let fn = Fn.myBind(obj)
// fn()
// fn.getName() // 直接调用是报错的,getName为原型上的方法,需要new

let fn = Fn.myBind(obj)
let Instance = new fn()
console.log('Instance: ', Instance)
Instance.getName()