2021前端面试押题

521 阅读15分钟

HTML

  1. 必考:你是如何理解 HTML 语义化的?
    i.html语义化让页面的内容结构化,结构更清晰,便于对浏览器、搜索引擎解析;
    ii.即使在没有样式CSS情况下也以一种文档格式显示,并且是容易阅读的;
    iii.搜索引擎的爬虫也依赖于HTML标记来确定上下文和各个关键字的权重,利于SEO;
    iv.使阅读源代码的人对网站更容易将网站分块,便于阅读维护理解。

  2. meta viewport 是做什么用的,怎么写?
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1">
    meta viewport 是用来做移动端适配的,让网页可以在移动端上得到很好的呈现。

    meta 标签里面有两个属性,name 和 content 。name 里面写 viewport ,content 里面写 width = device-width ,宽度等于设备宽度;initial-scale = 1 ,页面的初始缩放比例为 1 ; minimum-scale = 1 , maximum-scale = 1 ,用户能够放大和缩小的比例都为1,这是用来禁止用户缩放的;user-scalable = no ,也是用来禁止用户缩放的。之所以重复禁止用户缩放,是因为在有些浏览器上其中一个可能会失效。

  3. 你用过哪些 HTML 5 标签?
    header,section,footer,aside,nav,main,article,canvas,video,audio

  4. H5 是什么?
    H5是指那些运行在微信APP里面的网页链接或二维码,它可以用来收集信息,做品牌推广宣传,活动邀请等。

  5. REM是什么?
    根据设备的分辨率,等比缩放布局
    公式 设备分辨率/750*100(在分辨率宽度为750的设备下,1rem=100px)

CSS

  1. 必考:两种盒模型分别说一下。
    IE盒模型和标准盒模型,IE盒模型的宽高包含content+padding+border

  2. 必考:如何垂直居中?
    如果 .parent 的 height 不写,只需要 padding: 10px 0; 就能将 .child 垂直居中;
    如果 .parent 的 height 写死了,就很难把 .child 居中,以下是垂直居中的方法。\

    1.table自带功能
    2.100% 高度的 afrer before 加上 inline block
    这个方法还有一个优化版本
    3.div 装成 table
    4.margin-top -50%
    5.translate -50%
    6.absolute margin auto
    7.flex

  3. 必考:flex 怎么用,常用属性有哪些?\

    1. flex-wrap 是否换号
    2. flex-basis 宽度
    3. flex-grow 扩展
    4. flex-shrink 收缩
    5. align-items 垂直对其方式;
    6. justify-content 水平对其方式
    7. align-self 子元素水平对其方式
    8. justify-self 子元素垂直对其方式
    9. flex-direction 内容的主轴方向
  4. 必考:BFC 是什么?
    块级格式化上下文[MDN]

    1. 浮动元素(元素的 float 不是 none)
    2. 绝对定位元素(元素的 position 为 absolute 或 fixed)
    3. 行内块元素
    4. overflow 值不为 visible 的块元素
    5. 弹性元素(display为 flex 或 inline-flex元素的直接子元素)
  5. CSS 选择器优先级
    内联>id>(class、属性、伪类)>(元素、关系、伪元素)>统配符

    1.  越具体优先级越高
    1.  同样优先级写在后面的覆盖写在前面的
    1.  !important 优先级最高,但是要少用
    
  6. 清除浮动说一下
    背代码

     .clearfix:after{
         content: '';
         display: block; /*或者 table*/
         clear: both;
         height:0;
     }
     .clearfix{
         zoom: 1; /* IE 兼容*/
     }
    

