「这是我参与11月更文挑战的第6天,活动详情查看:2021最后一次更文挑战」
题目
最近朋友遇到几次的大厂视频面试要求手写的笔试题,第一次是快手,题目要求:
装饰数组的push放法,要求不能改变原功能,并打印出“push”
示例:
let arr = [1, 2, 3]
console.log(arr.push(4))
// 控制台打印:
// “push”
// 4
思路
该题的目的是想让你重写原生的数组push方法,并加入一些自定义逻辑
第一反应就是直接在Array原型上自己实现push方法并覆盖掉原生的push
于是就写下了:
Array.prototype.push = function() {
console.log("push");
for( let i = 0; i < arguments.length; i++){
this[this.length] = arguments[i] ;
}
return this.length;
}
let arr = [1,2,3]
console.log(arr.push(4))
在浏览器打印后结果:
可见虽然实现了效果,但是破坏了原生的push功能,并且有意想不到的死循环,push会一直打印
这时面试官说不改变原有push函数,能否实现
于是就想到了把原来的push方法存储一份:
let _push = Array.prototype.push
Array.prototype.push = function() {
console.log('push')
return _push.apply(this, arguments)
}
var a = [1,2,3]
a.push(1)
虽然也实现了,但是也出现了和上面一样的死循环,疯狂打印push
突然,灵光一闪~ 明白了面试官的用意
其实面试官想考察的是vue源码里对数组方法的响应式处理
我们可以根据尤大处理数组方法的思路来实现
下面正解:
const arrayProto = Array.prototype;
const arrayMethods = Object.create(arrayProto);
['push', 'pop', 'shift', 'unshift'].forEach(function(method) {
arrayMethods[method] = function() {
console.log('push') // 打印push, 更新视图
return arrayProto[method].apply(this, arguments)
}
})
let a = [1,2,3]
a.__proto__ = arrayMethods // 关键
console.log(a.push(44));
思路:
- 首先将数组的原型保存起来,然后创建一个__proto__指向Array原型的新对象
- 第二步在赋予命名为push的新方法给新对象,在新方法里做你想做的事,比如更新视图。然后返回执行刚才存储起来的原生push等方法
- 最后将我们数组的__proto__指向新对象即可
这样在执行push方法时,沿着原型链走的是我们新对象里的push方法,达到装饰效果
最后附上github vue2.x处理数组方法的源码,只有几十行代码:observer/array.js
总结
常读源码,你会有不一样的收获,也能学到比较先进的思维方式