前端面试题

1.CSS高度塌陷,怎么解决

子元素设置浮动以后,子元素会完全脱离文档流,导致父元素高度塌陷

  • 解决方案
    • 给父元素一个固定的值(只是表面好)
    • overflow设置为hidden
    • 在高度塌陷的父元素最后,设置一个空白的div,对其进行清除浮动
    • 通过after伪类,添加一个空白的块元素,然后进行清除浮动

2.继承

什么是对象

  • 对象是对单个事物的抽象,对象是一个容器,封装了属性(状态)和方法(行为)

原型

  • 在javascript中,函数可以有属性,每个函数都有一个特殊的属性:原型(protptype),属性值是一个对象
  • 在prototype中,默认存在一个constructr属性,属性值就是当前函数自身原型对象的所有属性和方法都能被实例共享

构造函数

  • 用来专门生成实例对象的函数,他是对象的模板,描述对象的基本结构
构造函数的特点
  • 函数体内使用this,关键字,代表所要生成的实例对象
  • 生成对象的时候,必须使用new关键字
  • 构造函数的首字母要大写
new命令
  • 执行构造函数,返回一个实例对象
new命令的原理
  • 创建一个空对象,作为将要返回的对象实例
  • 将这个空对象的原型,指向构造函数的Prototype属性
  • 将这个空对象的赋值给函数内部关键字this
  • 执行构造函数内部代码
ES5继承
  • 原型链继承

    让新实例的原型对象等于父类的实例

    • 优点
      • 子类可以继承父类所有的属性和方法
    • 缺点
      • 子类无法向父类传参
      • 父类原型链上的属性被多个实例共享,造成一个实例修改了原型,其他的也会变
  • 使用构造函数继承

    • 用call和apply将父类的构造函数引入子类函数
    • 优点
      • 原型链引用值独立,不再被所有实例共享
      • 子类可以向父类传参
    • 缺点
      • 构造函数只能继承父类实例的属性和方法,不能继承父类的原型
  • 组合继承

    • 结合两种模式的优点,传参和复用
  • 原型式继承

  • 寄生式继承

  • 寄生组合式继承

  • es6继承

    • 主要使用extends关键字实现继承,利用class配合extends和super

      class A{
         constructou(){
      
        }
      }
      
      classB extends A{
        constructouo(){
          super()
        }
      }
      

3.基本数据类型和内置对象

  • JS数据类型分为基本数据类型和引用数据类型

    • 基本数据分为underfined null boolean number string

    • 引用数据类型为object

  • js内置对象包含boolean string number array function date math object regexp error global

4.webpak中loader和plugin的区别

loader

  • loader让webpack能够去处理那些非javascript文件,(webpack自身只理解javscript)
  • loader可以将所有的类型文件转为webpack能够处理的有效模块,然后你就可以利用webpack的打包能力,对他们进行处理
  • 本质上,webpack,loader将所有类型的文件,转换为应用程序的依赖图(和最终的bundle)可以直接引用的模块

plugin

  • loader用于转换某些类型的模块,plugin插件则可以用于执行范围更广的任务
  • 插件的范围包括:从打包优化和压缩,一直到重新定义环境中的变量,插件接口功能极其强大,可以用来处理各种各样的任务
常用的loader
  • babel-loader 把es6转为es5
  • eslint-loader 检查代码格式
  • sty;e-lpader css-loader less-loader
  • file-loader url-loader
常用的plugin
  • html-webpack-plugin 根据模板自动生成html代码,并且自动引入css.js
  • clear-webpack-plugin 打包之前将指定的文件夹清除
  • uglifyjs-webpack-plugin:压缩js代码

5.怎么使用chunk做代码分割

optimization:{//最优化
  splitChunks:{
    chunks:"all"
  }
}

