前端面试小册子

390 阅读21分钟

前端面试小册

常考面试题一

1、原始类型有那种?nulll是对象吗?

原始数据类型主要分为: number,string,Boolean,null,undefind null是对象

2、对象类型和基础类型的不同之处,函数参数是对象类型和基础类型分别会如何?

基础类型按值储存在栈内存中 对象类型(引用类型)储存在堆内存中。以指针方式去使用 函数参数和复制操作: 基础数据类型在重新创建一份新值。改变其中任何一个,另一个不受影响 引用数据类型。变量对象也会为其开辟空间,但复制的是指针,都指向内存的值,一个改变,另一个也回受影响

3、typeof 与 instanceof?

typeof 与 instanceof都是判断数据类型的。区别如下: typeof判断基础数据类型。当判断null的时候将返回objcet instanceof判断引用数据类型,原理根据原型链追踪去实现

4、类型转换?

  • 转数字?

    null=》0 undefined=》NaN Boolean true =》1 false=》0 Sting 数字=》数字 字母=》NaN 空字符串=》0 数组 空数组=》0 一个元素且为数字=》数字 其他=》NaN 引用类型 NaN

  • 转字符串

    underfind=》‘underfind’ null=》‘null’ Number '数字' Boolean ‘true’ ‘false’ 数组 ‘【12.43,323】’=>12.43,323 对象 [object Object] function function(){console.log(212)}

  • 转布尔值?

    null false underfind false Number 0,-0,NaN=>false 其他=>true String ''=>false 其他=>true 引用类型 true

5、对象转基础数据类型?

转字符串类型直接调用toString() 转其他类型先转valueOf()

6、四则运算?

运算中其中一方是字符串,另一个方也将转换成字符串 如果一方不是字符串数字。那么会将它转成数字或者字符串 除了加减运算之外。一方为数字,另一方转为数字

7、比较运算符?

如果是对象,就通过 toPrimitive 转换对象 如果是字符串,就通过 unicode 字符索引来比较

8、如何判断this指向,箭头函数中this指向那里?

  • 全局this

    window

  • 函数中的this

    对象调用,则指向这个对象 独立调用,指向window

  • 使用call,apply显示指定this

    apply.call,bind动态this

  • 构造函数与原型方法上的this

    指向当前实例化后的对象。

  • 箭头函数中的this

    调用箭头函数外层第一个普通函数的this

常考面试题二

==和===的区别?

  • ==

    先判断数据类型是否相同,一致判断值大小 类型不同,进行类型转换 先判断是否是null和underfind的比较,是返回true 判断是否是string和number的比较,是先将字符串转成对应的数字进行比较 判断一方是否为布尔值,是则把布尔值换成number进行比较 判断一方是否为object,且另外一方为string,number,是则把object转成基础类型进行判断

  • ===

    不会尽享类型转换,直接对比值的大小

闭包

  • 什么是闭包?

    在函数执行上下A 在执行上下文A中的函数B 调用了A中的变量。闭包产生

  • 闭包的好处?以及和垃圾回收机制的关系?

    在js中,函数上下文执行完之后,生命周期结束后。垃圾回收机制就会回收内部不被使用的变量,也就是内存中失去引用的变量,对其进行回收。闭包会阻止此过程 js中具有自动垃圾回收机制,对于函数内部的变量失去引用之后,很快会被回收,但是处于全局的变量,js不会回收,除非引用完及时释放,尽量少使用全局变量

浅拷贝和深拷贝

  • 浅拷贝

    基础类型值的拷贝 引用类型拷贝的引用地址(指针) 一个改变另一个必受影响 Object.assign()和...都是浅拷贝

  • 深拷贝

    重新开辟内存空间,互相不影响

    function deepClone(obj) {
              function isObject(o) {
                  return (typeof o == 'object' || typeof o == 'function') && o !== null
              }
              if (!isObject(obj)) {
                  throw new Error('this is not objcet')
              }
              let isArray = Array.isArray(obj)
              let newObj = isArray ? [...obj] : {
                  ...obj
              }
              Reflect.ownKeys(newObj).forEach(key => {
                  newObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key]
              })
              return newObj
         }
    

