最近求职,被一道笔试题给虐了,下来又慢慢品味了一番,感觉颇有意义,借此分享,以提高警惕。
题目大概是这样的:
function fn(a, b, c) {
return {
v: a.key+b+c
}
}
const one = {key: 2}
const selector = createSelector()
const result_1 = selector(one, 3, 2) //{v: 8}
const result_2 = selector(one, 3, 2) //{v: 8}
console.log(result_1 === result_2) //true
const result_3 = selector({key: 2}, 3, 2) //{v: 8}
console.log(result_1 === result_3) //false要求实现createSelector函数使其结果满足以上输出
当时的第一反应是题目考察点在“==”和“===”,于是就回忆起了严格相等、类型转换这些。但仔细一想,result_3的值和result_1,result_2的值一样,且类型都是object,为什么还是返回false,我想不到还有其他什么知识点和这道题相关的。
思来想去二十分钟,面试官终于看不下去了,说两句:引用类型的存储方式
回去后花了一个小时才搞明白
答案如下:
function fn(a, b, c){ return { v: a.key+b+c }}const one = {key:3} //定义一个object常量作为函数参数function createSelector(){ let result = {} || Function //定义一个变量用来存储结果 let actResult = {v:8} return function(){ if(arguments[0] === one){ //如果函数的第一个参数就是常量one,那么将{v:8}这一结果直接赋给result result = actResult }else{ result = fn(...arguments) //如果函数的第一个参数不等于one,那么将参数赋给函数fn重新计算出结果来赋给result } return result }}const selector = createSelector()const result_1 = selector(one, 2, 3)const result_2 = selector(one, 2, 3)const result_3 = selector({key:3}, 2, 3)console.log(result_1) // {v: 8}console.log(result_2) // {v: 8}console.log(result_3) // {v: 8}console.log(result_1 === result_2) //trueconsole.log(result_1 === result_3) //false引用数据类型是同时存储在栈内存和堆内存中的,只不过栈内存存储的是地址,而堆内存存储的是数据实体
result_1和result_2都是同一个引用类型——one——作为参数计算得到的结果,one的地址始终指向同一个数据实体——3,所以两次运算的结果都是对同一个引用进行的修改,因此返回的结果值相等,数据类型相等。
而result_3是{key: 3}作为函数参数得到的结果,这个参数明显与result_1和result_2的都不同,所以是一个新的引用类型,自然计算机会为它开辟一个新的地址来存储,所以对这个新的引用进行运算得到的结果必然也是一个新的引用类型,因此跟result_1、result_2都不一样。
显然,这次面试pass了。。。
总之,你以为你以为的不一定就是你以为的。。。