6.vue $set

  • 当生成vue实例,再次给数据赋值,有时候数据并没有更新视图,是因为收到es5的限制,vue不能检测到对象属性的添加或者删除,vue在初始化实例的时候将属性转换为getter/setter
  • 使用$set,让其有setter/getter
  • Vue.set()是将set函数绑定在vue的构造函数上,this.$set是将set函数绑定在vue原型上

7.node中怎么处理fs模块

  • fs模块常用的莫过于对文件的增删改查以及读取文件

  • 对文件的读取,有同步读取和异步读取,同步读取如果文件过大会造成阻塞,而异步读取不会导致IO阻塞

fs的常用方法

  • fs.stat 检测是文件还是目录
  • fs.mkdir 创建目录
  • fs.writeFile 创建写入文件
  • fs.appendFile 追加文件
  • fs.readFile 读取文件
  • fs.readdir 读取目录
  • fs.rename 重命名 移动文件
  • fs.rmdir 删除目录
  • fs.unlink 删除文件
fs.stat
const fs=require('fs')

  fs.stat('./html',(err,data)=>{
            if(err){
                console.log(err);
                return;
            }

            console.log(`是文件:${data.isFile()}`);
            console.log(`是目录:${data.isDirectory()}`);

        })
fs.mkdir 创建目录
  • path 将创建的目录路径
  • mode 目录权限(读写权限)
  • callback 回调
  fs.mkdir('./css',(err)=>{

            if(err){
                console.log(err);
                return;
            }
            console.log('创建成功');
        })
fs.writeFile 创建写入文件
  • filename (String) 文件名称
  • data (String | Buffer) 将要写入的内容,可以使字符串 或 buffer数据。
  • options (Object) option数组对象,包含
  • encoding (string) 可选值,默认 ‘utf8′,当data使buffer时,该值应该为 ignored。
  • mode (Number) 文件读写权限,默认值 438
  • flag (String) 默认值 ‘w’
  • callback {Function} 回调,传递一个异常参数err。
     fs.writeFile('./html/index.html','你好nodejs',(err)=>{

            if(err){
                console.log(err);
                return;
            }
            console.log('创建写入文件成功');
        })

8.node高并发

  • 只有一个主线程程序代码
  • 主线程之外,维护一个事件队列
  • 主线程代码执行完毕,通过event loop,也就是事件循环机制,开始从event queue的开头去除第一个事件,从线程池分配到一个线程去执行这个事件,然后去取第二个,主线程不断的检查事件队列中是否有未执行的事件,直到事件队列都执行完毕
  • 不断重复第三步

9.restful

  • Representational State Transfer 表述性状态转移
  • REST 指的是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就是 RESTful。
  • Web 应用程序最重要的 REST 原则是,客户端和服务器之间的交互在请求之间是无状态的。从客户端到服务器的每个请求都必须包含理解请求所必需的信息。如果服务器在请求之间的任何时间点重启,客户端不会得到通知。此外,无状态请求可以由任何可用服务器回答,这十分适合云计算之类的环境。客户端可以缓存数据以改进性能。
  • RESTFUL特点包括:
    • 每一个URI代表1种资源;
    • 客户端使用GET、POST、PUT、DELETE4个表示操作方式的动词对服务端资源进行操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源;
    • 通过操作资源的表现形式来操作资源;
    • 资源的表现形式是XML或者HTML;
    • 客户端与服务端之间的交互在请求之间是无状态的,从客户端到服务端的每个请求都必须包含理解请求所必需的信息

10.有哪些块元素,行内元素,有什么区别

块元素

h标签
p标签
ul,ol,dl
table
form 
div
特点
  • 总是从新的一行开始

  • 高度、宽度都是可控的

  • 宽度没有设置时,默认为100%

  • 块级元素中可以块级元素和行内元素

行内元素

span
a
br
b
strong
i
select
  • 和其他元素都在一行
  • 高度、宽度以及内边距都是不可控的
  • 宽高就是内容的高度,不可以改变
  • 行内元素只能行内元素,不能包含块级元素

