修炼秘籍

2019-03-07  本文已影响2人  王大白_

引言

当下,正面临着近几年来的最严重的互联网寒冬,听得最多的一句话便是:相见于江湖~🤣。缩减 HC、裁员不绝于耳,大家都是人心惶惶,年前如此,年后想必肯定又是一场更为惨烈的江湖厮杀。但博主始终相信,寒冬之中,人才更是尤为珍贵。只要有过硬的操作和装备,在逆风局下,同样也能来一波收割翻盘。

博主也是年前经历了一番厮杀,最终拿到多家大厂的 offer。在闭关修炼的过程中,自己整理出了一套面试秘籍供自己反复研究,后来给了多位有需要的兄台,均表示相当靠谱,理应在这寒冬之中回报于社会。于是决定花点精力整理成文,让大家能比较系统的反复学习,快速提升自己。

面试固然有技巧,但绝不是伪造与吹流弊,通过一段短时间沉下心来闭关修炼,出山收割,步入大厂,薪资翻番,岂不爽哉?🤓

修炼原则

想必大家很厌烦笔试和考察知识点。因为其实在平时实战中,讲究的是开发效率,很少会去刻意记下一些细节和深挖知识点,脑海中都是一些分散的知识点,无法系统性地关联成网,一直处于似曾相识的状态。不知道多少人和博主一样,至今每次写阻止冒泡都需要谷歌一番如何拼写。🤪。

以如此的状态,定然是无法在面试的战场上纵横的。其实面试就犹如考试,大家回想下高考之前所做的事,无非就是 理解系统性关联记忆。本秘籍的知识点较多,花点时间一个个理解并记忆后,自然也就融会贯通,无所畏惧。

由于本秘籍为了便于记忆,快速达到应试状态,类似于复习知识大纲。知识点会尽量的精简与提炼知识脉络,并不去展开深入细节,面面俱到。有兴趣或者有疑问的童鞋可以自行谷歌下对应知识点的详细内容。😋

CSS

1. 盒模型

页面渲染时,dom 元素所采用的 布局模型。可通过box-sizing进行设置。根据计算宽高的区域可分为:

2. BFC

块级格式化上下文,是一个独立的渲染区域,让处于 BFC 内部的元素与外部的元素相互隔离,使内外元素的定位不会相互影响。

IE 下为 Layout,可通过 zoom:1 触发

3.层叠上下文

元素提升为一个比较特殊的图层,在三维空间中 (z 轴) 高出普通元素一等。

76ad2eda-85c8-f1b1-561f-f9976d62a07d.png76ad2eda-85c8-f1b1-561f-f9976d62a07d.png

4. 居中布局

5. 选择器优先级

6.去除浮动影响,防止父级高度塌陷

7.link 与 @import 的区别

8. CSS 预处理器(Sass/Less/Postcss)

CSS 预处理器的原理: 是将类 CSS 语言通过 Webpack 编译 转成浏览器可读的真正 CSS。在这层编译之上,便可以赋予 CSS 更多更强大的功能,常用功能:

面试中一般不会重点考察该点,一般介绍下自己在实战项目中的经验即可~

9.CSS 动画

经验

通常,CSS 并不是重点的考察领域,但这其实是由于现在国内业界对 CSS 的专注不够导致的,真正精通并专注于 CSS 的团队和人才并不多。因此如果能在 CSS 领域有自己的见解和经验,反而会为相当的加分和脱颖而出。

JavaScript

1. 原型 / 构造函数 / 实例

说了一大堆,大家可能有点懵逼,这里来举个栗子,以Object为例,我们常用的Object便是一个构造函数,因此我们可以通过它构建实例。

// 实例
const instance = new Object();

则此时, 实例为instance, 构造函数为Object,我们知道,构造函数拥有一个prototype的属性指向原型,因此原型为:

// 原型
const prototype = Object.prototype;

这里我们可以来看出三者的关系:

实例.__proto__ === 原型

原型.constructor === 构造函数

构造函数.prototype === 原型

