interView - Azurehuihui/LearnJS GitHub Wiki

自我介绍

学前端是通过什么途径学的

各种技术官网(阮一峰),视频,博客, 书籍(js高级程序设计、深入理解ES6、vuejs实战), 咨询朋友

问工作经验经验

1、是独立负责一个项目还是和别人合作 团队协作

2、介绍下项目 项目中的亮点 做过些什么项目

3、项目中遇到的印象比较深刻的问题

4、项目是几个人开发的?角色定位?

5、 与别人相比的优势和劣势 

6、谈谈你如何看待前端岗位的工作职责,工作内容?

7、关于项目细节的一些灵魂拷问

1.结合js介绍下自己对面向对象 (封装 继承 多态的理解)

面向对象是一种编程思维,他把很多行为都存放到一个对象中,当你需要使用他是,根据对象来获取。根据名词找内容, 面向过程是执行执行动词  

大象装进冰箱 
面向过程
1、开冰箱
2、放大象
3、关冰箱  

面向对象
1、冰箱.开门
2、冰箱.装进
3、冰箱.关门


面向对象有3大特点: 封装、继承、多态

封装: 把一些行为和属性都归置到一个对象中,即事物的内部逻辑和属性对外不直接可见。当你需要的时候从对象中获取内容,这样可以降低程序的耦合度,保护事物的内部数据, 比如 {}, 立即执行函数,普通函数等

继承:对某一类事物的公共方法和属性进行抽象为父类, 子类可以继承父类的属性和方法,还可以扩展自己的属性和方法,js中可以通过原型链来实现继承,es6中通过语法糖class以及extends来实现继承

多态:某个方法在不同的条件下,会进行不同的执行动作,主要是重写(overwrite)和重载(override)

重写: 允许对方法重新实现,但是不能改变方法名, 形参列表和返回类型 

重载:允许子类改变方法的形式参数列表、返回值类型,但不允许改变签名,在调用方法时,根据输入的形式参数选择对应的重载(js中不直接支持)
多态是展现在继承中的,即父类和子类的同名方法的重写

2.说下vue的双向数据绑定

3.谈谈你对前后端分离的看法

前后端分离是一个项目管理层面上的决策,当展示页面的逻辑越来越复杂的时候,前后端的耦合性会非常强,一方面是前后端的界限不明确,那前后端的工作进度不好统一。由此产生了前后端分离的概念,并推动得到了像 vue react等前端框架。 

要做到前后端分离需要考虑整个项目的流程, 如方案设计,开发,测试,部署等。

1、前后端的边界在哪里
2、前后端协作之间的会话是怎么产生的?后端是怎么知道前端发过去的请求是谁发的

4.谈谈浏览器缓存看法, 包括强缓存协商缓存

 对于一个数据请求来说,可以分为发起网络请求、后端处理、浏览器响应三个步骤。浏览器缓存可以帮助我们在第一和第三步骤中优化性能。比如说直接使用缓存而不发起请求,或者发起了请求但后端存储的数据和前端一致,那么就没有必要再将数据回传回来,这样就减少了响应数据  

 所以良好的缓存策略可以减少不必要的数据传输,节省带宽,减少服务器的负担,提升网站性能,降低资源的重复加载,提高客户端网页的整体加载速度,总体来说用户体验友好

缺点:
资源如果有更改但是客户端不及时更新会造成用户获取信息滞后,如果老版本有bug的话,情况会更加糟糕  

缓存分为强缓存和协商缓存  

强缓存就是强制使用缓存, 当用户在浏览器输入一个url后,浏览器会先去缓存中查找是否有该资源,若有状态码200则直接使用该资源,若没有,就向服务器发起请求,服务器会先根据last-modified和etag确定该资源是否真的不存在,若还是存在,则返回状态码304和Not Modified的字符串,然后又去浏览器缓存中加载该资源,若真的没有,那么浏览器就获取该资源,并返回客户端,状态码是200,客户端会把该资源再存入缓存中。

协商缓存的可走的两个流程:

发请求-->看资源是否过期-->过期-->请求服务器-->服务器对比资源是否真的过期-->没过期-->返回304状态码-->客户端用缓存的老资源。 

发请求-->看资源是否过期-->过期-->请求服务器-->服务器对比资源是否真的过期-->过期-->返回200状态码-->客户端如第一次接收该资源一样,记下它的cache-control中的max-age、etag、last-modified等。  

1、基本原理
1)浏览器在加载资源时,根据请求头的expires和cache-control判断是否命中强缓存,是则直接从缓存读取资源,不会发请求到服务器。
2)如果没有命中强缓存,浏览器一定会发送一个请求到服务器,通过last-modified和etag验证资源是否命中协商缓存,如果命中,服务器会将这个请求返回,但是不会返回这个资源的数据,依然是从缓存中读取资源
3)如果前面两者都没有命中,直接从服务器加载资源
2、相同点
如果命中,都是从客户端缓存中加载资源,而不是从服务器加载资源数据;

3、不同点
强缓存不发请求到服务器,协商缓存会发请求到服务器。


etag的作用是唯一标识符

最后总结一下浏览器的三级缓存原理:

先去内存看,如果有,直接加载
如果内存没有,择取硬盘获取,如果有直接加载
如果硬盘也没有,那么就进行网络请求
加载到的资源缓存到硬盘和内存  

强制缓存优先于协商缓存进行,若强制缓存(Expires和Cache-Control)生效则直接使用缓存,若不生效则进行协商缓存(Last-Modified / If-Modified-Since和Etag / If-None-Match),协商缓存由服务器决定是否使用缓存,若协商缓存失效,那么代表该请求的缓存失效,返回200,重新返回资源和缓存标识,再存入浏览器缓存中;生效则返回304,继续使用缓存。  