11.promise ,async/await,Generator

  • promise的写法只是回调函数的改进,用then()方法免去了嵌套,更为直观
  • 最优秀的解决方案是使用async/await
  • 协程(coroutine),意思就是多个线程互相协作,完成异步任务
  • 协程遇到yield命令就会暂停,把执行权交给其他协程,等到执行返回继续往后执行,最大的优点就是代码写法和同步操作几乎没有差别,只是多了yield命令
  • Generator是协程在ES6的实现,最大的特点就是可以交出函数的执行权,懂得退让
  • async函数就是Generator函数的语法糖

Async函数的优点

  • 内置执行器
    • Generator函数的执行必须靠执行器,所有才有了co库,而asyc函数自带执行器。
  • 语义化更好
  • 更广的适用性
    • yield命令后面只能是Thunk函数或Promise对象,而async函数的await命令后面可以跟Promise对象和原始类型的值(数值,字符串和布尔值,但这是等同于同步操作)

12.浏览器的事件循环机制

基本概念

同步任务–异步任务–宏任务–微任务–任务队列

  • 事件循环可以简单的描述为 主代码块-微任务-宏任务-微任务(主代码块也是一个宏任务)

详细解释

  • 主代码块入栈
  • 顺序执行同步任务,遇到异步任务交浏览器内核模块处理,处理完将异步任务回调函数加入到任务队列中
  • 函数执行栈为空时,如果任务队列中微任务加入到任务栈,函数执行栈为空,在从任务队列取宏任务入栈
  • 执行2-3,直到所有的任务执行完

13.手写防抖节流

  • 函数防抖

    • 再次触发会清除掉旧的定时器,重新计时

      function debounce (fn,time){
        let timer
        return function(){
          if(timer){
            clearTimeout(timer)
          }
          timer=setTimeout(fn,time)
        }
      }
      
  • 函数节流

    • 函数在执行一次以后,在这次执行完毕之前,不会再次触发

      let flag=true
      function throttle(fn,time){
        return function(){
         if(!flag){
          return false
         }
          flage=false
          setTimeout(()=>{
            fn()
            flag=true
          },time)
        }
      }
      

14.jsonp解决跨域为什么用script而不用image

  • 新图像元素只要设置了src属性就会开始下载,兼容性好,缺点就是:只能发生GET请求,而且无法获取响应文本
  • jsonp的优势在于:可以直接访问响应文本,支持在浏览器和服务器之间的双向通信

15.如何判断一个属性是自身属性还是圆形属性

  • hasOwnProperty用于检查给定的属性是否存在当前实例对象中,而不是原型

    function(obj,pro){
      return !obj.hasOwnProperty(pro)&&pro in object
    }
    

16.csrf是什么

  • 跨站请求攻击
  • 攻击者盗用了用户的身份,发送恶意请求

如何防御

  • 提交验证码
  • refer check
  • token验证

17.手写事件订阅

class EventEmitter{
  constructor(){
    this.subs=Object.create(null)
  }
  //注册
  $on(eventType,handler){
    this.subs[eventType]=this.subs[eventType]||[]
    this.subs[eventType].forEach(handler=>{
      handler()
    })
  }
}

18.webpack优化

  • 优化loader,使用include缩小文件搜索范围
  • 使用DLLPlugin[动态链接库](大量复用模块的动态链接库只需要编译一次)
  • 使用happyPack(loader对文件转化操作分配给多个进程去并行处理)

19.node模块查找规则

  • require(’./find’)

    • 先找同名的js文件,再找同名文件夹
    • 如果找到文件夹,再找文件夹下的index.js
    • 如果没有找到index,js就在find文件夹下的package.js查找main的文件入口
    • 如果指定的入口文件不存在或者没有指定入口文件,就会报错
  • require(‘find’)

    • 假设是系统模块
    • 会在node_modules下找
    • 其他步骤和上述一样