// 这条线其实是是基于原型进行获取的,可以理解成一条基于原型的映射线
// 例如:
// const o = new Object()
// o.constructor === Object   --> true
// o.__proto__ = null;
// o.constructor === Object   --> false
实例.constructor === 构造函数

此处感谢 caihaihong 童鞋的指出。

放大来看,我画了张图供大家彻底理解:

1cbeb6a1-96e6-2c05-4a2d-71213890be36.png1cbeb6a1-96e6-2c05-4a2d-71213890be36.png

2.原型链:

原型链是由原型对象组成,每个对象都有 __proto__ 属性,指向了创建该对象的构造函数的原型,__proto__ 将对象连接起来组成了原型链。是一个用来实现继承和共享属性的有限的对象链。

3. 执行上下文(EC)

执行上下文可以简单理解为一个对象:

2.变量对象

变量对象,是执行上下文中的一部分,可以抽象为一种 数据作用域,其实也可以理解为就是一个简单的对象,它存储着该执行上下文中的所有 变量和函数声明(不包含函数表达式)

活动对象 (AO): 当变量对象所处的上下文为 active EC 时,称为活动对象。

3. 作用域

执行上下文中还包含作用域链。理解作用域之前,先介绍下作用域。作用域其实可理解为该上下文中声明的 变量和声明的作用范围。可分为 块级作用域函数作用域

特性:

let foo = (function() {
  console.log(1);
})(
  (function foo() {
    foo = 10; // 由于foo在函数中只为可读,因此赋值无效
    console.log(foo);
  })()
);

// 结果打印:  ƒ foo() { foo = 10 ; console.log(foo) }

4.作用域链

我们知道,我们可以在执行上下文中访问到父级甚至全局的变量,这便是作用域链的功劳。作用域链可以理解为一组对象列表,包含 父级和自身的变量对象,因此我们便能通过作用域链访问到父级里声明的变量或者函数。

如此 [[scopr]]包含[[scope]],便自上而下形成一条 链式作用域

5. 闭包

闭包属于一种特殊的作用域,称为 静态作用域。它的定义可以理解为: 父函数被销毁 的情况下,返回出的子函数的[[scope]]中仍然保留着父级的单变量对象和作用域链,因此可以继续访问到父级的变量对象,这样的函数称为闭包。

6. script 引入方式:

7. 对象的拷贝

8. new 运算符的执行过程

9. instanceof 原理

能在实例的 原型对象链 中找到该构造函数的prototype属性所指向的 原型对象,就返回true。即:

// __proto__: 代表原型对象链
instance.[__proto__...] === instance.constructor.prototype

// return true

10. 代码的复用

当你发现任何代码开始写第二遍时,就要开始考虑如何复用。一般有以下的方式:

11. 继承

在 JS 中,继承通常指的便是 原型链继承,也就是通过指定原型,并可以通过原型链继承原型上的属性或者方法。

var inherit = (function(c, p) {
  var F = function() {};
  return function(c, p) {
    F.prototype = p.prototype;
    c.prototype = new F();
    c.uber = p.prototype;
    c.prototype.constructor = c;
  };
})();

12. 类型转换

大家都知道 JS 中在使用运算符号或者对比符时,会自带隐式转换,规则如下:

13. 类型判断

判断 Target 的类型,单单用 typeof 并无法完全满足,这其实并不是 bug,本质原因是 JS 的万物皆对象的理论。因此要真正完美判断时,我们需要区分对待:

很稳的判断封装:

let class2type = {};
"Array Date RegExp Object Error"
  .split(" ")
  .forEach(e => (class2type["[object " + e + "]"] = e.toLowerCase()));

function type(obj) {
  if (obj == null) return String(obj);
  return typeof obj === "object"
    ? class2type[Object.prototype.toString.call(obj)] || "object"
    : typeof obj;
}

14. 模块化

模块化开发在现代开发中已是必不可少的一部分,它大大提高了项目的可维护、可拓展和可协作性。通常,我们 在浏览器中使用 ES6 的模块化支持,在 Node 中使用 commonjs 的模块化支持。

15. 防抖与节流

防抖与节流函数是一种最常用的 高频触发优化方式,能对性能有较大的帮助。