原型链

对象的 proto 属性指向原型, proto 将对象和原型连接起来组成了原型链

  • 构造函数
  • 原型
  • 原型

es6常考知识点

什么是提升?什么是暂时性死区?var、let 及 const 区别?

  • 提升

    当控制器执行到可执行的代码的时候,js就会创建执行上下文 执行上下文的创建阶段会创建变量对象。此时 此时会先创建arguments对象 检查函数,以函数声明并创建属性 检查var声明的变量,复制underfind 其实这个就是所谓的变量提升阶段

  • var 、const、let的区别?

    区别: var存在变量提升 可以重复声明 可以用window去调用 const、let 不存在变量提升 不可重复声明 拥有块级作用域 不可通过window去调用 const赋值不可更改

  • 暂时性死区

    当变量未声明之前。去调用此变量,被叫做暂时性死区

原型如何实现继承?Class 如何实现继承?Class 本质是什么?

  • 原型继承

    • 组合继承

      构造函数继承通过 call改变this指向去继承上面的方法和属性 原型上的继承 将子类的原型指向父级的实例话对象

    • 寄生继承

      构造函数继承同样使用call改变指向进行继承 原型继承则通过Object.create()进行继承

  • class继承

    class 实现继承的核心在于使用 extends 表明继承自哪个父类 并且在子类构造函数中必须调用 super,因为这段代码可以看成 Parent.call(this, value)

  • class本质

    class 的本质就是函数。

为什么要使用模块化?都有哪几种方式可以实现模块化,各有什么特点?

  • 模块化的使用

    解决命名冲突 实现复用 提高代码的可维护程度 避免变量全局污染

  • 匿名函数自执行

  • ES Module

    不支持动态引入

Proxy 可以实现什么功能?

  • 对象的代理

    代理,可是实现数据的双向绑定

      let handler = {
                  get(target, property, receiver) {
                      getLogger(target, property)
                      return Reflect.get(target, property, receiver)
                  },
                  set(target, property, value, receiver) {
                      setBind(value, property)
                      return Reflect.set(target, property, value)
                  }
              }
              return new Proxy(obj, handler)
          }
          let p = onWatch(
              obj,
              (v, property) => {
                  console.log(`监听到属性${property}改变为${v}`)
              },
              (target, property) => {
                  console.log(`'${property}' = ${target[property]}`)
              }
          )
          p.a = 2 // 监听到属性a改变
          console.log(p.a)
    
  • 表单校验

  • 阅后即焚

  • 过滤不存在的属性

map, filter, reduce

  • map

    返回一个新数组。可以在这个函数中加逻辑处理返回 接受三个参数 当前的元素 index 愿数组

  • filter

    返回一个新数组。对符合条件元素进行返回 接受三个参数 当前的元素 index 愿数组

  • reduce

    数组的计算 接受两个参数,分别是回调函数和初始值

性能优化琐碎事

图片优化

常考算法知识点

Vue 常考基础知识点

生命周期

父子组建传旨

设计模式

工厂模式

隐藏了这个复杂的过程,只需要一句代码调用就能实现功能。

单例模式

隐藏了这个复杂的过程,只需要一句代码调用就能实现功能。

适配器模式

适配器用来解决两个接口不兼容的情况,不需要改变已有的接口,通过包装一层的方式实现两个接口的正常协作。 ``` class Man { getName() { return '港版插头' } } class Son { constructor() { this.proxy = new Man() } getName() { return this.proxy.getName() + 'hahahahh' } static create(name) { console.log(name) } } let s = new Son() console.log(s.getName())

```

装饰模式

代理模式