具体参考资料:

资料1
资料2

怎么做缓存比较合适?每种资源都可以做缓存吗

5.谈谈缓存在你项目中的应用

我的项目中没有用到浏览器缓存,但用到了commonJS中的require缓存   

为什么不在浏览器也是用CommonJS
回答这个问题之前,我们首先要清楚一个事实,CommonJS的 require 语法是同步的,当我们使用require 加载一个模块的时候,必须要等这个模块加载完后,才会执行后面的代码。如果知道这个事实,那我们的问题也就很容易回答了。NodeJS 是服务端,使用 require 语法加载模块,一般是一个文件,只需要从本地硬盘中读取文件,它的速度是比较快的。但是在浏览器端就不一样了,文件一般存放在服务器或者CDN上,如果使用同步的方式加载一个模块还需要由网络来决定快慢,可能时间会很长,这样浏览器很容易进入“假死状态”。所以才有了后面的AMD和CMD模块化方案,它们都是异步加载的,比较适合在浏览器端使用
commonJS模块(nodejs)和ES6模块(浏览器)的加载和导出  

commonJS  
导出有两种形式: 
1、exports  
2、module.exports = {}

导入/加载
let x = require(modulePath) 

exports 是module.exports 的别名  exports 是指向module.exports 的应用
    相同点 :通过exports 和  module.exports 对外公开的方法都可以访问
    区别:  module.exports 不是真正的接口  返回模块对象本身
            exports 最终调用的是module.exports而不是exports  
            module.exports 指向是一个对象  
            exports 指向是对module.exports 的引用
            require 返回的是module.exports  在项目中使用exports 可能会报错,
   我们一般使用  module.exports

注意点: 
1、commonJs中的导入只是值的拷贝,不同的拷贝之间互不影响,且原先exports文件中的改动不会影响导出的内容  
2、commonJS模块是运行时加载的,且require()是同步加载模块
3、require会存在缓存,每次都使用第一个加载进来的东西,除非手动删除缓存  require.cache[]可以访问缓存
4、
// a.js
var counter = 3;
function incCounter() {
  return ++counter;
}
module.exports = {
  counter: counter,
  incCounter: incCounter,
};
// 然后在其他文件导入该模块  
let a = require('./a')

console.log(a.counter) // 3
console.log(a.incCounter()) // 4
console.log(a.counter); // 3

上面的例子充分说明了如果我们对外输出了counter 变量,就算后续调用模块内部的incCounter 方法去修改它的值,它的值依旧没有变化。

ES6
导出:
export  或者 export default
导入:
import XXX   

export 和 export default的区别?
     export 和 export default 都可以导出常量 ,函数 ,文件 , 模块
     export 可以导出多个
     export default  仅有一个
     export 导出   在导出的时候可以  import {} from 'xxx' 通过结构赋值导入相应的
     export default  导出   import xxx  from 'xxx'

JS 引擎对脚本静态分析的时候,遇到模块加载命令import,就会生成一个只读引用。等到脚本真正执行的时候,再根据这个只读引用,到被加载的那个模块里去取值。
// a.js
export let counter = 3;
export function incCounter() {
  return ++counter;
}
// b.js
import { counter, incCounter } from './a';
console.log(counter); // 3
console.log(incCounter()); // 4
console.log(counter); // 4

上面代码说明,ES6 模块import的变量counter是可变的,完全反应其所在模块lib.js内部的变化。 而第二个差异,也是为什么ES6模块这么受人欢迎的最大原因之一。我们知道CommonJS其实加载的是一个对象,这个对象只有在脚本运行时才会生成,而且只会生成一次。但是ES6模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成,这样我们就可以使用各种工具对JS模块进行依赖分析,优化代码,而Webpack中的 tree shaking 和 scope hoisting 实际上就是依赖ES6模块化。

注意点
1、import导出的模块是对象的一个引用,引用对象在一处的变化会导致其他引用的地方都发生改变。 且这个引用是只读的,可以修改这个引用的属性值,追加新属性,但不能重新赋值,即 obj = {}是不正确的。 

2、import出的内容,不同脚本加载这个接口都是同样一个实例
3、import是异步加载
  • 两者的循环依赖

commomJS

// a.js
exports.done = false;
var b = require('./b.js');
console.log('在 a.js 之中,b.done = ' +  b.done);
exports.done = true;
console.log('a.js 执行完毕');
// b.js
exports.done = false;
var a = require('./a.js');
console.log('在 b.js 之中,a.done = ' + a.done);
exports.done = true;
console.log('b.js 执行完毕');
// main.js
var a = require('./a.js'); // require时就会执行整个文件
/**
 输出:
 在 b.js 之中,a.done = false
 b.js 执行完毕
 */
var b = require('./b.js');
/**
跟a.js类似,b.js导出一个变量后,在第二行就开始加载a.js,发生了循环依赖
然后系统就会去内存对象的exports 中取done变量的值,
可是因为a.js没有执行完,所以只取到刚开始输出的值false。
接着b.js继续执行后面的代码,执行完毕后,再把执行权交还给a.js ,
执行完后面剩下的代码
 */
console.log('在 main.js 之中, a.done= ' + a.done + ' b.done= ' + b.done);
/**
 输出:
 在 main.js 之中, a.done= true b.done= true
 */

ES6 参考1 参考2

6.结合去node化谈谈缓存

不知道

7.谈谈最近做过的项目,你担任的角色和碰到的主要问题

8.会问到一些项目中的技术


9.两道题快速实现

