手写call、apply、bind
程序员文章站
2022-03-01 13:05:56
...
call apply bind是面试中经常被问到的问题,也是很考验js基础是否扎实的一道题,它们的作用都是改变函数的this指向,也就是改变当前的作用域。其用法也都差不多,只是传参方式有所不同,但如果你只告诉面试官这些的话,那面试官也就仅仅只知道你会用,那么我们不止要知其然,更要知其所以然。
- 语法
fun.call(thisArg, param1, param2, ...)
fun.apply(thisArg, [param1, param2, ...])
fun.bind(thisArg, param1, param2, ...)
参数:
thisArg: 执行作用域
param1, param2, … :(可选)都是传递给fun的参数,只是写法不同
- 原理
调用这些api的必须是函数,因为它们都是挂在Function对象上的三个方法,只有函数才有这些方法。
Object.prototype.toString()就是一个函数所以我们经常能看到这样的写法:Object.prototype.toString.call(data)
- 手写call
思路:
1.在函数的原型对象上添加一个call_方法以便所有的函数都能使用
2.根据call的规则设置上下文对象,也就是this的指向。
3.通过设置context的属性,将函数的this指向隐式绑定到context上
4.通过隐式绑定执行函数并传递参数。
5.删除临时属性,返回函数执行结果
Function.prototype.call_ = function(context) {
// 正确判断上下文对象并赋予作用域参数 如果没有赋予全局作用域
if(context === null || context === undefined) {
context = window //指定为 null 或 undefined 的 this 值指向全局对象
} else {
context = Object(context) // 值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的实例对象
}
context.fn = this // 为这个函数作用域添加一个fn方法,并把这个函数赋给这个方法
let args = [...arguments].slice(1) // 获取传给函数的参数
let result = context.fn(...args) // 执行方法
Reflect.deleteProperty(context, 'fn') // 删除这个方法
return result // 返回结果
}
- 手写apply
给函数传递参数的方式不太一样,其他部分都一样
Function.prototype.apply_ = function(context) {
// 正确判断上下文对象并赋予作用域参数 如果没有赋予全局作用域
if(context === null || context === undefined) {
context = window //指定为 null 或 undefined 的 this 值指向全局对象
} else {
context = Object(context) // 值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的实例对象
}
context.fn = this // 为这个函数作用域添加一个fn方法,并把这个函数赋给这个方法
let result
if (arguments[1]) {
result = context.fn(...arguments[1])
} else {
result = context.fn()
}
Reflect.deleteProperty(context, 'fn') // 删除这个方法
return result // 返回结果
}
到这里应该就能理解call和apply的底层是什么样子的了,如果还不理解,还有一种方法就是你可以直接理解成:把当前的这个函数直接放在一个对象里,通过这个对象调用这个方法,那么这个函数的作用域就是这个对象,里面的this值全都指向这个对象。
- 手写bind
今天的难点来了,先来讲讲bind方法的原理,bind()依然是改变函数的this指向。但是它不会像call和apply一样立即执行这个函数,而是返回一个新的函数给外部。bind方法返回的函数不仅可以作为普通函数调用,还可以当作构造函数被实例化。
首先获取bind的this并保存到一个变量中(因为在内部函数的作用域中无法访问的外部函数的this)当返回的函数被执行时,绑定 bind 的原方法将被调用,并将原方法内部作用域对象替换为绑定 bind 时传入的第一个参数,bind 的实现离不开 call 或 apply
Function.prototype.bind_ = function(context, ...rest) {
const self = this; // 由于内部函数的作用域无法访问外部函数作用域中的this,因此需要把this用一个变量保存
// 创建一个新的函数变量,用来改变函数执行作用域
return function fn(...args) {
// this是否是fn的实例 也就是返回的fn是否通过new调用, 通过new调用返回的fn函数就绑定到self上,否则就绑定到传入的context上
return this instanceof fn ? new self(...rest, ...args) : self.apply(context, rest.concat(args))
}
}
- 区别
call apply 的效果是一样的, 区别就在于
传参:
- call 第一个参数是要执行的作用域, 之后的参数便是传递给函数的参数
- apply 第一个参数是要执行的作用域, 第二个是要传递给函数的参数整合的一个数组
执行: - call和apply 改变了函数的this指向后马上执行该函数
- bind 是返回改变了this指向的函数,不执行该函数
返回值: - call和apply 返回fun的执行结果
- bind 返回fun的拷贝,并指定了fun的this指向,保存了fun的参数
上一篇: Ubuntu 添加新用户
下一篇: Ubuntu添加新用户,并创建管理员权限
推荐阅读
-
JavaScript中的apply/call/bind和this
-
JavaScript中的call方法和apply方法使用对比
-
JS中apply,call,bind区别与用法
-
JavaScript call apply使用 JavaScript对象的方法绑定到DOM事件后this指向问题_javascript技巧
-
call()和apply()方法有什么区别
-
js中call()和apply()改变指针问题的讲解
-
call和apply的区别是什么(apply的用法和搭配)
-
浅谈JavaScript中的apply/call/bind和this的使用
-
JavaScript中的call和apply的用途以及区别
-
有关JavaScript中call()和apply() 的一些理解