原生 JS

  1. 必考:ES 6 语法知道哪些,分别怎么用?
    举例法
    let const 箭头函数 Promise 对象解构 展开操作符 默认参数 import export,见方方整理的列表

  2. 必考 Promise、Promise.all、Promise.race 分别怎么用?

    1. 背代码 Promise 用法

       function fn(){
           return new Promise((resolve, reject)=>{
               成功时调用 resolve(数据)
               失败时调用 reject(错误)
           })
       }
       fn().then(success, fail).then(success2, fail2)
      
    2. 背代码 Promise.all 用法

       Promise.all([promise1, promise2]).then(success1, fail1)
      

      promise1和promise2都成功才会调用success1

    3. 背代码 Promise.race 用法

       Promise.race([promise1, promise2]).then(success1, fail1)
      

      promise1和promise2只要有一个成功就会调用success1;
      promise1和promise2只要有一个失败就会调用fail1;
      总之,谁第一个成功或失败,就认为是race的成功或失败。

  3. 必考:手写函数防抖和函数节流

    1. 背代码

       // 节流(一段时间执行一次之后,就不执行第二次)
       function throttle(fn, delay){
           let canUse = true
           return function(){
               if(canUse){
                   fn.apply(this, arguments)
                   canUse = false
                   setTimeout(()=>canUse = true, delay)
               }
           }
       }
       
       const throttled = throttle(()=>console.log('hi'))
       throttled()
       throttled()
      

      注意,有些地方认为节流函数不是立刻执行的,而是在冷却时间末尾执行的(相当于施法有吟唱时间),那样说也是对的。

    2. 背代码

       // 防抖(一段时间会等,然后带着一起做了)
       function debounce(fn, delay){
           let timerId = null
           return function(){
               const context = this
               if(timerId){window.clearTimeout(timerId)}
               timerId = setTimeout(()=>{
                   fn.apply(context, arguments)
                   timerId = null
               },delay)
           }
       }
       const debounced = debounce(()=>console.log('hi'))
       debounced()
       debounced()
      
  4. 必考:手写AJAX

    ```
     var request = new XMLHttpRequest()
     request.open('GET', '/a/b/c?name=ff', true);
     request.onreadystatechange = function () {
       if(request.readyState === 4 && request.status === 200) {
         console.log(request.responseText);
       }};
     request.send();
    ```
    
  5. 必考:这段代码里的 this 是什么?

    1.  fn()\
        this => window/global(严格模式下this=>undefined)
    1.  obj.fn()\
        this => obj
    1.  fn.call(xx)\
        this => xx
    1.  fn.apply(xx)\
        this => xx
    1.  fn.bind(xx)\
        this => xx
    1.  new Fn()\
        this => 新的对象
    1.  fn = ()=> {}\
        this => 外面的 this
    
  6. 必考:闭包/立即执行函数是什么?
    闭包 有权访问另一个函数的作用域的函数都是闭包
    立即执行函数 创建一个独立的作用域,这个作用域里面的变量,外面访问不到(即避免「变量污染」)。

  7. 必考:什么是 JSONP,什么是 CORS,什么是跨域?
    Web 开发中,跨域请求是个经常碰到的问题,因为涉及到网站安全,所以浏览器是拒绝跨域请求的。通常解决跨域会采用 JSONP 和 CORS

    JSONP 在 HTML 中动态生成 script 标签,在 “src” 中引入请求的 URL + 回调函数。
    CORS 增加一些 HTTP 头,让服务器能声明允许的访问来源。

  8. 常考:async/await 怎么用,如何捕获异常?

    (async function(){
        try{
            await fetchData();
            console.log('123')
        }catch(e){
            console.log(e);
        }
    })()
    
  1. 常考:如何实现深拷贝?
    背代码,要点:

    1. 递归
    2. 判断类型
    3. 检查环(也叫循环引用)
    4. 需要忽略原型
  2. 常考:如何用正则实现 trim()?
    背代码

    String.prototype.trim = function(){
        return this.replace(/^\s+|\s+$/g, '')
    }
    
  3. 常考:不用 class 如何实现继承?用 class 又如何实现?

    1. 背代码,不用 class 这样实现

       function Animal(color){
           this.color = color
       }
       Animal.prototype.move = function(){} // 动物可以动
       function Dog(color, name){
           Animal.call(this, color) // 或者 Animal.apply(this, arguments)
           this.name = name
       }
       // 下面三行实现 Dog.prototype.__proto__ = Animal.prototype
       function temp(){}
       temp.prototype = Animal.prototype
       Dog.prototype = new temp()
      
       Dog.prototype.constuctor = Dog // 这行看不懂就算了,面试官也不问
       Dog.prototype.say = function(){ console.log('汪')}
      
       var dog = new Dog('黄色','阿黄')
      
    2. 背代码,用 class 就简单了

       class Animal{
           constructor(color){
               this.color = color
           }
           move(){}
       }
       class Dog extends Animal{
           constructor(color, name){
               super(color)
               this.name = name
           }
           say(){}
       }
      
  4. 常考:如何实现数组去重?

    function unique(arr){
      let hash = {};
      let result = [];
      for(let i=0;i<arr.length;i++){
         if(!hash[arr[i]]){
           hash[arr[i]] = true;
           result.push(arr[i]);
         }
      }
      return result;
    }
    
  5. 放弃:== 相关题目(反着答)
    不要背,记不住,太复杂且没有规律

  6. 送命题:手写一个 Promise