20.async/defer/preload

  • defer/sync都告诉浏览器,可以在后台加载脚本的同时解析html。并在脚本加载完成以后执行,这样,脚本下载就不用阻碍dom的构建和渲染,用户在所有脚本加载完成之前都可以看到页面
  • defer比async要引入浏览器,他的执行在解析完成以后才开始执行,它处在DOMContentLoader事件之前
  • async脚本在他们加载完成以后就会开始执行,有可能会阻断dom的构建,通常设置了async的文件优先级较低
  • preload有较高的优先级,告诉浏览器尽快的加载他们

21.手写instanceof

  • instanceof 运算符用于测试构造函数的 prototype 属性是否出现在对象原型链中的任何位置。

    实现思路:

    首先 instanceof 左侧必须是对象, 才能找到它的原型链

    instanceof 右侧必须是函数, 函数才会prototype属性

    迭代 , 左侧对象的原型不等于右侧的 prototype时, 沿着原型链重新赋值左侧

示例: a instanceof B

检测a的原型链(__proto__)上是否有B.prototype,若有返回true,否则false

每次不断循环,拿出a的原型,然后再拿出原型指向的构造方法,然后判断一下是不是B。

如果A继承B,然后用B实例化a,然后a instanceof A

会返回true,因为沿着__proto__链一直上去。



function instance_of(L,R){    
    // 验证如果为基本数据类型,就直接返回false
    const baseType = ['string', 'number','boolean','undefined','symbol']
    if(baseType.includes(typeof(L))) { 
        return false 
    }
    
    let RP  = R.prototype;  //取 R 的显示原型
    L = L.__proto__;       //取 L 的隐式原型
    while(true){           // 无线循环的写法(也可以使 for(;;) )
        if(L === null){    //找到最顶层
            return false;
        }
        if(L === RP){       //严格相等
            return true;
        }
        L = L.__proto__;  //没找到继续向上一层原型链查找
    }
}

22.promise.all参数是什么,返回的是什么

  • 参数是一个数组,iterable一个可迭代对象,如 ArrayString

  • 返回也是一个数组(传入的数组是多个promise实例)

    • 1.如果传入的参数是一个空的可迭代对象,则返回一个已完成(already resolved)状态的 Promise
    • 2.如果传入的参数不包含任何 promise,则返回一个异步完成(asynchronously resolved) Promise。注意:Google Chrome 58 在这种情况下返回一个已完成(already resolved)状态的 Promise
    • 3.其它情况下返回一个处理中(pending)的Promise。这个返回的 promise 之后会在所有的 promise 都完成或有一个 promise 失败时异步地变为完成或失败。 见下方关于“Promise.all 的异步或同步”示例。返回值将会按照参数内的 promise 顺序排列,而不是由调用 promise 的完成顺序决定。
  • Promise.all方法的参数可以不是数组,但必须具有iterator接口,且返回的成员都是Promise实例

  • **Promise.all(iterable)** 方法返回一个 Promise 实例,此实例在 iterable 参数内所有的 promise 都“完成(resolved)”或参数中不包含 promise 时回调完成(resolve);如果参数中 promise 有一个失败(rejected),此实例回调失败(reject),失败原因的是第一个失败 promise 的结果。

    var promise1 = Promise.resolve(3);
    var promise2 = 42;
    var promise3 = new Promise(function(resolve, reject) {
      setTimeout(resolve, 100, 'foo');
    });
    
    Promise.all([promise1, promise2, promise3]).then(function(values) {
      console.log(values);
    });
    

    手写promise.all

    Promise.prototype.all = function(iterator){
     let promises = Array.from(iterator)
     let len = promises.length
     let conunt = 0
     let resultList = []
     return new Promise((resolve,reject)=>{
     promises.forEach((p,index)=>{
     Promise.resolve(p).then((result)=>{
     conunt++
     resultList[index]=result
     if (conunt == len) {
     resolve(resultList)
     }
     })
     })
     }).catch(e=>{
     reject(e)
     })
     }
    

