前端面试卷六

1,782 阅读6分钟

Vue 的响应式原理中 Object.defineProperty 有什么缺陷?

为什么在 Vue3.0 采用了 Proxy,抛弃了 Object.defineProperty?

Object.defineProperty 无法监控到数组下标的变化,导致通过数组下标添

加元素,不能实时响应;以及对象添加了新的属性也不行。

Object.defineProperty 只能劫持对象的属性,从而需要对每个对象,每个

属性进行遍历,如果,属性值是对象,还需要深度遍历。Proxy 可以劫持整个对

象,并返回一个新的对象。

Proxy 不仅可以代理对象,还可以代理数组。还可以代理动态增加的属性。

怎么让一个 div 水平垂直居中

<div class="parent">
    <div class="child"></div>
</div> 


//flex
.parent {
    display:flex;
    justify-content:center;
    align-items:center
}


//relative,
.parent{
    position:'relative',
}
.child{
   position:'absolute',
   top:50%;     //左上角在父亲的中间
   left:50%;    //左上角在父亲的中间
   transform:translate(-50%,-50%)    //以自己为中心左移上移自己高度和宽度的50%,两者中心重合,
   //对于child的height,width固定的,可以直接使用像素或者margin:auto(会自动计算)
}

//grid
.parent {
    display:grid;
}
.child {
    justify-self:center;
    align-self:center
}


//vertacal-align inline-block
.parent{
    text-align:center
}
.child{
    display:inline-block,
    verticael-align:middle
}


//line-height
.parent{
    text-align:center,
    height:30px;
    line-height:30px;
}

输出以下代码的执行结果并解释为什么

var a = {n: 1}; 
var b = a;a.x = a = {n: 2}; 
console.log(a.x) 
console.log(b.x)

结果:undefined{n:2}

首先,a 和 b 同时引用了{n:2}对象,接着执行到 a.x = a = {n:2}语句,尽管赋值

是从右到左的没错,但是.的优先级比=要高,所以这里首先执行 a.x,相当于为

a(或者 b)所指向的{n:1}对象新增了一个属性 x,即此时对象将变为

{n:1;x:undefined}。之后按正常情况,从右到左进行赋值,此时执行 a ={n:2}的时

候,a 的引用改变,指向了新对象{n:2},而 b 依然指向的是旧对象。之后执行

a.x = {n:2}的时候,并不会重新解析一遍 a,而是沿用最初解析 a.x 时候的 a,

也即旧对象,故此时旧对象的 x 的值为{n:2},旧对象为 {n:1;x:{n:2}},它被 b

引用着。后面输出 a.x 的时候,又要解析 a 了,此时的 a 是指向新对象的 a,而

这个新对象是没有 x 属性的,故访问时输出 undefined;而访问 b.x 的时候,将

输出旧对象的 x 的值,即{n:2}。

冒泡排序如何实现,时间复杂度是多少, 还可以如何改进?

//常规的冒泡排序
function maopao(arr){
    var array = [...arr]    //拷贝arr防止修改了原有的arr
    var len = array.length
    for(var i = 0;i < len-1; i++){    //比较的趟数
        for(var j = 0; j < len -1 -i; j++){    //每趟要比较的元素个数,一趟结束后最大值放最后
            if(array[j]>array[j+1]){
                var temp = array[j]
                array[j] = array[j+1]
                array[j+1] = temp
            }
        }
    }
    return array
}

//分析:对于上面的算法,即使后面的数据有序了,还是会进行比较,可以设置一个变量flag减少比较次数,
并记录上一躺最后一次交换的位置,该位置(从左到右遍历时)后面的元素都是排好序的,反之亦然。

//优化代码:
funtion maopao(arr){
    var array = [...arr]
    var len = array.length
    var lastMinIndex = 0    //上一躺交换位置,从右到左遍历
    var lastMaxIndex = len-1    //上一躺交换位置,从左到右遍历
    for(var i = 0; i<len-1;i++){    //比较的趟数
        var minIndex = 0 , maxIndex = 0    //初始化本躺最后交换位置
        var flag = 0 //代表没有交换, 1代表交换
        for(var j = lastMinIndex;j<lastMaxIndex;j++){
            if(array[j]>array[j+1]){
                var temp = array[j]
                array[j] = array[j+1]
                array[j+1] = temp
                flag = 1
                maxIndex = j
            }
        }
        if(!flag) break    //没有交换说明数据已经排好序
        lastMaxIndex = maxIndex
        for(var j = lastMaxIndex;j>lastMinIndex;j--){
            if(array[j]<array[j-1]){
                var temp = array[j]
                array[j] = array[j-1]
                array[j-1] = temp
                flag = 1
                minIndex = j
            }
        }
        if(!flag) break    //没有交换说明数据已经排好序
        lastMinIndex = minIndex
    }    
    return array
}

//测试代码
var arr = []    
for (var i = 0; i < 100; i++) {  
    arr.push(100 - i)
}    
console.log('排序前:', arr)    
console.log('排序后:', maopao(arr))

某公司 1 到 12 月份的销售额存在一个对象里面

如下:{1:222, 2:123, 5:888},请把数据处理为如下结构:[222, 123, null, null, 888,

null, null, null, null, null, null, null]。