function debounce(fn, wait, immediate) {
  let timer = null;

  return function() {
    let args = arguments;
    let context = this;

    if (immediate && !timer) {
      fn.apply(context, args);
    }

    if (timer) clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(context, args);
    }, wait);
  };
}
function throttle(fn, wait, immediate) {
  let timer = null;
  let callNow = immediate;

  return function() {
    let context = this,
      args = arguments;

    if (callNow) {
      fn.apply(context, args);
      callNow = false;
    }

    if (!timer) {
      timer = setTimeout(() => {
        fn.apply(context, args);
        timer = null;
      }, wait);
    }
  };
}

16. 函数执行改变 this

由于 JS 的设计原理: 在函数中,可以引用运行环境中的变量。因此就需要一个机制来让我们可以在函数体内部获取当前的运行环境,这便是this

因此要明白 this 指向,其实就是要搞清楚 函数的运行环境,说人话就是,谁调用了函数。例如:

但这种机制并不完全能满足我们的业务需求,因此提供了三种方式可以手动修改 this 的指向:

17. ES6/ES7

由于 Babel 的强大和普及,现在 ES6/ES7 基本上已经是现代化开发的必备了。通过新的语法糖,能让代码整体更为简洁和易读。

18. AST

抽象语法树 (Abstract Syntax Tree),是将代码逐字母解析成 树状对象 的形式。这是语言之间的转换、代码语法检查,代码风格检查,代码格式化,代码高亮,代码错误提示,代码自动补全等等的基础。例如:


function square(n){
return n \* n
}

通过解析转化成的AST如下图:

4c2a1231-bf3e-fa66-0129-113d6f906310.png4c2a1231-bf3e-fa66-0129-113d6f906310.png

19. babel 编译原理

20. 函数柯里化

在一个函数中,首先填充几个参数,然后再返回一个新的函数的技术,称为函数的柯里化。通常可用于在不侵入函数的前提下,为函数 预置通用参数,供多次重复调用。

const add = function add(x) {
  return function(y) {
    return x + y;
  };
};

const add1 = add(1);

add1(2) === 3;
add1(20) === 21;

21. 数组(array)

var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
arr.sort(function() {
  return Math.random() - 0.5;
});
Array.prototype.flat = function() {
  this.toString()
    .split(",")
    .map(item => +item);
};

22. 函数式编程(Shopee 面试题

const removeCharacter = str => str.replace(/[^\w\s]/g, " ");
const toUpper = str => str.toUpperCase();
const split = str => str.split(" ");
const filterEmpty = arr => arr.filter(str => !!str.trim().length);

const fn = compose(
  removeCharacter,
  toUpper,
  split,
  filterEmpty
);

fn("Hello, to8to World!"); // => ["HELLO","TO8TO","WORLD"]

// 请实现`compose`方法来达到效果

参考答案:

const compose = (...args) => {
  return str => args.reduce((prev, next) => next.call(null, prev), str);
};

浏览器

1. 跨标签页通讯

不同标签页间的通讯,本质原理就是去运用一些可以 共享的中间介质,因此比较常用的有以下方法:

2. 浏览器架构

3. 浏览器下事件循环(Event Loop)

事件循环是指: 执行一个宏任务,然后执行清空微任务列表,循环再执行宏任务,再清微任务列表

面试题(Shopee 面试题)

题目:


实现一个LazyMan,可以按照以下方式调用:
LazyMan(“Hank”)输出:
Hi! This is Hank!
 
LazyMan(“Hank”).sleep(10).eat(“dinner”)输出
Hi! This is Hank!
//等待10秒..
Wake up after 10
Eat dinner~
 
LazyMan(“Hank”).eat(“dinner”).eat(“supper”)输出
Hi This is Hank!
Eat dinner~
Eat supper~
 
LazyMan(“Hank”).sleepFirst(5).eat(“supper”)输出
//等待5秒
Wake up after 5
Hi This is Hank!
Eat supper


function Lazyman ( name ) {
    return new _Lazyman ( name );
}

class _Lazyman{
    constructor ( name ) {
        this.tasks = [];//设置任务队列
        let task = (name => () => {
            console.log ( `Hi! This is ${name} !` );
            this.next ();
        }) ( name );
        this.tasks.push ( task );
        //通过settimeout的方法,将执行函数放入下一个事件队列中,从而达到先注册事件,后执行的目的

        setTimeout ( () => {
            this.next ();
        }, 0 );

    }
    //尾调用函数,一个任务执行完然后再调用下一个任务
    next () {
        let task = this.tasks.shift ();
        task && task ();
    }

    eat ( food ) {
        let task = (food => () => {
            console.log ( `Eat ${food}` );
            this.next ();
        }) ( food );
        this.tasks.push ( task );
        return this;
    }

    sleep ( time ) {
        let task = (time => () => {
            setTimeout ( () => {
                console.log ( `Wake up after ${time} s!` );
                this.next ();
            }, time * 1000 )
        }) ( time );
        this.tasks.push ( task );
        return this;
    }

    sleepFirst ( time ) {
        let task = (time => () => {
            setTimeout ( () => {
                console.log ( `Wake up after ${time} s!` );
                this.next ();
            }, time * 1000 )
        }) ( time );
        this.tasks.unshift ( task );//sleepFirst函数需要最先执行,所以我们需要在任务队列前面放入,然后再执行后面的任务
        return this;
    }

}
}

