遇到几次的大厂笔试题:装饰数组push方法

1,189 阅读1分钟

「这是我参与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))

在浏览器打印后结果:

8d2abc1883ae4564a60cef55a902ed7e_tplv-k3u1fbpfcp-watermark.png

可见虽然实现了效果,但是破坏了原生的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

突然,灵光一闪~ 明白了面试官的用意

src=http___n.sinaimg.cn_sinacn20115_582_w291h291_20190114_9028-hrpcmqw7151726.jpg&refer=http___n.sinaimg.jpeg

其实面试官想考察的是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));

思路:

  1. 首先将数组的原型保存起来,然后创建一个__proto__指向Array原型的新对象
  2. 第二步在赋予命名为push的新方法给新对象,在新方法里做你想做的事,比如更新视图。然后返回执行刚才存储起来的原生push等方法
  3. 最后将我们数组的__proto__指向新对象即可

这样在执行push方法时,沿着原型链走的是我们新对象里的push方法,达到装饰效果

最后附上github vue2.x处理数组方法的源码,只有几十行代码:observer/array.js

总结

常读源码,你会有不一样的收获,也能学到比较先进的思维方式