23.哪些操作会引起重排

  • 页面初始化
  • 浏览器窗口改变尺寸
  • 元素位置,尺寸变化
  • 添加或者删除元素

24.数组都有哪些方法。foreach和map的区别

  • concat()合并数组
  • join()使用分隔符,将数组变成字符串返回
  • pop()删除最后一位,并返回删除的数据
  • shift()删除第一位,返回删除的数据
  • push()在数组尾部添加一位
  • unshift()在头部添加一位
  • reserve()反转数组
  • sort()数组排序
  • splice()删除指定位并替换,返回删除的数据
  • foreach()参数为回调函数,接收三个参数value,index,self没有返回值
  • map()回调函数返回数据,遍历组成新数组由map返回

25.es6新特性

  • let,const
  • 模板字符串
  • 解构赋值
  • for of /for in
  • 展开运算符
  • 箭头函数
  • super和extends

26.浏览器缓存

  • 强缓存
  • 协商缓存

27.json-server和mock怎么选择

  • json-server主要是搭建本地的数据接口,创建json文件,便于调试调用
  • mock主要是随机生成js数据

28.介绍一下this

  • (1)ES5非严格模式下,全局中的this和函数中的this都指向window
    (2)ES5严格模式下,全局中的this指向window,函数中的this指向undefined
    (3)对象中函数的this指向当前对象自身,属性中的this指向对象外this的指向(对象上下文环境中this的指向,原因是因为此时对象还没有创建完成,this还没有生成)
    (4)回调函数中的this,如果是直接执行的回调函数,this指向最外层的window;如果通过arguments直接使用参数执行函数,this指向执行当前函数的arguments;如果回调函数通过call/apply/bind重新指向了新的对象时,this就是这个重新指向的对象
    (5)事件中的this,addEventListener()侦听事件时,this指向事件的侦听对象(只要不适用箭头函数,就是侦听对象)e.currentTarget;IE8使用attach()侦听事件时,this指向window
    (6)ES6类中的this,普通方法中,this指向呗实例化的对象;静态方法中是当前类名,也是构造函数,静态方法中无法获取到实例化对象中的this
    (7)ES5面向兑现中的this,类中的this指向window或undefined;面向对象方法中的this指向执行该方法的实例化对象;面向对象静态方法中的this指向该类的类名
    (8)箭头函数中的this,this是箭头函数外this的指向
    (9)call,apply,bind,this指向绑定的参数对象,如果使用call,apply,bind带入的参数是null,将会把this重新指向window,

  • 实际上this的最终执行就是调用它的对象

    function a(){
     var user=张三;
     console.log(this.user);//underfined
     console.log(this);//window
    }
    a();//等同与window.a
    

29.手写一个观察者

class Dep{
 constructor(){
 this.subs = []
 }
 
 addSub(sub){
 if (sub && sub.update){
 this.subs.push(sub)
 }
 }
 
 notify(){
 this.subs.forEach((sub)=>{
 sub.update()
 })
 }
}
class Watcher(){
 constructor(){
 
 }
 
 update(){
 
 }
}
let dep = new Dep()
let watcher = new Wathcher()
dep.addSub(watcher)
dep.notify()

30.描述一个策略模式

  • 对象有某个行为,但是在不同的场景下,该行为有不同的实现算法

31.JS基本设计原则和常见的设计模式

设计原则

  • 单一职责(只做一件事)
  • 最少知识原则(尽可能的避免和其他实体的交互)
  • 开放-封闭原则(可以扩展,补课修改)

设计模式

  • 单例模式
    • 确保只有一个实例,并提供全局访问
  • 策略模式
    • 将算法的使用和算法的实现分离
  • 代理模式
    • 提供一个替身对象来控制对某个对象的访问
  • 迭代器模式
  • 发布-订阅模式
    • 也称为观察者模式,定义了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知
  • 命令模式
  • 组合模式
  • 模板方法模式
  • 享元模式
  • 职责链模式
  • 中介者模式
  • 装饰者模式
  • 状态模式
  • 适配器模式
  • 外观模式