function Promise(extcutor){
    var self = this; 
    this.status = 'pending';
    this.value = undefined;
    this.reason = undefined;
    this.onResolvedCallbacks = [];
    this.onRejectedCallbacks = [];
  
  
    function resolve(){
        self.value = arguments;
        self.status = 'resolved';
        for(var i=0;i<self.onResolvedCallbacks.length;i++){
              var fn = self.onResolvedCallbacks[i];
              fn&&fn.apply(null,arguments);
        }
        self.onResolvedCallbacks = [];
    }
  
    function reject(){
        self.reason = arguments;
        self.status = 'rejected';
        for(var i=0;i<self.onRejectedCallbacks.length;i++){
              var fn = self.onRejectedCallbacks[i];
              fn&&fn.apply(null,arguments);
        }
        self.onRejectedCallbacks = [];
    }
  
    try{
      extcutor(resolve,reject);
    }catch(e){
      reject(e);
    }
}

Promise.prototype.then = function(onFulfilled,onRejected){
  if(this.status==='pending'){
      this.onResolvedCallbacks.push(onFulfilled);
      this.onRejectedCallbacks.push(onRejected);
  }
  if(this.status==='resolved'){
    onFulfilled&&onFulfilled.apply(null,this.value);
  }
   if(this.status==='rejected'){
    onRejected&&onRejected.apply(null,this.reason);
  }
}
  1. 手写一个 EventBus
function EventBus(){
  this.event = {};
}

EventBus.prototype.emit = function(){
    var name = arguments[0];
    var rset = Array.apply(null,arguments).slice(1);
    var callbacks = this.event[name];
    for(var i=0;i<callbacks.length;i++){
         callbacks[i]&&callbacks[i].apply(null,rset);
    }
}

EventBus.prototype.on = function(name,func){
    if(this.event[name]){
        this.event[name].push(func);
    }else{
      this.event[name] = [func]
    }
}

var bus = new EventBus();

DOM

  1. 必考:事件委托

    1. 错误版(但是可能能过)

       ul.addEventListener('click', function(e){
           if(e.target.tagName.toLowerCase() === 'li'){
               fn() // 执行某个函数
           }
       })
      

      bug 在于,如果用户点击的是 li 里面的 span,就没法触发 fn,这显然不对。

    2. 高级版

       function delegate(element, eventType, selector, fn) {
           element.addEventListener(eventType, e => {
             let el = e.target
             while (!el.matches(selector)) {
               if (element === el) {
                 el = null
                 break
               }
               el = el.parentNode
             }
             el && fn.call(el, e, el)
           })
           return element
         }
      

      思路是点击 span 后,递归遍历 span 的祖先元素看其中有没有 ul 里面的 li。

  2. 曾考:用 mouse 事件写一个可拖曳的 div

    var box = document.getElementById('box');
    var draging = false;
    var offset = {}
    
    box.onmousedown = function(event){
      draging = true;
      offset.clientX = event.clientX;
      offset.clientY = event.clientY;
    }
    document.onmousemove = function(event){
      if(!draging)return;
      var deltaX = event.clientX-offset.clientX;
      var deltaY = event.clientY-offset.clientY;
      box.style.left = box.offsetLeft+deltaX+'px';
      box.style.top = box.offsetTop+deltaY+'px';
      offset.clientX = event.clientX;
      offset.clientY = event.clientY;
    }
    document.onmouseup = function(){
      draging = false;
    }
    

