作为前端面试官,我会问哪些问题?(基础篇:附答案)

1,622 阅读7分钟

基础题

我出的这套基础题,面试(当面答题,想百度是不存在的)好多人,情况都比较差,没有令我满意的,是我出题太难了么? .github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}pre{font-size: 24px;line-height: 1.8}

3.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body">.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}pre{font-size: 24px;line-height: 1.8}

正常会有3轮技术面试, 第一和第二轮是基础面试

一面: 主要涵盖html,css,js基础, http协议等日常开发的常问题

二面: 主要是react,vue框架应用, webpack,rollup等工程化工具的运用

三面: 简单的算法, 系统设计的能力, 独立开发的能力

介绍一下 js 原型继承

参考答案

当我们想要从 object 中读取一个缺失的属性时,js 会自动从原型中获取它
举例:
const animal = {
  eats: true,
  sleep: true,
  voice: '',
  bark(){
    console.log(this.voice);
  },
}
const dog = {
  run: true,
  voice: 'wang~~ wang~~',
}
dog.__proto__ = animal;
console.log(dog.run) // true
console.log(dog.eats) // true
console.log(dog.bark()) // wang~~ wang~~
console.log(dog.sleep) // true
总结:
  所有的对象都有一个隐藏的 [[Prototype]] 属性,它可以是另一个对象或者 null。
  可以使用 obj.__proto__ 进行访问。
  [[Prototype]] 引用的对象称为“原型”。
  要读取 obj 属性或者调用一个方法,而且它不存在,那么就会尝试在原型中查找它。
  写/删除直接在对象上进行操作,它们不使用原型(除非属性实际上是一个 setter)。
  我们调用 obj.method(),而且 method 是从原型中获取的,this 仍然会引用 obj。
  方法重视与当前对象一起工作,即使它们是继承的。
扩展:
  构造函数继承、组合继承、寄生继承、寄生组合式继承。

说说什么是防抖、节流,在实际应用中在哪些场景会用到它们

参考答案

防抖:
  连续触发的事件(高频),在单位时间T内只执行最后一次,
  若在T内再次触发,则清空定时重新计算。场景:模糊搜索
节流:
  连续触发的事件(高频),在单位时间T内只执行一次。窗口滚动,获取滚动条top

javascript 如何实现跨窗口通信

参考答案

若子域同源,则可以通过设置document.domain将窗口视为同源站点,进行通信,
  例如本地储存等等方式。
若不同源,我们可以在通过 postMessage(data,targetOrigin) 这个接口,进行跨窗口通信。

什么是点击劫持,如何防范

参考答案

点击劫持:
  允许恶意网页以用户的名义点击 “受害站点”。
  通常恶意网页在受害网站链接之上放置一个透明 <iframe> 标签。
防范:
  服务端 header 字段 X-Frame-Options 能够允许或禁止 frame 内页面的显示。

介绍一下什么是单向链表?有哪些特点?你知道的在哪些方面有链表的应用

参考答案

链表(Linked list)是一种常见的基础数据结构,是一种线性表,
  但是并不会按线性的顺序储存数据,而是在每一个节点里存到下一个节点的指针(Pointer)。
  由于不必须按顺序储存,链表在插入的时候可以达到 o(1)的复杂度,比另一种线性表顺序表快得多,
  但是查找一个节点或者访问特定编号的节点则需要 o(n)的时间,而顺序表响应的时间复杂度分别是 o(logn)和 o(1)。
特点:
  无需预先分配内存,可以充分利用计算机内存空间,实现灵活的内存动态管理
  插入/删除节点不影响其他节点
  随机访问速度较慢
  增加了结点的指针域,空间开销比较大
单向链表:
  是链表中最简单的一种,它包含两个域,一个信息域和一个指针域。
  这个链接指向列表中的下一个节点,而最后一个节点则指向一个空值。
应用:
  git commit、es6的Iterator、react的fiber算法。

前后端路由差别

参考答案

前端路由(spa)纯浏览器行为,是由浏览器控制的(hashhistory),
  当打开一个spa页面后,切换路由,浏览器改变地址栏url并通过js展示对应页面(组件)
后端路由,切换路由时,服务端会去匹配并查找对应资源,返回给浏览器并渲染。

是否研究过 webpack,谈谈你对它的理解及 loader 的设计原则

参考答案
本题相对开放一些。
webpack个人理解:
  webpack是划时代的,比较完美解决了前端模块依赖的问题,任何资源都是js,任何资源都可以在js中声明依赖,
  真正实现了通用的模块化开发。
loader的设计原则:
  单一职责、所有模块都是js模块,webpack 只支持js模块,所有其他类型的模块,
  比如图片,css等,都需要通过对应的loader转成js模块。
  所以在webpack中无论任何类型的资源,本质上都被当成js模块处理。
  所有的loader都是一个管道,可以把一个loader看做是一个数据管道,
  进口是一个字符串,然后经过加工,输出另一个字符串。

给定一个一维数组 arr,将其按一定数量 num 分组为二维数组,例如: 条件为 arr=[0,1,2,3,4,5,6], num=3, 结果为[[0,1,2],[3,4,5],[6]]

参考答案

function transform(arr,num){
  const newArr = [];
  for(let i=0,len=arr.length;i < len;i=i+num){
    newArr.push(arr.slice(i,i+num));
  }
  return newArr;
}