代理是为了控制对对象的访问,不让外部直接访问到对象。在现实生活中,也有很多代理的场景。比如事件代理

发布-订阅模式

发布-订阅模式也叫做观察者模式。通过一对一或者一对多的依赖关系,当对象发生改变时,订阅方都会收到通知。

外观模式

JS 异步编程及常考面试题

并发(concurrency)和并行(parallelism)区别?

并发是一段时间相继完成,比如A和B,一段时间内切换完成类似早上起床前。洗漱,吃饭,出门。一段时间完成的一些事 并行是同时进行,比如边看手机边吃饭

什么是回调函数?回调函数有什么缺点?如何解决回调地狱问题?

当我们异步请求成功时会在回调函数中写我们的逻辑。当然这里的逻辑还涉及到进一步的请求,不断嵌套就会出现回调地狱 回调函数缺点: 嵌套过深,耦合度高,不易维护 难以捕捉错误 不能使用try。。catch 使用Generator和promise以及async都可以

你理解的 Generator 是什么?

天然的迭代器,可以是遍历停下来 可控制迭代器的函数,可以暂停,也可以选择任何时候恢复 使用场景: 抽奖 小游戏 斐波那契数列

Promise 的特点是什么,分别有什么优缺点?什么是 Promise 链?Promise 构造函数执行和 then 函数执行有什么区别?

特点: Promise三个状态,padding,resolve和reject。状态不可逆,一旦确认,无法改变 Promise链: 通过then方法去传递执行,执行then方法之后会返回一个promise对象,来完成链式操作 构造函数和then函数执行有哪些区别? 构造函数内的内容立即执行。 then的函数当状态改变在进行执行

async 及 await 的特点,它们的优点和缺点分别是什么?await 原理是什么?

优化了promise链式调用,以同步的写法去操作异步代码 原理其实是promise的语法糖,await 内部通过promise静态方法Promise.resove()返回一个新的promise

setTimeout、setInterval、requestAnimationFrame 各有什么特点?

JS 进阶知识点及常考面试题

apply

  Function.prototype.myApply = function (context, arr) {
            if (typeof this !== 'function') {
                throw new Error('error')
            }
            context = context || window
            context.fn = this
            let reslut;
            if (arr) {
                reslut = context.fn(...arr)
            } else {
                reslut = context.fn()
            }
            delete context.fn
            return reslut
        }

call

 Function.prototype.myCall = function (context) {
            if (typeof this !== 'function') {
                throw new Error('error')
            }
            context = context || window
            context.fn = this
            let args = [...arguments].slice(1)
            let reslut = context.fn(...args)
            delete context.fn
            return reslut
        }

bind

Function.prototype.myApply = function (context, arr) {
            if (typeof this !== 'function') {
                throw new Error('error')
            }
            context = context || window
            context.fn = this
            let reslut;
            reslut = arr ? context.fn(...arr) : context.fn()
            delete context.fn
            return reslut
        }

new

function news() {
            let obj = new Object()
            let Constructor = [...arguments].shift()
            obj.__proto__ = Constructor.prototype
            let res = constructor.apply(obj, arguments)
            return typeof res === 'object' ? ret : obj
        }

instanceof

  function instanceofs(left, right) {
            let prototype = right.prototype
            left = left.__proto__
            while (1) {
                if (left == null || left == undefined) {
                    return false
                }
                if (left == prototype) {
                    return true
                }
                left = left.__proto__
            }
        }

垃圾回收机制