1). 一个单词词组逆序(例如 "I am dog" => "dog am I") 分别用普通 高阶函数 reduce实现

function wordReverse(str) {
    let strArr = str.split(" ");
    let result = "";
    // plan A
    // for (let i = (strArr.length - 1); i >= 0; i--) {
    //     result += (strArr[i] + " ");
    // }
    // return result.substr(0, result.length - 1);

    // plan B
    return strArr.reduce((result, item) => {
        return (" " + item) + result;
    }, result).substr(1)

}

2). 统计一个页面中所有tag的种类和数目 代码尽量精简

var all = document.getElementsByTagName('*');

var tags = [];

for(var i=0;i<all.length;i++){
tags.push(all[i].tagName.toLocaleLowerCase());

}

var res = {};

for(var i=0;i<tags.length;i++){
if(!res[tags[i]]){
res[tags[i]] = 1;

} else {
res[tags[i]]++;

}

}

console.log(res);  

// 方法二
let allTags = Array.from(document.querySelectorAll('*')).map(v => v.tagName)
    let kinds = new Set(allTags).size

    let newObj = {}
    for(let i of allTags) {
      if (!newObj[i]) {
        newObj[i] = 1
      } else {
        newObj[i]++
      }
    }
    console.log(newObj)
    console.log(kinds)

关于项目监控(定时任务 流程控制方面),有没有做,如何做的?


pwa 有无项目经验? 策略 处理量级等

PWA(Progressive web apps,渐进式 Web 应用)运用现代的 Web API 以及传统的渐进式增强策略来创建跨平台 Web 应用程序。通俗来说,PWA应用是指那些使用指定技术和标准模式来开发的web应用,这将同时赋予它们web应用和原生应用的特性

关于组件库,你在组件库中参与什么角色? 如果碰到原组件实现不了的效果会怎么样做?做法细节?

elementui组件库, 三方的swiper组件库
调研组件的功能,显示效果,每个属性包括样式和参数的具体功能,有些手机显示 和 网页显示不一样  

拿input组件举例:
多层封装,导致flex的3个参数失效, 单层封装的话不能实现type为checkbox和radio的placeholder效果

vue的模板中只能有一个跟元素,后来想到用v-if来区分两类  

遇到的问题: 
手机的组件显示和web封装的组件显示有区别,主要是两者的渲染引擎不同,所以通过web封装的组件不能完全适配手机中的显示

let const var的区别

let 变量提升  没有块级作用域的概念, 变量可以重复定义和声明

const let 没有变量提升,存在暂时性死区, 有块级作用域的概念, 变量不能重复定义

generator


call apply bind

改变this的指向的方法
call和apply 仅参数形式的区别, apply必须是数组
bind会返回一个改变this指向的新方法

for in for of

in 是下标(其实是键值, 参考对象遍历中的 for in)
of 是数组的每一项(其实是value值) 

for...in 循环不仅遍历数字键名,还会遍历手动添加的其它键,甚至包括原型链上的键。for...of 则不会这样

let arr = [1, 2, 3]
arr.set = 'world'  // 手动添加的键
Array.prototype.name = 'hello'  // 原型链上的键
 
for(let item in arr) {
  console.log('item', item)
}
 
/*
  item 0
  item 1
  item 2
  item set
  item name
*/
 
for(let value of arr) {
  console.log('value', value)
}
 
/*
  value 1
  value 2
  value 3
*/

无论是 for...in 还是 for...of 都不能遍历出 Symbol 类型的值,遍历 Symbol 类型的值需要用 Object.getOwnPropertySymbols() 方法
{
  let a = Symbol('a')
  let b = Symbol('b')

  let obj = {
    [a]: 'hello',
    [b]: 'world',
    c: 'es6',
    d: 'dom'
  }

  for(let key in obj) {
    console.info(key + ' --> ' + obj[key])
  }

  /*
    c --> es6
    d --> dom
  */

  let objSymbols = Object.getOwnPropertySymbols(obj)
  console.info(objSymbols)    //  [Symbol(a), Symbol(b)]
  objSymbols.forEach(item => {
    console.info(item.toString() + ' --> ' + obj[item])
  })

  /*
    Symbol(a) --> hello
    Symbol(b) --> world
  */

  // Reflect.ownKeys 方法可以返回所有类型的键名,包括常规键名和Symbol键名
  let keyArray = Reflect.ownKeys(obj)
  console.log(keyArray)      //  ["c", "d", Symbol(a), Symbol(b)]
}


两者都可以与break、continue 和 return 配合使用,跳出循环 

forEach 循环无法中途跳出,break 命令或 return 命令都不能奏效

总之,for...in 循环主要是为了遍历对象而生,不适用于遍历数组

for...of 循环可以用来遍历数组、类数组对象,字符串、Set、Map 以及 Generator 对象

对象的遍历方式 遍历的顺序是怎样的

1、Object.keys(obj).forEach()

2、for item in obj  

3、Object.getOwnPropertyNames(obj),返回一个数组,包含对象自身的所有属性(不含Symbol属性,但是包括不可枚举属性)

4、使用Reflect.ownKeys(obj)遍历

返回一个数组,包含对象自身的所有属性,不管属性名是Symbol或字符串,也不管是否可枚举. 

var obj = {'0':'a','1':'b','2':'c'};

Reflect.ownKeys(obj).forEach(function(key){
console.log(key,obj[key]);
});

遍历顺序不保证
数字: 升序
字母: 升序

三栏等分布局(这里会让你选择自己会的布局来讲)

