刷《一年半经验,百度、有赞、阿里面试总结》·手记

在掘金上看到了一位大佬发了一篇很详细的面试记录文章-《一年半经验,百度、有赞、阿里面试总结》,为了查漏补缺,抽空就详细做了下。(估计只有我这么无聊了哈哈哈

有给出的或者有些不完善的答案,也尽力给出/完善了(可能有错,大家自行辨别)。有些很困难的题目(例如实现Promise),附带相关链接(懒癌患者福利)。

总的来说,将这些题目分成了“Javascript”、“CSS”、“浏览器/协议”、“算法”和“Web工程化”5个部分进行回答和代码实现。

最后,欢迎来我的博客和我扯犊子:godbmw.com。直接戳本篇原文的地址:刷《一年半经验,百度、有赞、阿里面试总结》·手记

1. Javascript相关

1.1 回文字符串

题目:实现一个函数,判断是不是回文字符串

原文的思路是将字符串转化成数组=>反转数组=>拼接成字符串。这种做法充分利用了js的BIF,但性能有所损耗

function run(input) {
  if (typeof input !== 'string') return false;
  return input.split('').reverse().join('') === input;
}

其实正常思路也很简单就能实现,性能更高,但是没有利用js的特性

// 回文字符串
const palindrome = (str) => {
  // 类型判断
  if(typeof str !== 'string') {
    return false;
  }

  let len = str.length;
  for(let i = 0; i < len / 2; ++i){
    if(str[i] !== str[len - i - 1]){
      return false;
    }
  }
  return true;
}

1.2 实现Storage

题目:实现Storage,使得该对象为单例,并对localStorage进行封装设置值setItem(key,value)和getItem(key)

题目重点是单例模式,需要注意的是借助localStorage,不是让自己手动实现!

const Storage = () => {}

Storage.prototype.getInstance = (() => {
  let instance = null
  return () => {
    if(!instance){
      instance = new Storage()
    }
    return instance
  }
})()

Storage.prototype.setItem = (key, value) => {
  return localStorage.setItem(key, value)
}

Storage.prototype.getItem = (key) => {
  return localStorage.getItem(key)
}

1.3 JS事件流

题目:说说事件流吧

事件流分为冒泡和捕获。

事件冒泡:子元素的触发事件会一直向父节点传递,一直到根结点停止。此过程中,可以在每个节点捕捉到相关事件。可以通过stopPropagation方法终止冒泡。

事件捕获:和“事件冒泡”相反,从根节点开始执行,一直向子节点传递,直到目标节点。印象中只有少数浏览器的老旧版本才是这种事件流,可以忽略。

1.4 实现函数继承

题目:现在有一个函数A和函数B,请你实现B继承A。并且说明他们优缺点。

方法一:绑定构造函数

优点:可以实现多继承

缺点:不能继承父类原型方法/属性

function Animal(){
  this.species = "动物";
}

function Cat(){
  Animal.apply(this, arguments); // 父对象的构造函数绑定到子节点上
}

var cat = new Cat()
console.log(cat.species) // 输出:动物

方法二:原型链继承

优点:能够继承父类原型和实例方法/属性,并且可以捕获父类的原型链改动

缺点:无法实现多继承,会浪费一些内存(Cat.prototype.constructor = Cat)。除此之外,需要注意应该将Cat.prototype.constructor重新指向本身。

js中交换原型链,均需要修复prototype.constructor指向问题。

function Animal(){
  this.species = "动物";
}
Animal.prototype.func = function(){
  console.log("heel")
}

function Cat(){}
Cat.prototype = new Animal()
Cat.prototype.constructor = Cat

var cat = new Cat()
console.log(cat.func, cat.species)

方法3:结合上面2种方法

function Animal(){
  this.species = "动物";
}
Animal.prototype.func = function(){
  console.log("heel")
}

function Cat(){
  Animal.apply(this, arguments)
}
Cat.prototype = new Animal()
Cat.prototype.constructor = Cat;

var cat = new Cat()
console.log(cat.func, cat.species)

1.5 ES5对象 vs ES6对象

题目:es6 class 的new实例和es5的new实例有什么区别?

ES6中(和ES5相比),classnew实例有以下特点:

  • class的构造参数必须是new来调用,不可以将其作为普通函数执行
  • es6class不存在变量提升
  • 最重要的是:es6内部方法不可以枚举。es5的prototype上的方法可以枚举。

为此我做了以下测试代码进行验证:

console.log(ES5Class()) // es5:可以直接作为函数运行
// console.log(new ES6Class()) // 会报错:不存在变量提升

function ES5Class(){
  console.log("hello")
}

ES5Class.prototype.func = function(){ console.log("Hello world") }

class ES6Class{
  constructor(){}
  func(){
    console.log("Hello world")
  }
}

let es5 = new ES5Class()
let es6 = new ES6Class()

console.log("ES5 :")
for(let _ in es5){
  console.log(_)
}

// es6:不可枚举
console.log("ES6 :")
for(let _ in es6){
  console.log(_)
}

这篇《JavaScript创建对象—从es5到es6》对这个问题的深入解释很好,推荐观看!

1.6 实现MVVM

题目:请简单实现双向数据绑定mvvm

vuejs是利用Object.defineProperty来实现的MVVM,采用的是订阅发布模式。每个data中都有set和get属性,这种点对点的效率,比Angular实现MVVM的方式的效率更高。

<body>
  <input type="text">
  <script>
    const input = document.querySelector('input')
    const obj = {}

    Object.defineProperty(obj, 'data', {
      enumerable: false,  // 不可枚举
      configurable: false, // 不可删除
      set(value){
        input.value = value
        _value = value
        // console.log(input.value)
      },
      get(){
        return _value
      }
    })
    obj.data = '123'
    input.onchange = e => {
      obj.data = e.target.value
    }
  </script>
</body>

1.7 实现Promise

这是一位大佬实现的Promise版本:过了Promie/A+标准的测试!!!网上能搜到的基本都是从这篇文章变形而来或者直接照搬!!!原文地址,直接戳:剖析Promise内部结构,一步一步实现一个完整的、能通过所有Test case的Promise类

下面附上一种近乎完美的实现:可能无法和其他Promise库的实现无缝对接。但是,上面的原文实现了全部的,欢迎Mark!

function MyPromise(executor){
  var that = this
  this.status = 'pending' // 当前状态
  this.data = undefined
  this.onResolvedCallback = [] // Promise resolve时的回调函数集,因为在Promise结束之前有可能有多个回调添加到它上面
  this.onRejectedCallback = [] // Promise reject时的回调函数集,因为在Promise结束之前有可能有多个回调添加到它上面

  // 更改状态 => 绑定数据 => 执行回调函数集
  function resolve(value){
    if(that.status === 'pending'){
      that.status = 'resolved'
      that.data = value
      for(var i = 0; i < that.onResolvedCallback.length; ++i){
        that.onResolvedCallback[i](value)
      }
    }
  }

  function reject(reason){
    if(that.status === 'pending'){
      that.status = 'rejected'
      that.data = reason
      for(var i = 0; i < that.onResolvedCallback.length; ++i){
        that.onRejectedCallback[i](reason)
      }
    }
  }

  try{
    executor(resolve, reject) // resolve, reject两个函数可以在外部传入的函数(executor)中调用
  } catch(e) { // 考虑到执行过程可能有错
    reject(e)
  }
}

// 标准是没有catch方法的,实现了then,就实现了catch
// then/catch 均要返回一个新的Promise实例

MyPromise.prototype.then = function(onResolved, onRejected){
  var that = this
  var promise2

  // 值穿透
  onResolved = typeof onResolved === 'function' ? onResolved : function(v){ return v }
  onRejected = typeof onRejected === 'function' ? onRejected : function(r){ return r }

  if(that.status === 'resolved'){
    return promise2 = new MyPromise(function(resolve, reject){
      try{
        var x = onResolved(that.data)
        if(x instanceof MyPromise){ // 如果onResolved的返回值是一个Promise对象,直接取它的结果做为promise2的结果
          x.then(resolve, reject)
        }
        resolve(x) // 否则,以它的返回值做为promise2的结果
      } catch(e) {
        reject(e) // 如果出错,以捕获到的错误做为promise2的结果
      }
    })
  }

  if(that.status === 'rejected'){
    return promise2 = new MyPromise(function(resolve, reject){
      try{
        var x = onRejected(that.data)
        if(x instanceof MyPromise){
          x.then(resolve, reject)
        }
      } catch(e) {
        reject(e)
      }
    })
  }

  if(that.status === 'pending'){
    return promise2 = new MyPromise(function(resolve, reject){
      self.onResolvedCallback.push(function(reason){
        try{
          var x = onResolved(that.data)
          if(x instanceof MyPromise){
            x.then(resolve, reject)
          }
        } catch(e) {
          reject(e)
        }
      })

      self.onRejectedCallback.push(function(value){
        try{
          var x = onRejected(that.data)
          if(x instanceof MyPromise){
            x.then(resolve, reject)
          }
        } catch(e) {
          reject(e)
        }
      })
    })
  }
}

MyPromise.prototype.catch = function(onRejected){
  return this.then(null, onRejected)
}

// 以下是简单的测试样例:
new MyPromise(resolve => resolve(8)).then(value => {
  console.log(value)
})

1.8 Event Loop

题目:说一下JS的EventLoop

其实阮一峰老师这篇《JavaScript 运行机制详解:再谈Event Loop》已经讲的很清晰了(手动赞)!

这里简单总结下:

  1. JS是单线程的,其上面的所有任务都是在两个地方执行:执行栈和任务队列。前者是存放同步任务;后者是异步任务有结果后,就在其中放入一个事件。
  2. 当执行栈的任务都执行完了(栈空),js会读取任务队列,并将可以执行的任务从任务队列丢到执行栈中执行。
  3. 这个过程是循环进行,所以称作Loop

2. CSS相关

2.1 水平垂直居中

题目: 两种以上方式实现已知或者未知宽度的垂直水平居中

第一种方法就是利用CSS3translate进行偏移定位,注意:两个参数的百分比都是针对元素本身计算的。

.wrap {
  position: relative;
  width: 100vw;
  height: 100vh;
}

.box {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

第二种方法是利用CSS3flex布局,父元素diplay属性设置为flex,并且定义元素在两条轴线的布局方式均为center

.wrap {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100vw;
  height: 100vh;
}

.wrap .box {
  width: 100px;
  height: 100px;
}

第三种方法是利用margin负值来进行元素偏移,优点是浏览器兼容好,缺点是不够灵活(要自行计算margin的值):

.wrap {
  position: relative;
  width: 100vw;
  height: 100vh;
}

.box {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 100px;
  height: 100px;
  margin: -50px 0 0 -50px;
}

2.2 “点击”改变样式

题目:实现效果,点击容器内的图标,图标边框变成border 1px solid red,点击空白处重置。

利用event.target可以判断是否是指定元素本身(判断“空白处”),除此之外,注意禁止冒泡(题目指明了“容器内”)。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <style>
  #app {
    min-width: 100vw;
    min-height: 100vh;
  }
  #app .icon{
    display: inline-block;
    cursor: pointer;
  }
  </style>