4. 从输入 url 到展示的过程

5. 重绘与回流

当元素的样式发生变化时,浏览器需要触发更新,重新绘制元素。这个过程中,有两种类型的操作,即重绘与回流。

回流必定触发重绘,重绘不一定触发回流。重绘的开销较小,回流的代价较高。

最佳实践:

6. 存储

我们经常需要对业务中的一些数据进行存储,通常可以分为 短暂性存储 和 持久性储存。

7. Web Worker

现代浏览器为JavaScript创造的 多线程环境。可以新建并将部分任务分配到worker线程并行运行,两个线程可 独立运行,互不干扰,可通过自带的 消息机制 相互通信。

基本用法:

// 创建 worker
const worker = new Worker("work.js");

// 向主进程推送消息
worker.postMessage("Hello World");

// 监听主进程来的消息
worker.onmessage = function(event) {
  console.log("Received message " + event.data);
};

限制:

8. V8 垃圾回收机制

垃圾回收: 将内存中不再使用的数据进行清理,释放出内存空间。V8 将内存分成 新生代空间老生代空间

9. 内存泄露

可用 chrome 中的 timeline 进行内存标记,可视化查看内存的变化情况,找出异常点。

服务端与网络

1. http/https 协议

2. 常见状态码

3. get / post

两者详细对比如下图:

3f34b6df-1962-9a4c-e089-5fb8a2e7b2c9.png3f34b6df-1962-9a4c-e089-5fb8a2e7b2c9.png

4. Websocket

Websocket 是一个 持久化的协议, 基于 http , 服务端可以 主动 push

5. TCP 三次握手

建立连接前,客户端和服务端需要通过握手来确认对方:

6. TCP 四次挥手

7. Node 的 Event Loop: 6 个阶段

8. HTTP2,详细参考

跨域

function jsonp(url, jsonpCallback, success) {
  const script = document.createElement("script");
  script.src = url;
  script.async = true;
  script.type = "text/javascript";
  window[jsonpCallback] = function(data) {
    success && success(data);
  };
  document.body.appendChild(script);
}

安全

框架:Vue

1. nextTick

在下次dom更新循环结束之后执行延迟回调,可用于获取更新后的dom状态

2. 生命周期

上面是vue的声明周期的简单梳理,接下来我们直接以代码的形式来完成vue的初始化


new Vue({})

// 初始化 Vue 实例
function \_init() {
// 挂载属性
initLifeCycle(vm)
// 初始化事件系统,钩子函数等
initEvent(vm)
// 编译 slot、vnode
initRender(vm)
// 触发钩子
callHook(vm, 'beforeCreate')
// 添加 inject 功能
initInjection(vm)
// 完成数据响应性 props/data/watch/computed/methods
initState(vm)
// 添加 provide 功能
initProvide(vm)
// 触发钩子
callHook(vm, 'created')

     // 挂载节点
    if (vm.$options.el) {
        vm.$mount(vm.$options.el)
    }

}