HTTP

  1. 必考:HTTP 状态码知道哪些?分别什么意思?

    • 2xx 表示成功
    • 3xx 表示需要进一步操作
    • 4xx 表示浏览器方面出错
    • 5xx 表示服务器方面出错
    • 完整参考 www.runoob.com/http/http-s…
  2. 大公司必考:HTTP 缓存有哪几种?

    • 需要详细的了解 ETag、CacheControl、Expires 的异同

    • 参考 imweb.io/topic/5795d…

    • 答题要点:

      1. ETag 是通过对比浏览器和服务器资源的特征值(如MD5)来决定是否要发送文件内容,如果一样就只发送 304(not modified)
      2. Expires 是设置过期时间(绝对时间),但是如果用户的本地时间错乱了,可能会有问题
      3. CacheControl: max-age=3600 是设置过期时长(相对时间),跟本地时间无关。
  3. 必考:GET 和 POST 的区别

    1. 错解,但是能过面试

      • GET在浏览器回退时是无害的,而POST会再次提交请求。
      • GET产生的URL地址可以被加入收藏栏,而POST不可以。
      • GET请求会被浏览器主动cache,而POST不会,除非手动设置。
      • GET请求只能进行url编码,而POST支持多种编码方式。
      • GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
      • GET请求在URL中传送的参数是有长度限制的,而POST么有。
      • 对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
      • GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
      • GET参数通过URL传递,POST放在Request body中。
    2. 正解
      就一个区别:语义——GET 用于获取资源,POST 用于提交资源。

    3. 想装逼请参考 zhuanlan.zhihu.com/p/22536382

  4. Cookie V.S. LocalStorage V.S. SessionStorage V.S. Session

    • Cookie V.S. LocalStorage

      1. 主要区别是 Cookie 会被发送到服务器,而 LocalStorage 不会
      2. Cookie 一般最大 4k,LocalStorage 可以用 5Mb 甚至 10Mb(各浏览器不同)
    • LocalStorage V.S. SessionStorage

      1. LocalStorage 一般不会自动过期(除非用户手动清除),而 SessionStorage 在回话结束时过期(如关闭浏览器)
    • Cookie V.S. Session

      1. Cookie 存在浏览器的文件里,Session 存在服务器的文件里
      2. Session 是基于 Cookie 实现的,具体做法就是把 SessionID 存在 Cookie 里

框架 Vue

  1. 必考:watch 和 computed 和 methods 区别是什么?

    1. 思路:先翻译单词,再阐述作用,最后强行找不同。

    2. 要点:

      1. computed 和 methods 相比,最大区别是 computed 有缓存:如果 computed 属性依赖的属性没有变化,那么 computed 属性就不会重新计算。methods 则是看到一次计算一次。
      2. watch 和 computed 相比,computed 是计算出一个属性(废话),而 watch 则可能是做别的事情(如上报数据)
  2. 必考:Vue 有哪些生命周期钩子函数?分别有什么用?

    1. 钩子在文档全都有,看红色的字。
    2. 把名字翻译一遍就是满分
    3. 要特别说明哪个钩子里请求数据,答案是 mounted
  3. 必考:Vue 如何实现组件间通信?

    1. 父子组件:使用 v-on 通过事件通信
    2. 爷孙组件:使用两次 v-on 通过爷爷爸爸通信,爸爸儿子通信实现爷孙通信
    3. 任意组件:使用 eventBus = new Vue() 来通信,eventBus.oneventBus.on 和 eventBus.emit 是主要API
    4. 任意组件:使用 Vuex 通信
  4. 必考:Vue 数据响应式怎么做到的?
    1. 使用 Object.defineProperty 把这些属性全部转为 getter/setter
    1. Vue 不能检测到对象属性的添加或删除,解决方法是手动调用 Vue.set 或者 this.$set

  5. 必考:Vue.set 是做什么用的?
    见上一题

  6. Vuex 你怎么用的?

    1. 背下文档第一句:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式
    2. 说出核心概念的名字和作用:State/Getter/Mutation/Action/Module
  7. VueRouter 你怎么用的?

    1. 背下文档第一句:Vue Router 是 Vue.js 官方的路由管理器。

    2. 说出核心概念的名字和作用:History 模式/导航守卫/路由懒加载

    3. 说出常用 API:router-link/router-view/this.router.push/this.router.push/this.router.replace/this.$route.params

       this.$router.push('/user-admin')
       this.$route.params
      
  8. 路由守卫是什么?
    看官方文档的例子,背里面的关键的话