</head>
<body>
  <div id="app">
    <span class="icon">123456</span>
  </div>
  <script>
  const app = document.querySelector("#app")
  const icon = document.querySelector(".icon")

  app.addEventListener("click", e => {
    if(e.target === icon){
      return;
    }
    // 非空白处才去除 border
    icon.style.border = "none";
  })

  icon.addEventListener("click", e => {
    // 禁止冒泡
    e.stopPropagation()
    // 更改样式
    icon.style.border = "1px solid red";
  })
  </script>
</body>
</html>

3. 浏览器/协议相关

3.1 缓存机制

题目:说一下浏览器的缓存机制。

浏览器缓存分为强缓存和协商缓存。缓存的作用是提高客户端速度、节省网络流量、降低服务器压力

强缓存:浏览器请求资源,如果header中的Cache-ControlExpires没有过期,直接从缓存(本地)读取资源,不需要再向服务器请求资源。

协商缓存:浏览器请求的资源如果是过期的,那么会向服务器发送请求,header中带有Etag字段。服务器再进行判断,如果ETag匹配,则返回给客户端300系列状态码,客户端继续使用本地缓存;否则,客户端会重新获取数据资源。

关于过程中详细的字段,可以参考这篇《http协商缓存VS强缓存

3.2 从URL到页面生成