let obj  = {1:222, 2:123, 5:888}
let result = Array.from({length:12},(item,index)=>{
    return obj[index+1] || null
})

Array.from的三种用法:
1.Array.from(obj,mapFn)
obj指的是数组对象、类似数组对象或者是set对象,mapFn指的是对数组中的元素进行处理的方法。
2.Array.from({length:n},mapFn)
第一个参数指定了第二个参数执行的次数。可以将各种值转化为真正的数组。
3.Array.from(string)
Array.from('abc')    //['a','b','c']

要求设计 LazyMan 类,实现以下功能。

LazyMan('Tony'); 
// Hi I am Tony 
LazyMan('Tony').sleep(10).eat('lunch'); 
// Hi I am Tony // 等待了 10 秒... // I am eating lunch
LazyMan('Tony').eat('lunch').sleep(10).eat('dinner'); 
// Hi I am Tony // I am eating lunch // 等待了 10 秒... // I am eating diner
LazyMan('Tony').eat('lunch').eat('dinner').sleepFirst(5).sleep(1 0).eat('junk food');
 // Hi I am Tony// 等待了 5 秒... // I am eating lunch // I am eating dinner // 等待了 10 秒... // I am eating junk food

//分析:先来的任务先执行,要使用队列queue管理任务,sleepFirst最先执行,说明该任务在队列的头部
class LazyMan(){
   constructor(name){
      console.log('Hi I am '+name)
      this.queue = []    //队列管理任务 
      //开始调度任务,setTimeout保证后面的任务已经添加进来
      setTimeout(()=>{
        this.next()
      },0)
   }
   next(){    //取出队头任务并执行,先放进来的先执行
     var fn = this.queue.shift()
     fn && fn()
   }
   ear(food){
     const fn = ()=>{
       console.log('I am eating '+food)
       this.next()    //不等待直接执行列头函数
     }
     this.queue.push(fn)
     return this    //链式调用
   }
   sleep(time){
     const fn = ()=>{
        setTimeout(()=>{
           console.log('等待了'+time+'秒...')
           this.next()    //关键点,异步任务执行完了才能执行后面的任务
        },time)
     }
     this.queue.push(fn)
     return this    //链式调用
   }
   sleepFirst(time){
     const fn = ()=>{
        setTimeout(()=>{
           console.log('等待了'+time+'秒...')
           this.next()
        },time)
     }
     this.queue.unshift(fn)    //跟sleep就这里不同,这里优先执行要放在队头
     return this    //链式调用
   }
}

分析比较 opacity: 0、visibility: hidden、display:none 优劣和适用场景。

display: none (不占空间,不能点击)(场景,显示出原来这里不存在的结构)
visibility: hidden(占据空间,不能点击)(场景:显示不会导致页面结构发生变动,不会撑开)
opacity: 0(占据空间,可以点击)(场景:可以跟 transition 搭配)

箭头函数与普通函数(function)的区别是什么?构造函数(function)可以使用 new 生成实例,那么箭头函数可以吗?为什么?

箭头函数是普通函数的简写,可以更优雅的定义一个函数,和普通函数相比,有以下几点差异:

函数体内的 this 对象,就是定义时所在的对象,而不是使用时所在的对象。不可以使用 arguments 对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。

不可以使用 yield 命令,因此箭头函数不能用作 Generator 函数。不可以使用 new 命令,因为:

1.没有自己的 this,无法调用 call,apply。

2.没有 prototype 属性 ,而 new 命令在执行时需要将构造函数的 prototype 赋值给新的对象的 __proto__

function newFunc(father, ...rest) { 
    var result = {}; 
    result.__proto__ = father.prototype; 
    var result2 = father.apply(result, rest); 
    if ((typeof result2 === 'object' || typeof result2 === 'function')
     && result2 !== null ) {
        return result2; 
    }
    return result; 
}

给定两个数组,写一个方法来计算它们的交集

例如:给定 nums1 = [1, 2, 2, 1],nums2 = [2, 2],返回 [2, 2]。

var nums1 = [1,2,2,1], nums2 = [2,2]
var res = []

//对于未排序的两个数组,每次都要循环整个nums2数组 O(n^2)
nums1.forEach(item=>{
    var index = nums2.indexOf(item)
    if(index!=-1){
        res.push(item)    
        nums2.splice(index,1)    //找到了之后要将该元素从数组中清掉,防止重复
    }
})

//对于拍好序的两个数组,只需要使用双指针同时遍历,只要一方遍历到了数组最后一个元素就结束 O(n)
var i = 0,j=0
while(i<nums1.length&&j<nums2.length){
    if(nums1[i] == nums2[j]){    //同时后移指针
        res.push(nums1[1])
        i++
        j++
    }
    if(nums1[i] > nums2[j]){    //小的一方后移指针
        j++
    }
    if(nums1[i] < nums2[j]){    //小的一方后移指针
        i++
    }
}

已知如下代码,如何修改才能让图片宽度为 300px ?注意下面代码不可修改。

<img src="1.jpg" style="width:480px!important" /> 
max-width: 300px;    //优先级高于width
transform: scale(0.625,0.625) //按比例缩小