// elementui layout  不行,是固定的
// flex
1. 流体布局
<!DOCTYPE html>
<html lang="en">
<head>
    <style>
	.left {
	    float: left;
	    height: 200px;
	    width: 100px;
	    background-color: red;
	}
	.right {
	    width: 200px;
	    height: 200px;
	    background-color: blue;
	    float: right;
	}
	.main {
	    margin-left: 120px;
	    margin-right: 220px;
	    height: 200px;
	    background-color: green;
	}
    </style>
</head>
<body>
    <div class="container">
        <div class="left"></div>
        <div class="right"></div>
        <div class="main"></div>
    </div>
</body>
</html>
左右模块各自向左右浮动,并设置中间模块的 margin 值使中间模块宽度自适应。

缺点就是主要内容无法最先加载,当页面内容较多时会影响用户体验。

2. BFC 三栏布局
BFC 规则有这样的描述:BFC 区域,不会与浮动元素重叠。因此我们可以利用这一点来实现 3 列布局。

<!DOCTYPE html>
<html lang="en">
<head>
    <style>
	.left {
	    float: left;
	    height: 200px;
	    width: 100px;
	    margin-right: 20px;
	    background-color: red;
	}
	.right {
	    width: 200px;
	    height: 200px;
	    float: right;
	    margin-left: 20px;
	    background-color: blue;
	}	
	.main {
	    height: 200px;
	    overflow: hidden;
	    background-color: green;
	}
    </style>
</head>
<body>
    <div class="container">
        <div class="left"></div>
        <div class="right"></div>
        <div class="main"></div>
    </div>
</body>
</html>
缺点跟方法一类似,主要内容模块无法最先加载,当页面中内容较多时会影响用户体验。因此为了解决这个问题,有了下面要介绍的布局方案双飞翼布局。


3. 双飞翼布局
<!DOCTYPE html>
<html lang="en">
<head>
    <style>
        .content {
  	    float: left;
  	    width: 100%;
        }
        .main {
  	    height: 200px;
  	    margin-left: 110px;
  	    margin-right: 220px;
  	    background-color: green;
        }
	.left {
	    float: left;
	    height: 200px;
	    width: 100px;
	    margin-left: -100%;
	    background-color: red;
	}
	.right {
	    width: 200px;
	    height: 200px;
	    float: right;
	    margin-left: -200px;
	    background-color: blue;
	}	
    </style>
</head>
<body>
    <div class="content">
        <div class="main"></div>
    </div>
    <div class="left"></div>
    <div class="right"></div>
</body>
</html>
利用的是浮动元素 margin 负值的应用,感兴趣的同学可以上网搜搜原理。

主体内容可以优先加载,HTML 代码结构稍微复杂点。

4. 圣杯布局
<!DOCTYPE html>
<html lang="en">
<head>
    <style>
	.container {
	    margin-left: 120px;
	    margin-right: 220px;
	}
	.main {
	    float: left;
	    width: 100%;
	    height: 300px;
	    background-color: red;
	}
	.left {
	    float: left;
	    width: 100px;
	    height: 300px;
	    margin-left: -100%;
	    position: relative;
	    left: -120px;
	    background-color: blue;
	}
	.right {
	    float: left;
	    width: 200px;
	    height: 300px;
	    margin-left: -200px;
	    position: relative;
	    right: -220px;
	    background-color: green;
	}
    </style>
</head>
<body>
    <div class="container">
	<div class="main"></div>
	<div class="left"></div>
	<div class="right"></div>
    </div>
</body>
</html>
跟双飞翼布局很像,有一些细节上的区别,相对于双飞翼布局来说,HTML 结构相对简单,但是样式定义就稍微复杂,也是优先加载内容主体。

5. Flex 布局
<!DOCTYPE html>
<html lang="en">
<head>
    <style>
	.container {
            display: flex;
	}
	.main {
            flex-grow: 1;
	    height: 300px;
	    background-color: red;
	}
	.left {
	    order: -1;
	    flex: 0 1 200px;
	    margin-right: 20px;
	    height: 300px;
	    background-color: blue;
	}
	.right {
	    flex: 0 1 100px;
            margin-left: 20px;
	    height: 300px;
	    background-color: green;
	}
    </style>
</head>
<body>
    <div class="container">
	<div class="main"></div>
	<div class="left"></div>
	<div class="right"></div>
    </div>
</body>
</html>
简单实用,未来的趋势,需要考虑浏览器的兼容性。

6. Table 布局
<!DOCTYPE html>
<html lang="en">
<head>
    <style>
        .container {
	    display: table;
	    width: 100%;
        }
        .left, .main, .right {
	    display: table-cell;
        }
        .left {
	    width: 200px;
	    height: 300px;
	    background-color: red;
        }
        .main {
	    background-color: blue;
        }
        .right {
	    width: 100px;
	    height: 300px;
	    background-color: green;
        }
    </style>
</head>
<body>
    <div class="container">
	<div class="left"></div>
	<div class="main"></div>
	<div class="right"></div>
    </div>
</body>
</html>
缺点:无法设置栏间距

7. 绝对定位布局
<!DOCTYPE html>
<html lang="en">
<head>
    <style>
	.container {
	    position: relative;
	}
	.main {
	    height: 400px;
	    margin: 0 120px;
	    background-color: green;
	}
	.left {
	    position: absolute;
	    width: 100px;
	    height: 300px;
	    left: 0;
	    top: 0;
	    background-color: red;
	}
	.right {
	    position: absolute;
	    width: 100px;
	    height: 300px;
	    background-color: blue;
            right: 0;
	    top: 0;
	}
    </style>
</head>
<body>
    <div class="container">
        <div class="main"></div>
	<div class="left"></div>
	<div class="right"></div>
    </div>
</body>
</html>
简单实用,并且主要内容可以优先加载。

参考