32.数组去重

es6数组去重(set)

let a=[1,2,6,3,5,6,2,]
 a=new Set(a)

es5 for循环splice去重

function unique(arr){
 for(var x=0;x<arr.length;x++){
   for(var j=x+1;j<arr.length;j++){
     if(arr[x]==arr[j]){
       j--
     }
   }
   return arr
 }
}
console.log(unique([1,2,6,3,6,5,2,5]))

filter去重

function unique(arr){
 return arr.filter((item,index,arr)=>{
  return arr.indexOf(item,0)===index
 })
}

console.log(unquire([1,2,6,3,6,5,2,5]))

33.多种方法实现数组排序

sort

let arr = [1,7,4,2,9]
  arr.sort(function(a,b){
   return a-b
 })
 console.log(arr)

冒泡排序

let arr = [1,7,4,2,9]
 function bubble(arr){
  for(var x= 1; x< arr.length;x++){
  for (var y= 0;y<arr.length-x;y++){
     if (arr[y] > arr[y+1]){
       var temp = arr[y]
       arr[y] = arr[y+1]
       arr[y+1] = temp
    }
   }
 }
   return arr
 }
   console.log(bubble(arr))

选择排序

function select(arr){
  for (var x =0;x<arr.length;x++){
   let minIndex = x
   for (var y = x +1; y<arr.length; y++){
     if (arr[minIndex]> arr[y]){
       minIndex = y
     }
 }
     let temp = arr[x]
     arr[x] = arr[minIndex]
     arr[minIndex] = temp
 }
      return arr
 }
     console.warn(select(arr))

34.ajax,axios,fetch的区别

ajax

  • 传统ajax指的是XMLHttpRequest(XHR),最早出现的发送请求技术,
  • 核心使用XMLHttpRequest对象,多个请求之间如果有先后关系的话,就会出现回调地狱

axios

  • axios是一个基于Promise用于浏览器和nodejs的Http客户端,本质上是对于原生XHR的封装,只不过它是Promise的现实版本,符合最新的ES规范,它有如下特征
    • 从浏览器中创建XMLHttpRequest
    • 支持Promise API
    • 客户端支持防止csrf,
    • 提供了一些并发请求的接口
    • 从node.js创建http请求
    • 拦截请求和响应
    • 转换请求和响应数据
    • 取消请求
    • 自动转换JSON数据

fetch

  • fetch号称是ajax的替代品,在ES6中出现,使用了ES6中的promise对象
  • fetch是基于promise设计的,fetch的代码结果比ajax简单,参数有点想jQuery ajax
  • fetch不是ajax的进一步封装,是原生js,没有使用XMLHttpRequest对象

35.webpack和devserver实现原理,热更新是怎么实现的

webpack构建原理

  1. 初始化参数

从配置⽂件和shell语句中读取与合并参数,得到最终的参数

  1. 开始编译

从上⼀个得到的参数初始化Compiler参数,加载所有配置的插件,执⾏对象的

run⽅法开始编译

  1. 确定⼊⼝根据配置中的entry找出所有的⼊⼝⽂件

  2. 编译模块

从⼊⼝⽂件出发,调⽤所有配置的loader对模块进⾏编译,再找出改模块依赖

的模块,再递归本步骤知道所有⼊⼝依赖的⽂件都处理完毕

  1. 完成模块编译

使⽤loader编译完所有的模块后,得到了每个模块被编译后的最终内容以及他

们的依赖关系

  1. 输出资源

根据⼊⼝和模块之间的依赖关系,组装成⼀个个包含多个模块的chunk,再把

每⼀个chunk转换成⼀个单独的⽂件加⼊到输出列表,

  1. 输出完毕