框架 React

  1. 必考:受控组件 V.S. 非受控组件

     <FInput value={x} onChange={fn}/> 受控组件
     <FInput defaultValue={x} ref={input}/> 非受控组件
    

    区别受控组件的状态由开发者维护,非受控组件的状态由组件自身维护(不受开发者控制)

  2. 必考:React 有哪些生命周期函数?分别有什么用?(Ajax 请求放在哪个阶段?)
    答题思路跟 Vue 的一样

    1. 钩子在文档里,蓝色框框里面的都是生命周期钩子
    2. 把名字翻译一遍就是满分
    3. 要特别说明哪个钩子里请求数据,答案是 componentDidMount
  3. 必考:React 如何实现组件间通信?

    1. 父子靠 props 传函数
    2. 爷孙可以穿两次 props
    3. 任意组件用 Redux(也可以自己写一个 eventBus)
  4. 必考:shouldComponentUpdate 有什么用?

    1. 要点:用于在没有必要更新 UI 的时候返回 false,以提高渲染性能
    2. 参考:taobaofed.org/blog/2016/0…
  5. 必考:虚拟 DOM 是什么?

    1. 要点:虚拟 DOM 就是用来模拟 DOM 的一个对象,这个对象拥有一些重要属性,并且更新 UI 主要就是通过对比(DIFF)旧的虚拟 DOM 树 和新的虚拟 DOM 树的区别完成的。
    2. 参考:www.alloyteam.com/2015/10/rea…
  6. 必考:什么是高阶组件?

    1. 要点:文档原话——高阶组件就是一个函数,且该函数接受一个组件作为参数,并返回一个新的组件。
    2. 举例:React-Redux 里 connect 就是一个高阶组件,比如 connect(mapState)(MyComponent) 接受组件 MyComponent,返回一个具有状态的新 MyComponent 组件。
  7. React diff 的原理是什么?
    看你记忆力了:imweb.io/topic/579e3…

  8. 必考 Redux 是什么?

// store.js 定义 Store 的初始值
const initialState = { value: 0 }

// Reducer,处理 Action 返回新的 State
function counterReducer(state = initialState, action) {
  switch (action.type) {
    case 'counter/incremented':
      return { value: state.value + 1 }
    case 'counter/decremented':
      return { value: state.value - 1 }
    default:
      return state
  }
//app.js
import React from 'react'
import ReactDOM from 'react-dom'

import { Provider } from 'react-redux'
import store from './store'

import App from './App'

const rootElement = document.getElementById('root')
ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  rootElement
)
//page
import { connect } from 'react-redux'
import Counter from '../components/Counter'
import actions from '../actions/counter';

//将state.counter绑定到props的counter
const mapStateToProps = (state) => {
    return {
        counter: state.counter
    }
};
//将action的所有方法绑定到props上
const mapDispatchToProps = (dispatch, ownProps) => {
    return {
        increment: (payload) => dispatch({type:'counter/incremented',payload}),
        decrement: (payload) => dispatch({type:'counter/decremented',payload})
    }
};

//通过react-redux提供的connect方法将我们需要的state中的数据和actions中的方法绑定到props上
export default connect(mapStateToProps, mapDispatchToProps)(Counter)
1.  背下文档第一句:Redux 是 JavaScript 状态容器,提供可预测化的状态管理。重点是『状态管理』。
1.  说出核心概念的名字和作用:Action/Reducer/Store/单向数据流
1.  说出常用 API:store.dispatch(action)/store.getState()
  1. connect 的原理是什么?
    react-redux 库提供的一个 API,connect 的作用是让你把组件和store连接起来,产生一个新的组件(connect 是高阶组件)
    参考:segmentfault.com/a/119000001…

TypeScript

  1. never 类型是什么?
    不应该出现的类型 尤雨溪的答案:www.zhihu.com/question/35…
  2. TypeScript 比起 JavaScript 有什么优点?
    提供了类型约束,因此更可控、更容易重构、更适合大型项目、更容易维护