预编译期(没用过)

事件的传递过程(


vue通信方法

typescript用过吗

1、 ts与js的区别  
    ts是严格类型的,即必须指定类型

    ts多出的类型: tuple类型(元组类型)、enum类型(枚举类型)、any类型(任意类型)

    ts中的特殊类型:
       void类型(没有任何类型)表示定义方法没有返回值
       never类型:是其他类型(包括null和undefined)的子类型,代表从不会出现的值这意味着声明never变量只能被never类型所赋值
    lambda(箭头函数)

    ts会做代码静态检查(静态编译) js是动态编译 
    js没有重载概念,ts有可以重载
    ts更利于重构
    ts是js的超集,即你可以在ts中使用原生js语法。
    ts需要静态编译,它提供了强类型与更多面向对象的内容。
    ts最终仍要编译为弱类型,基于对象的原生的js,再运行。故ts相较java/C#这样天生面向对象语言是有区别和局限的
    ts是由微软牵头主导的,其语法风格与概念主要来自C#

    有了强类型,就有了更好的IDE支持,包括语法提示和编译时错误信息。有了接口和继承,代码就更易读易维护。这些都是大型复杂前端工程急需的特性。

    语言层面:JavaScript和TypeScript都是ECMAScript(ECMA-262)的具体实现。执行环境层面:浏览器引擎和Node.js都能够直接运行JavaScript,但无法直接运行TypeScript。时序层面:TypeScript被真正执行前,会通过编译转换生成JavaScript,之后才能被解释执行。厂商层面:JavaScript由Netscape率先推出,现在主要由各大浏览器厂商实现。而TypeScript is a trademark of Microsoft Corporation,目前由微软进行设计和维护。 TypeScript是ECMAScript 2015的语法超集,是JavaScript的语法糖。JavaScript程序可以直接移植到TypeScript,TypeScript需要编译(语法转换)生成JavaScript才能被浏览器执行

    js声明数组的方式:
    let a = [1,2,3]
    ts声明数组的方式:
    let a: number[] = [1,2,3]
    let b: Array<number> = [1,2,3]
       
2、typescript 常用哪些? 泛型有了解吗? Pick 和 Omit 有用过吗
泛型的使用
// 创建一个泛型类
class Queue<T> {
  private data: T[] = [];
  push = (item: T) => this.data.push(item);
  pop = (): T | undefined => this.data.shift();
}

// 简单的使用
const queue = new Queue<number>();
queue.push(0);
queue.push('1'); // Error:不能推入一个 `string`,只有 number 类型被允许
3、断言可以举个例子吗  
类似 强制类型转换,具体没有深入理解过

发送文本图片和语音是怎么实现的


是否支持混合发送(文本+图片)

可以介绍下 echarts 吗(没用过)

ECharts 是一个使用 JavaScript 实现的开源可视化库,涵盖各行业图表,满足各种需求

1、echarts 是怎么设计的有了解吗
没有

antd 表单用的多吗

没有

介绍下 Vue (知道多少说多少)

渐进式前端框架, 是MVVM模式的
生命周期
组件封装

块级元素/行内元素/行内块的区别


行内元素可以设置 padding 和 margin 吗

讲一下 cookie、sessionStorage、localStorage 的区别

介绍下 BFC 外边距合并 BFS清除浮动

ES6 新特性有了解哪些

let const

箭头函数和普通函数有什么区别

箭头函数 本身没有this, 没有arguments

讲一下 promise

是js中异步编程的接口 
new Promise((resolve, reject) => {

})

三种状态: pending sucess rejected
状态改变就是确定的
通过.then 来执行状态改变后的一些操作

关于 promise 的链式调用知道是怎么实现的吗

介绍下原型链

所有对象都有 __proto__
只有函数有 prototype

层叠上下文可以介绍下吗

可以通过position来实现

css 的 animation 有用过吗 translate transition transform

从输入 url 到页面渲染发生了什么, 这个过程哪些部分可以优化

css 和 js 会去阻塞页面渲染吗

HTML、css、js 是怎么把他做成一个服务的,让其他人也可以访问

静态资源怎么做缓存

Vue 的响应式是怎么监听数据改变的?数组是怎么实现响应式的?为什么不直接去更改数组原型上的方法?改了会有什么问题

vue2 Object.defineProperty()
vue3 proxy
vue2中的数组是另外处理的

Vue 的 $store 是怎么绑定到 Vue 实例上的?

给 Vue 全局绑定一个变量有什么方式? 为什么 $store 不直接绑定到原型

聊一下浏览器同步和异步?为什么要有异步?异步是怎么实现的

对模块化有没有封装过什么组件

有

有发过 npm 包吗?在使用别人的 npm 包的时候,知道是什么时候决定升级一个大版本,这个版本号有没有什么机制

pageage.json 中版本号 ^ 和 ~ 有什么区别吗?

当我们用 CommonJS 规范去 require 加载一个包,require 的加载机制大概是怎么样的

会先去 require.cache中找有没有加载过,(这个寻找可能还回去父类或者子类中寻找),若没有再加载

构建和打包是怎么做的?有配过 webpack 吗

entry 打包的入口 (可以有多个打包入口)
output 打包的最终内容

除了自己的项目平时也会做一些项目练手吗

给滚动容器绑定一个事件,随着滚动会去频繁触发事件,怎么优化

节流的方式,间隔一定时间执行一次

防抖和节流有什么区别 手写debounce

都可以通过setTimeout实现,防抖是最后一次触发事件后的delay时间执行回调函数, 节流是间隔一定时间执行一次

js 里有没有什么方法禁止一个对象的属性被删除

属性描述符

并行发送异步请求怎么做

HTML 的 meta title 是前端做的还是后端做的?HTML 模板是怎么渲染出来的

node 端有了解多少

React hooks 有用到吗

有没有用过百分比布局的情况,padding-top: 50%; 是相对于与谁

怎么实现一个自适应正方形(宽度不确定)

css 选择器有哪些?优先级说一下

id class 标签名

代码回滚怎么做?有哪些方式有什么区别

git checkout XXX
git reset XXX 

cookie 中 httponly 有什么用

项目中有遇到 eslint 吗

没有用

算法题 数组去重,数组里面存的是数字或字符串

说一下 http 协议, https HTTP2

http请求过程

http 协议的无状态是指什么

TCP 三次握手四次挥手

vue 异步刷新如何实现

nextTick() 
事件循环

宏任务和微任务

闭包、垃圾回收机制

手撕单链表的实现

堆和栈

React 生命周期/vue生命周期

css 盒子模型

高阶组件

JS 基本数据类型

7种

网站性能优化(提示从 html、css、js等方面说)

隐藏盒子,display:none 和 visibity:hidden 的区别

dispaly: 非继承属性,设置为none,子组件都会隐藏
none  visible
none时不占用空间,只在文档对象中, none -> visible 回流


visibility:继承性属性, 设置为hidden,但子组件设置为visible,子组件自然可以显示,子元素visibility不存在则子元素不可见
hidden visible
hidden是占据空间,只是不显示  hidden -> visible 重绘
该元素虽然被隐藏了,但仍然会影响布局

v-if 和 v-show的区别

v-if指令与v-show指令都可以根据值动态控制DOM元素显示隐藏,v-if和v-show属于Vue的内部常用的指令,指令的职责是当表达式的值改变时把某些特殊的行为应用到DOM上。

区别:

- 实现方式: v-if是动态的向DOM树内添加或者删除DOM元素,v-show是通过设置DOM元素的display样式属性控制显隐。

- 编译过程: v-if切换有一个局部编译卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件,v-show只是简单的基于CSS切换。

- 编译条件: v-if是惰性的,如果初始条件为假,则什么也不做,只有在条件第一次变为真时才开始局部编译, v-show是在任何条件下都被编译,然后被缓存,而且DOM元素保留。

- 性能消耗: v-if有更高的切换消耗,v-show有更高的初始渲染消耗。

- 使用场景: v-if适合条件不太可能改变的情况,v-show适合条件频繁切换的情况。

如何让一个盒子垂直居中 垂直居中的实现方法,可以有几种方式


如何让一个盒子水平居中,行内块元素内的文字水平居中


如何让一个正方形的盒子变成圆,追问在不知道高度的情况下怎么实现

borderRadius

如何让动画更流畅

React 单向数据流和Vue双向数据流

框架出现的原因,与不用框架的区别

ajax 跨域的解决方式

算法(判断两棵二叉树是否相等)

有了解过 React Hooks 吗

算法题:两个叶子结点,找最短的公共父节点

vue和jQuery的区别

vue中在data里面定义了对象,比如说student,但是没定义对象的属性,当拿到后端的数据之后,用obj.age,obj.name渲染在页面中时,数据不会发生改变,是为什么? 面试官说是因为实现响应式必须要先声明,才能去注册到响应式的里面。

vue2中不能, 劫持key 
vue3中可以

flex布局说一下。说了flex-grow,flex-shrink等属性,垂直居中属性

flex-grow和flex-shrink是怎么控制放大缩小比例的?

比如说有三个需要布局,flex-shrink分别是1,1,2。当他们的总宽度超过了父级宽度100px时,按照什么原则缩小? 面试官说会不一定完全按照比例缩小,也会考虑他们自身的宽度。

不同浏览器会有不同的缩小方式

对js的EventLoop(事件循环)机制了解吗?

同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入Event Table并注册函数。
当指定的事情完成时,Event Table会将这个函数移入Event Queue。
主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。
上述过程会不断重复,也就是常说的Event Loop(事件循环)。

setTimeout和promise,比如说一段代码 Promise.then(console.log(1); setTimeout(console.log(2),0) ); 打印顺序。1 2

new一个对象发生了什么?

this指向说一下你自己的理解

改变this指向的方法有哪些?

call apply bind
直接把方法拿出来用

如果让你自己实现一个bind方法,你会怎么实现? 面试官引导了好久,是我太菜了。 bind函数内部也使用了apply或call。

算法题:获取一个链表的倒数第k个结点。 双指针

// 拼多多

js的defer和async有何区别

尾递归

domcontentloaded,load区别

怎样减少重绘重排

purecomponent是什么

hooks了解过吗

跨域,怎么跨域

轮询

优化前端性能方法

react redux和useContext

react class component和function component

HTML5的新特性

css3伪类

柯里化

算法写一个动画,一个三角形从透明到实体,持续一秒,然后匀速前进五秒,然后变大持续两秒

算法将中文数字转换成阿拉伯数字表示: 如 五千零一-->5001 五千一 5100,需考虑到亿的单位

算法两个栈模拟一个队列,实现删除插入等功能

#酷家乐 一面(电话面,比较注重基础):

项目所使用到的技术栈

js的箭头函数和普通函数区别


let和const区别

三栏布局

讲一讲flex


js的事件循环


谈谈对promise的理解

instanceof和typeof 区别

深拷贝和浅拷贝

基本数据类型的复制,是按值传递的
引用数据类型的复制,是按引用(地址指针)传值
深拷贝和浅拷贝都只针对引用数据类型,浅拷贝会对对象逐个成员依次拷贝,但只复制内存地址,而不复制对象本身,新旧对象成员还是共享同一内存;深拷贝会另外创建一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。
区别:浅拷贝只复制对象的第一层属性,而深拷贝会对对象的属性进行递归复制。

var fruits = ["Banana", "Orange", "Apple", "Mango"];
var obj=fruits;
obj[1]="water";
console.log("fruits",fruits);
console.log("obj",obj)

var num1 = 123;
var num2 = 123;
var num3 = num1;
num1 === num2; // true
num1 === num3; // true
num1 = 456;
num1 === num2; // false
num1 === num3; // false

var arr1 = [1, 2, 3];
var arr2 = [1, 2, 3];
var arr3 = arr1;
arr1 === arr2; // false
arr1 === arr3; // true
arr1 = [1, 2, 3];
arr1 === arr2; // false
arr1 === arr3; // false

浅拷贝的实现: 对对象的属性中所有引用类型的值,遍历到是基本类型的值为止
1、Object.assign()
2、Array.prototype.concat()和Array.prototype.slice()  
Array的slice和contact方法都不会修改原数组,而是会返回一个对原数组进行浅拷贝的新数组。这两种方法同Object.assign()一样,都是对第一层属性依次拷贝,如果第一层的属性是基本数据类型,就拷贝值;如果是引用数据类型,就拷贝内存地址

深拷贝的实现:
1、JSON.parse(JSON.stringify())  缺点: JSON.stringify()无法正确处理函数
原理:用JSON.stringify()将对象转成字符串,再用JSON.parse()把字符串解析成对象。
2、递归遍历
3、函数库
也可以使用一些函数库,比如函数库lodash,也有提供_.cloneDeep用来做深拷贝
var _ = require('lodash');
var obj1 = {
  a: 1,
  b: { f: { g: 1 } },
  c: [1, 2, 3]
};
var obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);
// false