在确定好输出内容以后,根据配置确定好输出的路径和⽂件名,把⽂件内容写

⼊到⽂件系统

devserver原理

devserver原理是启动一个express服务器,调用app.static方法

热更新

热更新是通过建立websocket实现服务端与客户端的双向通信,当我们的服务端发送变化时可以通知客户端进行页面进行刷新,实现的方式主要是两周iframe mode 和inline mode

babel原理

babel的转译过程也分为三个阶段:parsing、transforming、generating,

例: ES6-ES5

ES6代码输⼊ ==》 babylon进⾏解析 》 得到AST》 plugin⽤babel-traverse对

AST树进⾏遍历转译 》 得到新的AST树》 ⽤babel-generator通过AST树⽣成

ES5代码

36.高阶组件和mixin的区别

  • 高阶组件是通过将原组件包裹(wrapping)在容器组件(container component)里面的方式来组合(composes)使用原组件。高阶组件就是一个没有副作用的纯函数。

  • mixin广泛用于各种面向对象语言中,作用是为单继承语言创造一种李四多重继承的效果

    • 广义的mixin方法,就是用复制的方式将mixin对象的方法都挂载在原对象上,来实现对象的混入。类似ES6中的Object.assign()

      const mixin = function(obj, mixins){
          const newObj = obj;
          newObj.prototype = Object.create(obj.prototype);
      
          for(let prop in mixins){ // 遍历mixins的属性
              if(mixins.hasOwnPrototype(prop)){ // 判断是否为mixin的自身属性
                  newObj.prototype[prop] = mixins[prop]; // 赋值
              }
          }
      
          return newObj;
      }
      
      • 实质上就是把任意多个源对象拥有的自身可枚举属性复制给目标对象,然后返回目标对象
    • mixin缺陷

      • 破坏了原有组件的封装:可能会带来新的state和props。以为着会存在不可控的状态需要维护
      • 命名冲突:不同mixin中的命名不可知,所有非常容易发生冲突,需要花一定成本解决
      • 增加了复杂性,难以维护
  • 多个⻚⾯需要增加权限控制功能

37.前端性能优化有哪些

原则

  • 多使用内存、缓存
  • 减少CPU的计算、减少网络请求
  • 减少IO操作(硬盘读写)

加载资源优化

  • 静态资源的合并和压缩
  • 静态资源缓存
  • 使用cdn让资源加载更快

渲染优化

  • css放在head中,js放在body后

  • 图片懒加载

  • 减少dom操作

  • 事件节流

  • 非核心代码异步加载

    defer/async
    

38.观察者模式VS发布订阅模式

  • 所谓观察者模式。其实就是为了实现松耦合(loosely coupled)
  • 发布订阅模式,发布者和订阅者不是松耦合,而是完全解耦的

从表面看

  • 观察者模式里,只有两个角色–观察者+被观察者
  • 发布订阅模式,不仅仅只有发布者和订阅者,还有一个观察者

更深层次

  • 观察者和被观察者,是松耦合关系
  • 发布者和订阅者,完全不存在耦合

从使用上看

  • 观察者模式,多用于单个应用内部
  • 发布订阅模式,则更多的是一种跨应用的模式(cross-application pattern)比如我们经常用到的中间件

39.JS面向对象

封装

  • 把客观的事物封装成抽象的类,并且这些类可以把自己的数据和方法只让可信的类或对象操作

继承

  • 通过继承创建的新类成为’子类’或’派生类’。继承的过程,就是从一般到特殊的过程

多态

  • 对象的多功能,多方法,一个方法多种表现形式。同一个方法,面对不同的对象有不同的表现形式就叫做多态

40.高阶函数、纯函数、函数式编程

高阶函数

  • 把函数作为参数传入,这样的函数叫做高阶函数,函数式编程就是指这种高度抽象的编程范式

纯函数

  • 简单来讲,一个函数的返回结果只依赖于他的参数、并且在执行过程里面没有副作用

