看有人分享的某公司面试题,用js闭包实现call
、apply
、bind
。程序员
这是我没有考虑过的面试题,很好,它成功引发了个人兴趣。面试
不少人表示不理解,为何面试题总出这些看上去毫无做用的实现。编程
上次我写了一个promise
模拟实现应该注意的点,也有人吐槽重复造轮子意义何在。数组
其实这就是理念的问题了,在一家重视基础的公司里,轮子不是会用就行,甚至你能够不须要会轮子,可是理念以及基础必定要过关,否则你写出来的隐患代码每每会给公司带来未知的风险。promise
(想起了上月某男装店网页上的bug,应该被很多人薅了羊毛 🐑。)闭包
分享的兄弟写出了本身的答案,解法中用了eval
、arguments
,忘记return
结果等等一些严重的问题。说实话,他给的答案我只能给5分(10分制),甚至让我看着有点难受。app
醒醒吧,兄弟们,都9012年了,咱们是否能够用js的新特性实现呢?函数
一个合格的程序员,在写方法的时候第一点应该想到的是作好容错——当call
、apply
、bind
传入第一个参数不是一个引用类型的时候,你应该要知道它的表现。学习
剩下的apply
、bind
测试结果同样。测试
因此第一步:
Function.prototype.call = function(context) {
context = context === undefined || context === null ? window : Object(context)
}
Function.prototype.apply = function(context) {
context = context === undefined || context === null ? window : Object(context)
}
Function.prototype.bind = function(context) {
context = context === undefined || context === null ? window : Object(context)
}
复制代码
接下来是获取参数,call
多个参数用逗号分隔,apply
参数做为数组传入,bind
是逗号分隔,那么取未知个参数,你必定想到了arguments
.
可是相比arguments
获取参数,咱们是否应该想一想,在新特性中可以怎么作?
没错,就是rest参数
:
Function.prototype.call = function(context, ...args) {
context = context === undefined || context === null ? window : Object(context)
}
Function.prototype.apply = function(context, args) {
context = context === undefined || context === null ? window : Object(context)
}
Function.prototype.bind = function(context, ...bindArgs) {
context = context === undefined || context === null ? window : Object(context)
}
复制代码
终于能够进入主题了,不管咱们用call
、apply
仍是bind
,惟一的目的就是修改函数内部的this
指向。
仔细想一想,在call
、apply
方法中咱们拿到的this
应该就是原函数,因此把this
赋值给context
而后调用就改变了原函数的this
指向。
固然不要直接简单的使用对象赋值,为了外部取不到这个属性,这里使用Symbol是合理的。
Function.prototype.call = function(context, ...args) {
context = context === undefined || context === null ? window : Object(context)
const fn = Symbol('fn')
context[fn] = this
context[fn](...args)
}
Function.prototype.apply = function(context, args) {
context = context === undefined || context === null ? window : Object(context)
const fn = Symbol('fn')
context[fn] = this
context[fn](...args)
}
Function.prototype.bind = function(context, ...bindArgs) {
context = context === undefined || context === null ? window : Object(context)
const fn = Symbol('fn')
context[fn] = this
return function(...args) {
context[fn](...bindArgs, ...args)
}
}
复制代码
很明显,这里还有个问题就是修改了原对象上下文context
以及函数没有返回值,因此咱们应该return 结果
以及调用完毕后删除多余的原函数引用,除了bind
。
bind
做为一个特例,它的方法会返回一个新函数,若是这时候把原函数放到context
上,咱们不能删除它的原函数引用context._$fn
,不然将在调用的时候报错。
所幸的是咱们已经在上文中实现了call
、 apply
函数,在这里用上相得益彰,也表现出了你的封装思想。
那么修改一下bind
方法(注意bind
传入的参数在新函数传入的参数以前):
Function.prototype.call = function(context, ...args) {
context = context === undefined || context === null ? window : Object(context)
const fn = Symbol('fn')
context[fn] = this
const result = context[fn](...args)
delete context[fn]
return result
}
Function.prototype.apply = function(context, args) {
context = context === undefined || context === null ? window : Object(context)
const fn = Symbol('fn')
context[fn] = this
const result = context[fn](...args)
delete context[fn]
return result
}
Function.prototype.bind = function(context, ...bindArgs) {
context = context === undefined || context === null ? window : Object(context)
return (...args) => this.apply(context, [...bindArgs, ...args])
}
复制代码
Function.prototype.call = function(context, ...args) {
context = context === undefined || context === null ? window : Object(context)
const fn = Symbol('fn')
context[fn] = this
const result = context[fn](...args)
delete context[fn]
return result
}
Function.prototype.apply = function(context, args) {
context = context === undefined || context === null ? window : Object(context)
const fn = Symbol('fn')
context[fn] = this
const result = context[fn](...args)
delete context[fn]
return result
}
Function.prototype.bind = function(context, ...bindArgs) {
context = context === undefined || context === null ? window : Object(context)
return (...args) => this.apply(context, [...bindArgs, ...args])
}
复制代码
面试造轮子? 不,这不叫造轮子,一个重复的轮子公司是否有须要你来写的必要? 并且放着社区这么多优秀做品不用,难道相信咱们临时写的轮子么,公司也怕翻车。
包括我这里写的方法确定有所遗漏(但愿你们及时指正),这里真的只是为了考查你的js基础与编程思想。
因此大部分公司的面试题自有道理,但愿你们少点吐槽,多学习下基础吧。(无力吐槽脸.jpg)