题目:输入URL到看到页面发生的全过程,越详细越好

  1. DNS解析
  2. 建立TCP连接(3次握手)
  3. 发送HTTP请求,从服务器下载相关内容
  4. 浏览器构建DOM树和CSS树,然后生成渲染树。这个一个渐进式过程,引擎会力求最快将内容呈现给用户。
  5. 在第四步的过程中,<script>的位置和加载方式会影响响应速度。
  6. 搞定了,关闭TCP连接(4次握手)

3.3 TCP握手

题目:解释TCP建立的时候的3次握手和关闭时候的4次握手

看这题的时候,我也是突然懵(手动捂脸)。推荐翻一下计算机网络的相关书籍,对于FINACK等字段的讲解很赞!

3.4 CSS和JS位置

题目:CSS和JS的位置会影响页面效率,为什么?

先说CSS。CSS的位置不会影响加载速度,但是CSS一般放在<head>标签中。前面有说DOM树和CSS树共同生成渲染树,CSS位置太靠后的话,在CSS加载之前,可能会出现闪屏、样式混乱、白屏等情况。

再说JS。JS是阻塞加载,默认的<script>标签会加载并且立即执行脚本,如果脚本很复杂或者网络不好,会出现很久的白屏。所以,JS标签一般放到<body>标签最后。