函数式编程

  • 函数式编程(FP)死通过编写纯函数,避免共享状态、可变水、副作用来构建软件的过程

41.在地址栏输入网址敲回车发生了什么

  • 在浏览器输入域名,通过DNS解析出IP地址

    • dns是因特网的一箱核心服务,它作为可以将域名和ip地址相互映射的一个分布式数据库
    • 具体解析流程
      • 用户发起请求
      • 操作系统把域名发送给本地区的域名服务器
      • 如果没有,到Root Server的域名服务器请求解析
      • 返回一个主域名(.com)的服务器地址
      • 本地的域名服务器再向主域名服务器发起请求
      • 返回Name Server域名服务器地址
      • 接下来的解析就由域名提供商的服务器来解析
      • Name Server(二级域名)服务器查询存储的域名和ip的映射关系表
      • 返回ip地址和一个过期时间,根据这个时间缓存到本地,解析结束
  • 通过解析出来的ip与服务器进行连接(三次握手)

    • 第一次:客户端像服务器端发送一个连接请求等待服务器确认(第一次握手由浏览器发起,告诉服务器我要发送请求)
    • 第二次:服务器端接收到请求并确认在回复一个指令(第二次握手由服务器端发起,告诉浏览器我准备接收了,你发送吧)
    • 第三次:客户端收到服务端的回复并确认返回(第三次握手由浏览器发起,告诉服务器,我马上发送,准备接收)
    • 通过三次握手建立了客户端与服务端之间的连接,现在可以请求和发送数据了
  • 发送http请求

  • 服务器返回一个http请求浏览器接受响应

  • 浏览器拿到响应文本后开始渲染

    • 根据HTML解析出DOM树

      • 根据 HTML 的内容,将标签按照结构解析成为 DOM 树 DOM树解析的过程是⼀个深度优先遍历。即先构建当前节点的所有⼦节点,再构建下⼀个兄弟结点。 在读取 HTML ⽂档,构建 DOM树的过程中,若遇到 script 标签,则 DOM 树的构建会暂停,直⾄脚本执⾏完毕。
    • 根据CSS解析生成css规则树

      • 解析 CSS 规则树时 js 执⾏将暂停,直⾄ CSS 规则树就绪。 浏览器在 CSS 规则树⽣成之前不会进⾏渲染。
    • 结合DOM树和CS规则树,生成渲染树

      • DOM 树和 CSS 规则树全部准备好了以后,浏览器才会开始构建渲染树。 精简 CSS 并可以加快 CSS规则树的构建,从⽽加快⻚⾯相应速度。
    • 根据渲染树计算出每一个节点的信息

      • 布局:通过渲染树中渲染对象的信息,计算出每⼀个渲染对象的位置和尺⼨

        回流:在布局完成后,发现了某个部分发⽣了变化影响了布局,那就需要倒回去重新渲染。

    • 根据计算好的信息绘制页面

      • 绘制阶段,系统会遍历呈现树,并调⽤呈现器的“paint”⽅法,将呈现器的内容显示在屏幕上。

        重绘:某个元素的背景颜⾊,⽂字颜⾊等,不影响元素周围或内部布局的属性,将只会引起浏览器的重绘。

        回流:某个元素的尺⼨发⽣了变化,则需重新计算渲染树,重新渲染。*

  • 数据传输完毕断开连接(四次挥手)

    • 第⼀次挥⼿:由浏览器发起,发送给服务器,我请求报⽂发送完了,你准备关闭吧;

      第⼆次挥⼿:由服务器发起,告诉浏览器,我接收完请求报⽂,我准备关闭,你也准备吧;

      第三次挥⼿:由服务器发起,告诉浏览器,我响应报⽂发送完毕,你准备关闭吧;

      第四次挥⼿:由浏览器发起,告诉服务器,我响应报⽂接收完毕,我准备关闭,你也准备吧

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值