v8实现了精确式GC,GC算法采用分代垃圾回收机制,因此。v8将内存(堆)分为新生代和老生代两部分

  • 新生代算法

    对象存活时间较短,使用 Scavenge GC 算法 在新生代内存空间分为2部分,分别是from空间和to空间,在这2个空间其中必然有一个是空闲的。新分配的对象会放到from空间,当from空间被占满的时候,新生代GC就会启动,算法会检查from空间的存活对象,将他们复制到to空间,其中失去存活的对象就会被销毁,将复制完,from和to空间互换,

    • from空间
    • to空间
  • 老生代算法

    • 什么时候启动标记清除算法?

      某一个空间没有分块时 空间中被对象超过一段限制时 空间不能保证新生代对象转到老生代中 这个时候遍历堆中所有活的对象,在标记完成后,销毁那些没有标记的对象。

    • 标记整理算法

      清除对象后会造成堆内存出现碎片的情况 当碎片超过一定限制的时,会启动压缩算法。在压缩过程中,将活的对象像一端移动,直到所有对象都移动完成然后清理掉不需要的内存。

    • 什么时候对象会出现老生代算法中?

      新生代中的对象是否已经经历过Scavenge算法。如果经历过,会将新生代空间转到老生代空间中 当to空间的对象占空间大小的25%,这样情况下。为了不影响内存分配。会将对象从新生代的空间转老生代空间中

浏览器基础知识点及常考面试题

跨域

  • 什么是跨域?

    浏览器出与安全考虑。有同源策略。也就是当协议。域名。端口号任何一个不同就是跨域。Ajax就会请求失败

  • 为什么浏览器要使用同源策略?

    其实主要是用来防止 CSRF 攻击的。简单点说,CSRF 攻击是利用用户的登录态发起恶意请求。

  • 你有几种方式可以解决跨域问题?

    jsonp cors document.domain postMessage

  • 了解预检请求嘛?

    对于复杂请求来说,首先会发起一个预检请求,该请求是 option 方法的,通过该请求来知道服务端是否允许跨域请求。

事件

  • 事件处理程序

    • IE事件处理程序

      增加和和删除事件: element.attachEvent('on'+事件类型,处理函数) element.detachEvent(‘on’+事件类型,处理函数)

    • chrome事件处理程序

      element.addEventListener('click',处理函数,是否冒泡) false(默认冒泡) true(捕获) element.removeEventListener('click',处理程序,是否冒泡) 同添加事件一致

  • 事件对象

    • IE事件对象

      cancelBubble =》默认值为false,但设置true就可以取消事件冒泡 returnValue =》默认true,设置为false,,就可以取消事件的默认行为 srcElement =>事件目标 type=>事件类型

    • chrome事件对象

      stopPropagation =》取消事件冒泡 preventDefault  =》取消事件的默认行为 target  =>事件目标 type=>事件类型

  • 事件代理

    如果一个节点中子节点是动态生成的,那么子节点需要注册事件的化应该注册在父节点上 好处: 节省内存空间 不需要给子节点注销事件

  • 事件的触发过程是怎么样的?知道什么是事件代理嘛?

    事件触发有三个阶段: window往事件触发传播。遇到注册的捕获事件会触发 传播到事件触发时触发注册事件 从事件触发之处向window传播。遇到注册的冒泡会触发 从捕获到目标阶段再到冒泡的过程

    • 事件捕获

      从上向下执行的一个过程 Document=》html=》body=》div

    • 事件执行阶段

    • 事件冒泡

      从当前事件触发向上查找过程 div=>body=>html=>document

存储

cookie已经不建议存储了,如果没有大量数据存储需要,使用localStorage和sessionStorage 对于不怎么改变的数据存使用localStorage。否则使用sessionStorage

  • cookie

    作用:主要用于储存用户的登陆信息, 生命周期:一般有服务器生成,可以设置过期时间 数据储存大小:4k 与服务端通信:每次都会携带在headr中,对于请求性能影响

    属性 作用
    value 如果用于保存登陆状态,应加密,不能使用明文的用户标示
    http-only 不能通过js访问cookie,减少xss的攻击
    secure 只能在协议为https的请求中携带
    same-site 规定浏览器不能在跨域请求中携带cookie,减少CSRE攻击
  • localStorage

    生命周期:除非被清理,否则一直存在 数据存储大小:5M 与服务端通信:不参与

  • sessionStorage

    数据生命周期:页面关闭就清理 数据存储大小:5M 与服务端通信:不参与

  • indexDB

    数据生命周期:除非被清理,否则一直存在 数据存储大小:无限 是否与服务端通信:不参与