// 挂载节点实现
function mountComponent(vm) {
// 获取 render function
if (!this.options.render) {
// template to render
// Vue.compile = compileToFunctions
let { render } = compileToFunctions()
this.options.render = render
}
// 触发钩子
callHook('beforeMounte')
// 初始化观察者
// render 渲染 vdom,
vdom = vm.render()
// update: 根据 diff 出的 patchs 挂载成真实的 dom
vm.\_update(vdom)
// 触发钩子
callHook(vm, 'mounted')
}

// 更新节点实现
funtion queueWatcher(watcher) {
nextTick(flushScheduleQueue)
}

// 清空队列
function flushScheduleQueue() {
    // 遍历队列中所有修改
for(){
        // beforeUpdate
        watcher.before()

        // 依赖局部更新节点
        watcher.update()
        callHook('updated')
    }

}

// 销毁实例实现
Vue.prototype.$destory = function() {
     // 触发钩子
    callHook(vm, 'beforeDestory')
    // 自身及子节点
    remove()
    // 删除依赖
    watcher.teardown()
    // 删除监听
    vm.$off()
// 触发钩子
callHook(vm, 'destoryed')
}

3. 数据响应(数据劫持)

看完生命周期后,里面的watcher等内容其实是数据响应中的一部分。数据响应的实现由两部分构成: 观察者( watcher )依赖收集器( Dep ),其核心是 defineProperty这个方法,它可以 重写属性的 get 与 set 方法,从而完成监听数据的改变。

大家可以先看下面的数据相应的代码实现后,理解后就比较容易看懂上面的简单脉络了。

let data = { a: 1 };
// 数据响应性
observe(data);

// 初始化观察者
new Watcher(data, "name", updateComponent);
data.a = 2;

// 简单表示用于数据更新后的操作
function updateComponent() {
  vm._update(); // patchs
}

// 监视对象
function observe(obj) {
  // 遍历对象,使用 get/set 重新定义对象的每个属性值
  Object.keys(obj).map(key => {
    defineReactive(obj, key, obj[key]);
  });
}

function defineReactive(obj, k, v) {
  // 递归子属性
  if (type(v) == "object") observe(v);

  // 新建依赖收集器
  let dep = new Dep();
  // 定义get/set
  Object.defineProperty(obj, k, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter() {
      // 当有获取该属性时,证明依赖于该对象,因此被添加进收集器中
      if (Dep.target) {
        dep.addSub(Dep.target);
      }
      return v;
    },
    // 重新设置值时,触发收集器的通知机制
    set: function reactiveSetter(nV) {
      v = nV;
      dep.nofify();
    }
  });
}

// 依赖收集器
class Dep {
  constructor() {
    this.subs = [];
  }
  addSub(sub) {
    this.subs.push(sub);
  }
  notify() {
    this.subs.map(sub => {
      sub.update();
    });
  }
}

Dep.target = null;

// 观察者
class Watcher {
  constructor(obj, key, cb) {
    Dep.target = this;
    this.cb = cb;
    this.obj = obj;
    this.key = key;
    this.value = obj[key];
    Dep.target = null;
  }
  addDep(Dep) {
    Dep.addSub(this);
  }
  update() {
    this.value = this.obj[this.key];
    this.cb(this.value);
  }
  before() {
    callHook("beforeUpdate");
  }
}