现在,也可以为<script>标签设置async或者defer属性。前者是js脚本的加载和执行将与后续文档的加载和渲染同步执行。后者是js脚本的加载将与后续文档的加载和渲染同步执行,当所有元素解析完,再执行js脚本。

4. 算法相关

4.1 数组全排列

题目:现在有一个数组[1,2,3,4],请实现算法,得到这个数组的全排列的数组,如[2,1,3,4],[2,1,4,3]。。。。你这个算法的时间复杂度是多少

实现思路:从“开始元素”起,每个元素都和开始元素进行交换;不断缩小范围,最后输出这种排列。暴力法的时间复杂度是 \(O(N_N)\),递归实现的时间复杂度是 \(O(N!)\)

如何去重?去重的全排列就是从第一个数字起每个数分别与它后面非重复出现的数字交换。对于有重复元素的数组,例如:[1, 2, 2],应该剔除重复的情况。每次只需要检查arr[start, i)中是不是有和arr[i]相同的元素,有的话,说明之前已经输出过了,不需要考虑。

代码实现:

const swap = (arr, i, j) => {
  let tmp = arr[i]
  arr[i] = arr[j]
  arr[j] = tmp
}

const permutation = arr => {
  const _permutation = (arr, start) => {
    if(start === arr.length){
      console.log(arr)
      return
    }
    for(let i = start; i < arr.length; ++i){
      // 全排列:去重操作
      if(arr.slice(start, i).indexOf(arr[i]) !== -1){
        continue
      }
      swap(arr, i, start) // 和开始元素进行交换
      _permutation(arr, start + 1)
      swap(arr, i, start) // 恢复数组
    }
    return
  }
  return _permutation(arr, 0)
}

permutation([1, 2, 2])
console.log("**********")
permutation([1, 2, 3, 4])