Service Worker

service worker是一段脚本,与web worker一样,也在后台运行,作为一个独立的线程。运行环境和普通脚本不同,所以不能直接参与web交互行为。native app可以做到离线使用,消息推送。后台自启动,service worker的出现为了是web app 也有类似的能力

  • 使用场景

    • 后台消息传送

    • 网络代理转发请求。伪造响应

    • 离线缓存

      • 离线缓存
    • 消息推送

浏览器缓存机制

性能优化领域 缓存可以说是性能优化中最简单高效的一种方式了。他以最高减少网络传输所带来的损耗 数据请求: 网络请求 后端处理 浏览器响应 直接使用缓存,而不发请求 或发起请求后端存储的数据和前端一致,那么就没必要将数据回传过来,这样可以减少响应数据

缓存位置

  • Service Worker

    优点: 它的缓存和其他内建的缓存机制不同。它可以由我们自定缓存那些文件,如何匹配缓存,如何读取缓存,并且缓存是持久性的

  • Memory Cache

    优点: 内存中的缓存。读取内存中的数据比磁盘要快很多 访问过页面之后,再次刷新页面,可以发现之前很多数据都来自内存缓存 缺点: 持续性短,随着进程的释放而释放 大文件一般都是不会储存内存中,反之优先

  • Disk Cache

    优点: 储存在硬盘中。覆盖率基本是最大的,什么都可以存入 跟内存相比。容量和储存时效性超级好

  • Push Cache

    储存在会话中,一旦会话结束就会被释放

缓存策略

  • 强缓存

    • Expires

      是HTTP/1的产物,表示资源的过期时间,并受限与本地时间。如果修改了时间,缓存失效

    • Cache-control

      出现于HTTP/1.1的产物,优先级比Expires要高 该属性可以调整设置时间 Cache-control可以在请求头或者响应中设置,并且可以组合多种指令 private=>响应可以被客户端缓存 pablic=》可以同被客户端和代理服务器缓存 no-cache=》资源会被缓存,但立即失效。下次发起请求会验证是否过期

  • 协议缓存

    如果缓存过期了。就需要发起请求验证资源是否更新,协议缓存可以通过2个http-header实现:Last-Modified+ETag 当浏览器发起请求验证资源时,如果资源没有做出改变,那么服务器会返回304状态吗。并且更新缓存有效期 如果资源有所改变,更新资源

    • Last-Modified

      表示本地文件最后更改的日期。If-Modified-Since会将last-Modified的值发送给服务器,询问服务器在该日期更新后资源是否有变动,有更新,返回更新后的信息,反之,返回状态码 弊端: 如果是本地打开缓存文件,即使没有对文件进行修改,但还是会造成last-Modified的修改。服务端不能命中缓存导致发送相同的资源 因为last-Modified只能以秒计时。如果在不可感知的时间你内完成了这个文件,那么会造成服务端认为资源被秒中,不会返回正确值

    • ETag

      类似指纹,If-None-Match会将ETag发送给你服务器,询问服务器资源ETag是否改变,如有改变,将更新的资源返回来,ETag比Last-Modified的优先级高

浏览器渲染机制

浏览器接收到 HTML 文件并转换为 DOM 树

首先浏览器打开一个网页的时候,首先会解析他对应的html,在网络传输中我们平时所写的js+css+html都都是以子节数据(0-1)进行传输 转子节转为字符串。 字符串通过词法解析为标记(token),这一过程也被称为标记法。 将结束标记之后,将会把标记转为node 根据node之前的联系转成dom

将 CSS 文件转换为 CSSOM 树

