前提知识回顾:call()、apply()、bind()函数之间的区别
实现call()方法
解题思路:首先我们要知道使用call函数的一些规定
- 调用的call()方法的是函数
- 如果指定的
this值
为null
或者undefined
,则会自动指向全局对像(浏览器中就是window对象) - 如果指定的
this
值为数字,字符串,布尔值等,则会指向原始值的实例对象
Function.prototype.myCall = function(context){
if(typeof this != 'function'){
throw new TypeError('Error')
} //判断调用该方法的是不是函数,如果不是,则抛出异常
if(context === null || context === undefined){
context = window //指定为null 和 undefined 的 this值会自动指向全局对象(浏览器中为windiw)
} else {
context = Object(context) //值为数字,字符串,布尔值等的this 会指向原始值的实例对象
}
// 给context添加一个属性
context.fn = this
//剩余运算符,得到的是一个数组,包含除了this值的其他参数
// 通过参数伪数组将context后面的参数取出来
let args = [...arguments].slice(1)
// 调用该函数
let result = context.fn(...args);
// 删除fn
delete context.fn
// 返回结果
return result
}
实现apply()方法
apply()方法和call()方法类似,只是参数的形式不一样
因为apply()方法的参数是一个数组或者类数组对象,所以需要判断是否存在第二个参数,如果存在就将第二个参数也展开,如果不存在直接调用该方法
Function.prototype.myApply = function (context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
//判断调用该方法的是不是函数,如果不是,则抛出异常
if (context === null || context === undefined) {
context = window // 指定为 null 和 undefined 的 this 值会自动指向全局对象(浏览器中为window)
} else {
context = Object(context) // 值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的实例对象
}
// 给context添加一个属性
context.fn = this
var result = null
//判断是否存在第二个参数
//如果存在就将第二个参数也展开
// 如果不存在直接调用该方法
if(arguments[1]) {
result = context.fn(...arguments[1])
} else {
result = context.fn()
}
delete context.fn
return result
}
实现bind()方法
bind 和 call/apply 有一个很重要的区别,一个函数被 call/apply 的时候,会直接调用,但是 bind 会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this,之后的一序列参数将会在传递的实参前传入作为它的参数。
Function.prototype.mybind = function (context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
// 保存需要绑定的this上下文
const _this = this
// 取参数
const args = [...arguments].slice(1)
//返回一个改变了this指向的新的函数
return function F () {
if (this instanceof F) {
// this是否是F的实例 也就是返回的F是否通过new调用
//利用扩展运算法合并数组
return new _this(...args, ...arguments)
}
return _this.apply(context,args.concat(...arguments))
}
}