4.2 背包问题

题目:我现在有一个背包,容量为m,然后有n个货物,重量分别为w1,w2,w3...wn,每个货物的价值是v1,v2,v3...vn,w和v没有任何关系,请求背包能装下的最大价值。

这个还在学习中,背包问题博大精深。。。

4.3 图的连通分量

题目:我现在有一个canvas,上面随机布着一些黑块,请实现方法,计算canvas上有多少个黑块。

这一题可以转化成图的联通分量问题。通过getImageData获得像素数组,从头到尾遍历一遍,就可以判断每个像素是否是黑色。同时,准备一个width * height大小的二维数组,这个数组的每个元素是1/0。如果是黑色,二维数组对应元素就置1;否则置0。

然后问题就被转换成了图的连通分量问题。可以通过深度优先遍历或者并查集来实现。之前我用C++实现了,这里不再冗赘:

5. Web工程化

5.1 Dialog组件思路

题目:现在要你完成一个Dialog组件,说说你设计的思路?它应该有什么功能?

  • 可以指定宽度、高度和位置
  • 需要一个遮盖层,遮住底层内容
  • 由头部、尾部和正文构成
  • 需要监听事件和自定义事件,非单向数据流:例如点击组件右上角,修改父组件的visible属性,关闭组件。

关于工程化组件封装,可以去试试ElementUI。这个是ElementUI的Dialog组件:Element-Dialog

5.2 React的Diff算法和虚拟DOM

题目: react 的虚拟dom是怎么实现的

原答案写的挺好滴,这里直接贴了。

首先说说为什么要使用Virturl DOM,因为操作真实DOM的耗费的性能代价太高,所以react内部使用js实现了一套dom结构。
在每次操作在和真实dom之前,使用实现好的diff算法,对虚拟dom进行比较,递归找出有变化的dom节点,然后对其进行更新操作。
为了实现虚拟DOM,我们需要把每一种节点类型抽象成对象,每一种节点类型有自己的属性,也就是prop,每次进行diff的时候,react会先比较该节点类型:
假如节点类型不一样,那么react会直接删除该节点,然后直接创建新的节点插入到其中;
假如节点类型一样,那么会比较prop是否有更新,假如有prop不一样,那么react会判定该节点有更新,那么重渲染该节点,然后在对其子节点进行比较,一层一层往下,直到没有子节点。

参考链接:React源码之Diff算法

最后,欢迎来我的博客和我扯犊子:godbmw.com。直接戳本篇原文的地址:刷《一年半经验,百度、有赞、阿里面试总结》·手记

原文地址:https://www.cnblogs.com/geyouneihan/p/10041412.html

时间: 2024-10-13 05:00:56

刷《一年半经验,百度、有赞、阿里面试总结》·手记的相关文章

半经验分子轨道方法:初步印象(更新中)

分子轨道理论的两条发展思路: 1.向快速计算发展 – 可以计算更大体系 2.向精确计算发展 Term: Semiempirical implementations of MO 半经验分子轨道方法 半经验方法的核心思路:各种简化久期行列式 半经验方法的具体形式: 1.EHT: Extended Hückel Theory扩展休克尔理论 2.CNDO: Complete Neglect of Differential Overlap以及思路相近的INDO和MINDO.SINDO等 3.NDDO: N

[转]关于Web前端开发,附:(百度web前端笔试面试题目)

关于Web前端及百度web前端笔试面试题目 随着各大互联网公司设立了Web前端开发工程师.设计工程师等职位,web前端越来越得到互联网企业的认可.而且其重视程度与地位也随着浏览器 端的富客户端的体现而日益提高. 眼前对HTML5的未来和走向,业内的预测是会和Flash.Silverlight等相结合,从而取代传统的客户端应用程序.而实现这个目标的客户端核 心工作是有Web前端工程师来完成的. 从另一个角度,对于web产品来说,交互和用户体验是产品的第一价值,这部分价值的体现就是在web前端.可以