Webpack

  1. 有哪些常见的Loader?他们是解决什么问题的?

    • file-loader:把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件
    • url-loader:和 file-loader 类似,但是能在文件很小的情况下以 base64 的方式把文件内容注入到代码中去
    • source-map-loader:加载额外的 Source Map 文件,以方便断点调试
    • image-loader:加载并且压缩图片文件
    • babel-loader:把 ES6 转换成 ES5
    • css-loader:加载 CSS,支持模块化、压缩、文件导入等特性
    • style-loader:把 CSS 代码注入到 JavaScript 中,通过 DOM 操作去加载 CSS。
    • eslint-loader:通过 ESLint 检查 JavaScript 代码
  2. 有哪些常见的Plugin?他们是解决什么问题的?

    • define-plugin:定义环境变量
    • commons-chunk-plugin:提取公共代码
    • uglifyjs-webpack-plugin:通过UglifyES压缩ES6代码
  3. 英语题:loader 和 plugin 的区别是什么?
    不同的作用

    • Loader直译为"加载器"。Webpack将一切文件视为模块,但是webpack原生是只能解析js文件,如果想将其他文件也打包的话,就会用到loader。 所以Loader的作用是让webpack拥有了加载和解析非JavaScript文件的能力。
    • Plugin直译为"插件"。Plugin可以扩展webpack的功能,让webpack具有更多的灵活性。 在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。 不同的用法
    • Loadermodule.rules中配置,也就是说他作为模块的解析规则而存在。 类型为数组,每一项都是一个Object,里面描述了对于什么类型的文件(test),使用什么加载(loader)和使用的参数(options
    • Pluginplugins中单独配置。 类型为数组,每一项是一个plugin的实例,参数都通过构造函数传入。
  4. 必考:如何按需加载代码?

    通过import(*)语句来控制加载时机,webpack内置了对于import(*)的解析,会将import(*)中引入的模块作为一个新的入口在生成一个chunk。 当代码执行到import(*)语句时,会去加载Chunk对应生成的文件。import()会返回一个Promise对象,所以为了让浏览器支持,需要事先注入Promise polyfill

  5. 必考:如何提高构建速度?

    1. 使用Happypack 实现多线程加速编译;
  6. 转义出的文件过大怎么办?

    1. 多入口情况下,使用CommonsChunkPlugin来提取公共代码;
    2. 压缩代码。删除多余的代码、注释、简化代码的写法等等方式。可以利用webpack的UglifyJsPluginParallelUglifyPlugin来压缩JS文件, 利用cssnano(css-loader?minimize)来压缩css

上面五题请看这个不错的参考:zhuanlan.zhihu.com/p/44438844

安全

  1. 必考:什么是 XSS?如何预防?
    跨站脚本攻击 有一个input输入框,需要用户输入名字,用户却输入一个恶意脚本,<script>alert(document.cookie)</script>
    1、获取用户输入,不用.innerHTML,用innerText。
    2、对用户输入进行过滤,如 HTMLEncode 函数实现应该至少进行 & < > " ' / 等符号转义成 &amp &lt &gt &quot &#x27 &#x2F;
  2. 必考:什么是 CSRF?如何预防?
    跨站请求伪造,当用户登录了A网站,再被诱导到一个含有图片或ifromSRC为A域名的B网站,这时候B网站就可以拿到用户在A网站的cookie; 如何预防?添加校验token

开放题目

  1. 必考:你遇到最难的问题是怎样的?
    要点:一波三折。参考 www.zhihu.com/question/35…
  2. 你在团队的突出贡献是什么?
    把小事说大。
  3. 最近在关注什么新技术
    书、博客、推特、知乎,不要说 CSDN、百度。
  4. 有没有看什么源码,看了后有什么记忆深刻的地方,有什么收获
    看过源码说源码,推荐看 underscore.js 的源码
    没看过源码就说同事的代码,代码烂就说哪里烂,代码好就说哪里好
    收获:命名规范、设计模式

刁钻题目

  1. 代码

     [1,2,3].map(parseInt)
    

    答案

     1
     NaN
     NaN
    
  2. 代码

     var a = {name: 'a'}
     a.x = a = {}
     问 a.x 是多少?
    

    答案

     undefined
    
  3. (a ==1 && a== 2 && a==3) 可能为 true 吗?

    1. 利用 == 会调用 valueOf() 的特性

       var a = {
        value: 1,
        valueOf(){
         return this.value++
        }
       }
       a ==1 && a== 2 && a==3 // true
      
    2. 利用 a 会读取 window.a 的特性

       var value = 1; 
       Object.defineProperty(window, 'a', {
           get(){
               return value++;
           }
       })
       a ==1 && a== 2 && a==3 // true
       // 或者 
       a ===1 && a=== 2 && a===3 // true
      

超纲题

  1. JS 垃圾回收机制

    1. 看图讲解 javascript.info/garbage-col…

      1. 什么是垃圾
        i.当一个函数执行完成后,函数中声明的方法和变量就是垃圾; ii.当一个变量没有被引用,
      2. 如何捡垃圾(遍历和计数,只是不同的算法而已)
      3. 前端又有其特殊性(JS进程和DOM进程)
    2. 更深入一些的讲解 newhtml.net/v8-garbage-…

  2. Eventloop 说一下
    就是在程序中设置两个线程:一个负责程序本身的运行,称为"主线程";另一个负责主线程与其他进程(主要是各种I/O操作)的通信,被称为"Event Loop线程"。

     setTimeout(function () {
         console.log(4);
     }, 0);
     new Promise(function (resolve) {
         console.log(1);
         resolve();
         console.log(2);
     }).then(function () {
         console.log(5);
     });
    
     console.log(3);
    
     1
     2
     3
     5
     4
    
    1. 肤浅理解:『一会儿』和『尽快』异步任务

    2. 详细理解:Eventloop 是个啥?

    3. 浏览器有 Eventloop 吗?

    4. 每个 API 对应哪个任务队列?

      1. setTimeout
      2. setImmediate(浏览器没有)
      3. process.nextTick(浏览器没有)
      4. MutationObserver(Node 没有)
      5. promise.then
      6. await
    5. 这种题目尽量说思路,因为你不可能通过眼睛看出结果(必须画图)

       async function async1() {
           console.log(1);
           await async2();
           console.log(2);
       }
       async function async2() {
           console.log(3)
       }
      
       async1();
      
       new Promise(function (resolve) {
           console.log(4);
           resolve();
       }).then(function () {
           console.log(5);
       });
      
       1
       3
       4
       2
       5
      

      注意:这一题的答案不唯一,在 Node.js 和 Chrome 的结果不一样,甚至在 Chrome 上也是时而这个答案,时而那个答案。所以还是说思路最重要。

service worker

  1. Service Worker 提供了拦截前端请求的能力,使得它能够结合 Local Storage,成为一个独立的缓存方案。
  2. 缓存永远不过期。你只要下载过一次,就永远不需要再重新下载,除非主动删除。
  3. 永远不会访问过期的资源。换句话说,如果发布了一个新版本,那么你可以通过版本化的一些机制,来确保用户访问到的一定是最新的资源。

注册 Service Worker

if ('serviceWorker' in navigator) {
  navigator.serviceWorker
    .register('/sw.js')
    .then(() => {
      // 注册成功
      console.log('Service worker registered.');
    }, (err) => {
      // 注册失败
      console.log('ServiceWorker registration failed: ', err);
    });
  });
}