手写浅拷贝

function shallowCopy(fruits) {
let obj = Array.isArray(fruits) ? [] : {};
for (let i in fruits) {
    obj[i] = fruits[i];
}
return obj;
}
var fruits = ["Banana", "Orange", "Apple", ["Mango", "blue"]];
var obj = shallowCopy(fruits);
obj[2] = "water";
obj[3][0] = "water";
console.log("fruits", fruits);
console.log("obj", obj);

计算机网络模型

参考


TCP的三次握手和四次分手

参考 参考2

TCP 连接使用三次握手的首要原因 —— 为了阻止历史的重复连接初始化造成的混乱问题,防止使用 TCP 协议通信的双方建立了错误的连接

我们在这篇文章中讨论了为什么 TCP 建立连接需要经过三次握手,在具体分析这个问题之前,我们首先重新思考了 TCP 连接究竟是什么,RFC 793 - Transmission Control Protocol - IETF Tools 对 TCP 连接有着非常清楚的定义 —— 用于保证可靠性和流控制机制的数据,包括 Socket、序列号以及窗口大小。

TCP 建立连接时通过三次握手可以有效地避免历史错误连接的建立,减少通信双方不必要的资源消耗,三次握手能够帮助通信双方获取初始化序列号,它们能够保证数据包传输的不重不丢,还能保证它们的传输顺序,不会因为网络传输的问题发生混乱,到这里不使用『两次握手』和『四次握手』的原因已经非常清楚了:

『两次握手』:无法避免历史错误连接的初始化,浪费接收方的资源;
『四次握手』:TCP 协议的设计可以让我们同时传递 ACK 和 SYN 两个控制信息,减少了通信次数,所以不需要使用更多的通信次数传输相同的信息;

请求url到渲染过程

参考 参考2 参考3

BFC

参考

BFC 即 Block Formatting Contexts (块级格式化上下文),它属于上述定位方案的普通流。

具有 BFC 特性的元素可以看作是隔离了的独立容器,容器里面的元素不会在布局上影响到外面的元素,并且 BFC 具有普通容器所没有的一些特性。

通俗一点来讲,可以把 BFC 理解为一个封闭的大箱子,箱子内部的元素无论如何翻江倒海,都不会影响到外部。

三、触发 BFC
只要元素满足下面任一条件即可触发 BFC 特性:

body 根元素
浮动元素:float 除 none 以外的值
绝对定位元素:position (absolute、fixed)
display 为 inline-block、table-cells、flex
overflow 除了 visible 以外的值 (hidden、auto、scroll)

BFC的特性

同一个 BFC 下外边距会发生折叠
BFC 可以阻止元素被浮动元素覆盖
BFC 可以包含浮动的元素(清除浮动)
1.内部的Box会在垂直方向上一个接一个的放置。
2.垂直方向上的距离由margin决定
3.bfc的区域不会与float的元素区域重叠。
4.计算bfc的高度时,浮动元素也参与计算
5.bfc就是页面上的一个独立容器,容器里面的子元素不会影响外面元素。

position有几种,详细讲一下

static relative absolute fixed sticky

static是html的默认属性,如果省略position属性,浏览器就认为该元素是static定位
static定位所导致的元素位置,是浏览器自主决定的,所以这时top、bottom、left、right这四个属性无效

relative表示,相对于默认位置(即static时的位置)进行偏移,即定位基点是元素的默认位置

absolute表示,相对于上级元素(一般是父元素)进行偏移,即定位基点是父元素
它有一个重要的限制条件:定位基点(一般是父元素)不能是static定位,否则定位基点就会变成整个网页的根元素html
absolute定位的元素会被"正常页面流"忽略,即在"正常页面流"中,该元素所占空间为零,周边元素不受影响

fixed表示,相对于视口(viewport,浏览器窗口)进行偏移,即定位基点是浏览器窗口。这会导致元素的位置不随页面滚动而变化,好像固定在网页上一样。