阿里面试总结:如何介绍自己的项目经验(Java为例)

序言 在面试时,经过寒暄后,一般面试官会让你介绍项目经验.常见的问法是:"说下你最近的(或最拿得出手的)一个项目". 可能不少程序员对此没准备,说起来磕磕巴巴,甚至有人说出项目经验从时间段或技术等方面和简历上的不匹配,这样就会造成如下的后果: 第一印象就不好了,至少会感觉该候选人表述能力不强. 一般来说,面试官会根据程序员介绍的项目背景来提问题.假设面试时会问10个问题,那么至少有5个问题会根据程序员所介绍的项目背景来问,程序员如果没说好,那么就没法很好地引导后继问题了,就相当于把提问

阿里巴巴、百度Android实习生,面试之旅(下)

上一篇说到笔试结束,这一篇就从面试开始说.阿里巴巴是4月2号笔试的,百度笔试是在4月19号,然后阿里巴巴一周就会给一个结果,不管你过不过,百度就不会了,尤其是简历赛选环节,根本不会给你通知,都不知道是还在筛选还是没过,非常坑-我去年投的的一个实习生,现在简历还在筛选中,我一个同学和我一起投的,只是岗位不一样,到现在百度都还没给通知,都不知道是什么问题. 说一个题外话,我的情况可能和各位要去实习的不一样,我是大四的,今年九月份是要上研究生的,想趁毕业到开学这段时间去实习,所以我是不符合公司招聘的条

备战金九银十:三面百度,我的面试经历分享,助你秋招一臂之力

一面(主要是技术部分) 1.volatile和synchronized原理2.HashMap,ConcurrentHashMap3.epoll,select,poll4.TCP,UDP(tcp的具体状态没答出来5.jvm内存分布,垃圾收集,eden区6.jvm调优(不会7.java对象头里有什么(只答了age,monitor,其他的忘了8.mysql主从复制 项目(围绕项目问了一些问题) 根据项目出了场景题(dubbo,推荐框架,spark streaming): 1.为什么用mongo2.my

【面试题】百度糯米java工程师面试

面试经历: 技术题问的比较基础的java知识,有个编程题设计团购秒杀方面的设计,之前没有这种经验做的不好,做完题一个技术经理过来面试,主要问了一下之前做的什么项目,对struts,spring的原理做了一些深入的提问,基本都答出来了,感觉还不错. 面试官提出的问题 问题1: 对大数据量,数据库优化方面有什么经验. 回  答: 主要从sql和程序逻辑上两方面进行优化. 面试经历: 到地方以后,先填了一下个人资料,然后等了一会就在电脑上打开一个word文档进行机试,里面有不少题目,一些基本问题,然后

Android集成讯飞语音、百度语音、阿里语音识别

项目实践:https://blog.csdn.net/Jsagacity/article/details/80094164 demo下载地址:https://fir.im/jy28 demo源码:https://github.com/wapchief/android-CollectionDemo 百度网盘:https://pan.baidu.com/s/1gvoTlcddstxb7tyS8MvWzw密码:l5gd 2 科大讯飞  https://www.xfyun.cn/ 网友使用1https:

Java开发面试题,3年工作经验的Java程序员面试经

一.Java基础部分 1.使用length属性获取数组长度,public.private.protected.friendly区别 2.Collection和Collections区别 3.String s=new String('xyz');创建了几个object对象 4.short s1; s1=s1+1;是否有错? 5.Overriding和Overloading区别 6.Set里面的元素不能重复,用什么方法区分重复与否. 7.给出一个常见的runtime exception. 8.err

[经验]老梁 Java 面试通关要点汇总集

https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247484960&idx=1&sn=3d00e1c148c5dfc93de427bf5862f785&chksm=fa497991cd3ef08758226e7d383eb528472e8fb58117b2e200c6590fede987316f8f8cf9f8a8&mpshare=1&scene=1&srcid=0826zCOGJhF4Z