在 Service Worker 安装之后初始化缓存机制

const cacheName = 'my_app_cache';
// 在 sw.js 中监听安装完成事件
self.addEventListener('install', function(e) {
  console.log('Service worker installed.');
  // 初始化 Cache Storage
  const cacheOpenPromise = caches.open(cacheName);
  // 安装过程中,等待 Cache Storage 配置完成
  e.waitUntil(cacheOpenPromise);
});

拦截请求


// 监听所有的请求
self.addEventListener('fetch', function(e) {
  // 如果请求的路径不是 js 结尾,就通过 return false 来告诉 
  // service worker 这个请求应该发送到服务器端
  if (!request.url.endsWith('.js')) return false;
  
  // 否则检查 cache 中是否有对应的 response
  const promise = caches.open(cacheName).then(cache => {
    // 使用 cache.match 
    return cache.match(e.request).then(res => {
      if (res) {
        // 如果缓存存在则直接返回结果
        return Promise.resolve(res);
      } else {
        // 否则发出请求,并存到 cache
        const req = new Request(e.request.url);
        return fetch(corsRequest).then(res => {
          // 更新 cache
          cache.put(request, res.clone());
          return res;
        })
      }
    });
  });
  // 使用 e.respondWith 方法给请求返回内容
  e.respondWith(promise);
});

React单元测试

//测试组件
import { render, screen } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
  render(<App />);
  const linkElement = screen.getByText(/learn react/i);
  expect(linkElement).toBeInTheDocument();
});
//测试自定义hooks
import {renderHook,act} from '@testing-library/react-hooks'
import useCounter from './useCounter';

test('useCounter',()=>{
    const {result} = renderHook(()=>useCounter());
    act(()=>{
        result.current.increment();
    });
    expect(result.current.count).toBe(1);
    act(()=>{
        result.current.decrement();
    })
    expect(result.current.count).toBe(0);
});
//测试函数
import add from './add';

test('add', () => {
  const s = add(1, 2);
  expect(s).toBe(3);
});

个性化题目

  • PWA
  • echarts.js / d3.js
  • three.js
  • flutter
  • SSR