sticky跟前面四个属性值都不一样,它会产生动态效果,很像relative和fixed的结合:一些时候是relative定位(定位基点是自身默认位置),另一些时候自动变成fixed定位(定位基点是视口)
sticky生效的前提是,必须搭配top、bottom、left、right这四个属性一起使用,不能省略,否则等同于relative定位,不产生"动态固定"的效果。原因是这四个属性用来定义"偏移距离",浏览器把它当作sticky的生效门槛。
它的具体规则是,当页面滚动,父元素开始脱离视口时(即部分不可见),只要与sticky元素的距离达到生效门槛,relative定位自动切换为fixed定位;等到父元素完全脱离视口时(即完全不可见),fixed定位自动切换回relative定位

absolute和fixed会脱离文档流,即在正常页面的文档流中,元素所占的空间为0

fixed失效的场景:
在Chrome(Blink内核)中,position:fixed在:
祖先元素为transform 属性值不为 none 的元素
perspective 值不为 none 的元素
will-change 中指定了任意 CSS
filter 不为 none
设置了 transform-style: preserve-3d 的元素
情况下,定位容器由视口改为该祖先。

二面(微信视频面): 自我介绍

详细问项目相关

react hooks

问最快什么时候能来就职

二面主要就是追着项目问一些细节问题

hr面:

谈薪资
谈工作作息

#字节跳动: 一面(1h+):

问项目中所使用的api,如何实现的

react组件之间如何通信

react redux

高阶组件

HTTP2的优点

非对称加密与对称加密

https与http的不同,为啥安全性更高,ssl是什么

参考

SSL是一种加密协议
SSL协议大致就和上一节非对称加密的性质一样,握手的过程中主要也是为了交换秘钥,然后再通讯过程中使用对称加密进行通讯

websocket是什么,与轮询有什么不同

一道代码题看输出,考察var变量提升问题

手写promise.all

算法题 写一个链表相关代码题,找出两个链表的公共结点

requestAnimationFrame

二面(1h+): 自我介绍

问项目的一些细节

flex和grid布局

重绘和重排(回流),如何避免

重排和回流其实是一回事

浏览器渲染 浏览器在渲染页面的时候,大致是以下几个步骤:

  • 解析html生成DOM树,解析css,生成CSSOM树,将DOM树和CSSOM树结合,生成渲染树;
  • 根据渲染树,浏览器可以计算出网页中有哪些节点,各节点的CSS以及从属关系 - 回流
  • 根据渲染树以及回流得到的节点信息,计算出每个节点在屏幕中的位置 - 重绘
  • 最后将得到的节点位置信息交给浏览器的图形处理程序,让浏览器中显示页面
    参考 参考2
DOM的变化影响到了元素的几何属性(宽高),浏览器重新计算元素的几何属性,其他元素的几何属性和位置也会受到影响,浏览器需要重新构造渲染树,这个过程称为重排,浏览器将受到影响的部分重新绘制到屏幕上的过程称为重绘。

引起重排的原因有

添加或者删除可见的DOM元素,
元素位置、尺寸、内容改变,
浏览器页面初始化,
浏览器窗口尺寸改变,重排一定重绘,重绘不一定重排,

减少重绘和重排的方法:

不在布局信息改变时做DOM查询
使用cssText或者className一次性改变属性
使用fragment
对于多次重排的元素,如动画,使用绝对定位脱离文档流,让他的改变不影响到其他元素

animation动画

算法一串代码看输出,考察js事件循环的微任务和宏任务输出顺序

算法写css、HTML,完成一个弹窗,弹窗自底部往上弹出,弹窗垂直水平居中,长宽视内容的长宽而定,外部为灰色蒙版

算法

<ul>
<li></li>
</ul>
往ul里面插入10000个li,如何实现

document.fragment有什么好处

算法

实现一个class类Scheduler,完善一个add(promiseCreator)方法,实现一个带并发限制的promise异步调度器,保证同时运行的任务最多有两个
如:add(1000,'1')
add(200,'2')
add(300,'3')
add(400,'4')
输出:2 3 1 4

三面(1h+): 自我介绍

问其中一个项目如何实现,所使用的技术栈,就其中一个功能的思想进行讨论,是否可以有其他更优化的方式

算法题:变身程序员

将产品经理转为程序员以解决开发时间长的问题,每分钟,任何与程序员(在4个正方向上)相邻的产品经理都会变成程序员,
返回直到单元格中没有产品经理为止所必须经过的最小分钟数,如果不可能返回-1
eg:[[1,2,1],[1,1,0],[0,1,1]]   输出3
[[0,2],[1,0]]  输出-1
采用bfs思想

进程与线程

计算机网络结构

TCP与udp

网络的七层模型,各自有什么作用,为什么要进行分层

个人在前端有什么展望吗?或者说前端的个人兴趣在哪?有没有什么规划?有没有对前端方向感有要求(工程化、中后台...)

自己平常业余生活会有一些自己的总结吗

会,一般会写一些个人笔记

面试过程中面试官会说一些他在实际开发中遇到的一些可以优化的地方,也会给一些建议

反问

几轮面试(3 + 1)
是否有转正机会(有)
技术栈(新项目强制使用 React,老项目 vue、jQuery、纯 js)


面试表现
面试反馈时间(五一前,第二天 HR 联系了)
转正机会(看表现,毕业转正)
假如过来实习一般是做什么(mentor 带,酷家乐商业平台:管理系统,商家后台,中后台)  

看你专业挺少接触前端的,你是怎么想到要走前端的?

⚠️ **GitHub.com Fallback** ⚠️