假设我们有一个函数 slow(x,y,z,...) ,它是 CPU 重负载的,但它的结果是稳定的,换句话说,对于相同的输入 x,y,z...,它总是返回相同的结果 XYZ,我们该如何优化,才能避免在重新计算上花费额外的时间

function slow(x, y, z) {
  // 这里可能会有重负载的CPU密集型工作
  // ...

  alert(`Called with ${XYZ}`);
  return XYZ;
}
参考答案

function memo(func){
  const cache = new Map();
  return function(){
    // const key = [...arguments].join(',');
    const key = [].join.call(arguments);
    if(cache.has(key)){
      return cache.get(key);
    }
    const res = func.apply(this, arguments);
    cache.set(key, res);
    return res;
  }
}
memo(slow)(1,2,3);

用 js 实现一个发布-订阅模式

参考答案

class Event{
  constructor(){
    this.pool = new Map()
  }
  broadcast(){
    const [key,...rest] = [...arguments];
    if(this.pool.has(key)){
      const func = this.pool.get(key)
      func.apply(this, rest)
    }
  }
  addEventListenering(key, func){
    this.pool.set(key,func)
  }
  removeEventListenering(key){
    this.pool.delete(key)
  }
}
const ev = new Event();
ev.addEventListenering('event1',function(param){
  console.log('发布新值了',param)
})
ev.broadcast('event1',1000); //发布新值了 1000
ev.broadcast('event1',2000); //发布新值了 2000
ev.broadcast('event1',3000); //发布新值了 3000
ev.removeEventListenering('event1');
ev.broadcast('event1',3000);

以下代码打印的是什么

function A() {
  this.a = 1;
}
A.prototype.b = 1;

const a = new A();
console.log(a.a);
console.log(a.b);

const b = new A();
b.__proto__.a = 2;
A.prototype.b = 2;

console.log(a.a);
console.log(a.b);
参考答案
1 1 1 2

请输出下面代码的打印内容

var name = '小红';
var obj = {
  name: '小明',
  sayName() {
    console.log(`${this.name}`);
  },
};

var sayName = () => {
  console.log(`${this.name}`);
};
var fn = obj.sayName;

sayName();
obj.sayName();
sayName.call(obj);
obj.sayName.call(this);
fn();
参考答案
小红 小明 小红 小红 小红

请输出以下代码的打印内容

new Promise(resolve => {
  console.log(3);
  resolve();
}).then(() => {
  onsole.log(4);
});

setTimeout(() => {
  6;
  console.log(2);
}, 0);

Promise.resolve().then(() => {
  console.log(5);
});

console.log(1);
参考答案
3 1 4 5 2

px rem em vw vh 的区别和使用场景, 假设设计图是750px, 如何用rem或者vw做出适配(二选其一)

参考答案

如果是rem方案
  1. 定义开发过程的rem和设计图的比例,  假设是1:100,  那么设计图上面的100px对应开发中的1rem
  2. 动态设置根元素html上面的fontsize
  3. (window.innerWith/750) * 100
  4. viewport必须设置成with=device-width, (若考虑内嵌iframe/第三方video播放等功能,则不能缩放)
如果是vw方案
  1. vw方案一般只用于宽高
  2. 对于边距还要配合rem进行适配

用3种方式实现一个不限宽高的水平垂直居中

参考答案
定位、flextablegrid

如下代码中的alert会执行吗, 需要解释原理

if(3==true){
  alert('yes')
}
参考答案
考查js的隐式转换: 这里的布尔值会转换成1在和3进行比较

描述一下js里面的递归

一般来说一个完整的递归函数都有哪些要素

参考答案

判断条件, 什么时候结束递归

递归会有什么样的问题

参考答案

爆栈问题如何解决(一般来说是将递归改成while循环等, 循环引用可以做stack缓存处理)

实现一个方法: 反向输出一个单链表

const list = {
  value: 1,
  next: {
    value: 2,
    next: {
      value: 3,
      next: {
        value: 4,
        next: null,
      },
    },
  },
};
参考答案

function printReverseList1(list) {
  if (list.next) {
    printReverseList1(list.next);
  }


alert(list.value);
}




printReverseList1(list);




// divider




function printReverseList2(list) {
let arr = [];
let tmp = list;




while (tmp) {
arr.push(tmp.value);
tmp = tmp.next;
}




for (let i = arr.length - 1; i >= 0; i--) {
alert(arr[i]);
}
}




printReverseList2(list);

printReverseList2(list);

浏览器跨越如何解决, 至少谈到2种常用方式

参考答案

1. 反向代理
2. jsonp
3. cors(Cors方案需要说明浏览器什么时候发预检请求、Cors方案的cookie如何解决)

浏览器对于http请求的缓存策略

参考答案

说清楚强缓存和协商缓存
cache-control如何设置

Webview和js之间相互调用的实现方式

参考答案

1. Js调用native常用方式有3种
   1. Uri schema方式,  自定义协议 如: whatsapp://dosomething?a=1
   2. 拦截浏览器弹窗方式(prompt)
   3. Wkwebview会暴露一个内置的api.  js直接调用
2. Native调用js只有一种方法,  就是注入js代码并执行