首先这一过程是非常耗性能的,因为浏览器会确定每一个节点样式分别是什么,需要递归匹配到数据的变化 子节数据 字符串 标记(token) node cssom

生成渲染树?

当我们生成dom树和cssom树的时候,就需要将这2个树和成渲染树 在这一过程中,将包括需要的节点和这些节点的样式去渲染出来 当浏览器生成渲染树之后,浏览器根据渲染树布局,然后GPU绘制。合成图层

为什么操作 DOM 慢?

因为dom属于渲染引擎,js属于js引擎,面对两个线程之前的通信,操作dom次数一多,也就等同于进行线程之前的切换,并且操作dom可能带来回流。

什么情况阻塞渲染?

渲染的前提是生成渲染树,所有html和css肯定会堵塞渲染 降低一开始需要渲染的文件大小 并且扁平化。优化选择器 当浏览器在执行到script标签时,就会暂时dom,完成后从暂停处继续执行, 首评加载的越快,就不该在首评加载js。建议将script放到body下面的原因 async(适合没有依赖的文件) js文件下载和解析不会阻塞渲染 defer 并行下载,会等到html执行完毕之后在进行执行

重绘(Repaint)和回流(Reflow)

回流一定会引起重绘 但重绘不一定会引起回流

  • 重绘(Repaint)

    当改变节点的样的时候。不会改变布局的时候,比如修改color样式。

  • 回流(Reflow)

    一般布局修改,dom操纵一般会触发回流

减少重绘和回流的方式?

使用 transform 替代 top 使用 visibility 替换 display: none ,因为前者只会引起重绘,后者会引发回流(改变了布局) 不要把节点的属性值放在一个循环里当成循环里的变量 不要使用 table 布局,可能很小的一个小改动会造成整个 table 的重新布局 动画实现的速度的选择,动画速度越快,回流次数越多,也可以选择使用 requestAnimationFrame CSS 选择符从右往左匹配查找,避免节点层级过多

不考虑缓存和优化网络协议的前提下,考虑可以通过哪些方式来最快的渲染页面?

从文件大小 从script标签上使用来考虑 从html和css代码书写上来考虑 从需要下载的内容是否需要在首屏使用上来考虑

安全防范知识点

xss

想尽一切办法将可执行的代码注入网页中

  • 持久型攻击

    攻击的代码被服务器写日数据库,这种危害会很大,如果网站访问量大的话,会导致正常访问页面的用户都收到攻击 一般会以评论方式去注入

  • 非持久型攻击

    一般以修改URL参数的方式进行攻击 诱导用户访问链接从而实现攻击

转义字符

对于用户输入的东西永远不要相信,最普通的做法是对用户输入的内容进行转译。 引号。尖括号。斜杠等进行转译‘ 或者使用白名单也可以,js-xss来实现

cors

跨站伪造请求,攻击者构造出一个向后端请求的地址,诱导用户点击或者通过某种途径自动发起请求,如果用户是在登陆的情况下,后端以为是用户操作,从而进行诱导。 常用方式: 加入网站中有个get请求提交表单的接口。那么攻击者会在钓鱼网站加入一个图片,图片的地址就是这个评论的接口 防御: get请求不对数据进行修改 不让第三方访问到用户的cookie(some-site) 阻止第三方网站请求接口(验证refrer) 请求是附带验证信息,比如token或者验证码,进行判断

点击劫持

攻击者通过将要攻击的网站通过iframe的方式放入自己的网站中,并将iframe设为透明,诱导用户点击 防御: js判断,删除页面中的iframe

中间人攻击

是指攻击方同时将服务端和客户端同时进行连接。并让对方认为都是安全的,但实际上整个过程中,都是被控制了,攻击者可以同时修改用户的信息和数据库中的内容 防御: 尽量避免在公众场合使用Wi-Fi。避免被攻击

v8下的js性能优化

XMind: ZEN - Trial Version