4. virtual dom 原理实现(腾讯 3.5 面试

// diff 算法的实现
function diff(oldTree, newTree) {
  // 差异收集
  let pathchs = {};
  dfs(oldTree, newTree, 0, pathchs);
  return pathchs;
}

function dfs(oldNode, newNode, index, pathchs) {
  let curPathchs = [];
  if (newNode) {
    // 当新旧节点的 tagName 和 key 值完全一致时
    if (oldNode.tagName === newNode.tagName && oldNode.key === newNode.key) {
      // 继续比对属性差异
      let props = diffProps(oldNode.props, newNode.props);
      curPathchs.push({ type: "changeProps", props });
      // 递归进入下一层级的比较
      diffChildrens(oldNode.children, newNode.children, index, pathchs);
    } else {
      // 当 tagName 或者 key 修改了后,表示已经是全新节点,无需再比
      curPathchs.push({ type: "replaceNode", node: newNode });
    }
  }

  // 构建出整颗差异树
  if (curPathchs.length) {
    if (pathchs[index]) {
      pathchs[index] = pathchs[index].concat(curPathchs);
    } else {
      pathchs[index] = curPathchs;
    }
  }
}

// 属性对比实现
function diffProps(oldProps, newProps) {
  let propsPathchs = [];
  // 遍历新旧属性列表
  // 查找删除项
  // 查找修改项
  // 查找新增项
  forin(olaProps, (k, v) => {
    if (!newProps.hasOwnProperty(k)) {
      propsPathchs.push({ type: "remove", prop: k });
    } else {
      if (v !== newProps[k]) {
        propsPathchs.push({ type: "change", prop: k, value: newProps[k] });
      }
    }
  });
  forin(newProps, (k, v) => {
    if (!oldProps.hasOwnProperty(k)) {
      propsPathchs.push({ type: "add", prop: k, value: v });
    }
  });
  return propsPathchs;
}

// 对比子级差异
function diffChildrens(oldChild, newChild, index, pathchs) {
  // 标记子级的删除/新增/移动
  let { change, list } = diffList(oldChild, newChild, index, pathchs);
  if (change.length) {
    if (pathchs[index]) {
      pathchs[index] = pathchs[index].concat(change);
    } else {
      pathchs[index] = change;
    }
  }

  // 根据 key 获取原本匹配的节点,进一步递归从头开始对比
  oldChild.map((item, i) => {
    let keyIndex = list.indexOf(item.key);
    if (keyIndex) {
      let node = newChild[keyIndex];
      // 进一步递归对比
      dfs(item, node, index, pathchs);
    }
  });
}

// 列表对比,主要也是根据 key 值查找匹配项
// 对比出新旧列表的新增/删除/移动
function diffList(oldList, newList, index, pathchs) {
  let change = [];
  let list = [];
  const newKeys = getKey(newList);
  oldList.map(v => {
    if (newKeys.indexOf(v.key) > -1) {
      list.push(v.key);
    } else {
      list.push(null);
    }
  });

  // 标记删除
  for (let i = list.length - 1; i >= 0; i--) {
    if (!list[i]) {
      list.splice(i, 1);
      change.push({ type: "remove", index: i });
    }
  }

  // 标记新增和移动
  newList.map((item, i) => {
    const key = item.key;
    const index = list.indexOf(key);
    if (index === -1 || key == null) {
      // 新增
      change.push({ type: "add", node: item, index: i });
      list.splice(i, 0, key);
    } else {
      // 移动
      if (index !== i) {
        change.push({
          type: "move",
          form: index,
          to: i
        });
        move(list, index, i);
      }
    }
  });

  return { change, list };
}

5. Proxy 相比于 defineProperty 的优势

let data = { a: 1 };
let reactiveData = new Proxy(data, {
  get: function(target, name) {
    // ...
  }
  // ...
});

6. vue-router

7. vuex

算法

其实算法方面在前端的实际项目中涉及得并不多,但还是需要精通一些基础性的算法,一些公司还是会有这方面的需求和考核,建议大家还是需要稍微准备下,这属于加分题。

1. 五大算法

2. 基础排序算法

function bubleSort(arr) {
  var len = arr.length;
  for (let outer = len; outer >= 2; outer--) {
    for (let inner = 0; inner <= outer - 1; inner++) {
      if (arr[inner] > arr[inner + 1]) {
        [arr[inner], arr[inner + 1]] = [arr[inner + 1], arr[inner]];
      }
    }
  }
  return arr;
}
function selectSort(arr) {
  var len = arr.length;
  for (let i = 0; i < len - 1; i++) {
    for (let j = i; j < len; j++) {
      if (arr[j] < arr[i]) {
        [arr[i], arr[j]] = [arr[j], arr[i]];
      }
    }
  }
  return arr;
}
function insertSort(arr) {
  for (let i = 1; i < arr.length; i++) {
    //外循环从 1 开始,默认 arr[0]是有序段
    for (let j = i; j > 0; j--) {
      //j = i,将 arr[j]依次插入有序段中
      if (arr[j] < arr[j - 1]) {
        [arr[j], arr[j - 1]] = [arr[j - 1], arr[j]];
      } else {
        break;
      }
    }
  }
  return arr;
}

3. 高级排序算法

function quickSort(arr) {
  if (arr.length <= 1) {
    return arr; //递归出口
  }
  var left = [],
    right = [],
    current = arr.splice(0, 1);
  for (let i = 0; i < arr.length; i++) {
    if (arr[i] < current) {
      left.push(arr[i]); //放在左边
    } else {
      right.push(arr[i]); //放在右边
    }
  }
  return quickSort(left).concat(current, quickSort(right));
}
02ed96c9-28a6-d177-240e-eebb59ef1ca0.png02ed96c9-28a6-d177-240e-eebb59ef1ca0.png

稳定性: 同大小情况下是否可能会被交换位置, 虚拟 dom 的 diff,不稳定性会导致重新渲染;

4. 递归运用(斐波那契数列): 爬楼梯问题

初始在第一级,到第一级有 1 种方法(s(1) = 1),到第二级也只有一种方法(s(2) = 1), 第三级(s(3) = s(1) + s(2))

function cStairs(n) {
  if (n === 1 || n === 2) {
    return 1;
  } else {
    return cStairs(n - 1) + cStairs(n - 2);
  }
}

5. 数据树

7e94450b-9402-c9fa-d304-f8adff15976d.png7e94450b-9402-c9fa-d304-f8adff15976d.png

6. 天平找次品

有 n 个硬币,其中 1 个为假币,假币重量较轻,你有一把天平,请问,至少需要称多少次能保证一定找到假币?

7. 大整数相加(腾讯 3.5 面试题

function sum(a, b) {
  var res = "",
    temp = 0;
  const aArr = `${a}`.split("");
  const bArr = `${b}`.split("");
  const langArr = aArr.length > bArr.length ? aArr : bArr;
  const shortArr = aArr.length > bArr.length ? bArr : aArr;
  while (shortArr.length || temp) {
    temp += ~~aArr.pop() + ~~bArr.pop();
    res = (temp % 10) + res;
    temp = temp > 9;
  }
  res = langArr.join("") + res;
  return res.replace(/^0+/, "");
}

// 面试官说还有更优的算法,我想了下应该可以对数字分段补0相加的方式。懒得写了

前端性能优化(重要重要重要,腾讯就问这个!!

参考文章:
https://juejin.im/post/59672fbff265da6c3f70cd53

1. 浏览器渲染页面

2. 减少 HTTP 请求

3. 减少静态资源体积

4. 使用缓存

详细参考:缓存策略

5. 内存溢出

前端性能监控(重要重要重要,腾讯就问这个!!

参考文章:
https://juejin.im/post/5b7a50c0e51d4538af60d995

结语

由于精力时间及篇幅有限,这篇就先写到这。大家慢慢来不急。。🤪。下篇打算准备以下内容,我也得补补课先:

在面试中,很多领域并没有真正的答案,能回答到什么样的深度,还是得靠自己真正的去使用和研究。知识面的广度与深度应该并行,尽量的拓张自己的领域,至少都有些基础性的了解,在被问到的时候可以同面试官唠嗑两句,然后在自己喜欢的领域,又有着足够深入的研究,让面试官觉得你是这方面的专家。

知识大纲还在不断的完善和修正,由于也是精力时间有限,我会慢慢补充后面列出来的部分。当然,我也是在整理中不断的学习,也希望大家能一起参与进来,要补充或修正的地方麻烦赶紧提出。另外,刚新建了个公众号,想作为大家交流和分享的地方,有兴趣想法的童鞋联系我哈~~😉

作者:郭东东
链接:https://juejin.im/post/5c64d15d6fb9a049d37f9c20
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

上一篇 下一篇

猜你喜欢

热点阅读