前端-知识点整合(更新ing)

505 阅读23分钟

文章整合

new

工具

  • GrapQL
  • Immutability-helper | Immer
  • Lodash | Ramda
  • Redux-Saga | Rematch
  • css-in-js | styled-components

JS基础

Javascript 20年

8张 Javascript 思维导图

面试

面试指南

2020年前端面试复习必读精选文章

手写题

1.5年面经

进击的前端面试

手写

Promise

www.jianshu.com/p/327e38aec…

正则匹配.jpg或者.png图片

var reg=/\.(png|jpg)$/

function request(urls, maxNumber, callback)

要求编写函数实现,根据urls数组内的url地址进行并发网络请求,最大并发数maxNumber,当所有请求完毕后调用callback函数(已知请求网络的方法可以使用fetch api)

function multiRequest(urls, max) {
   return new Promise((resolve, reject) => {
       const requestRes = {}
       const len = urls.length //总大小
       let finshedCount = 0 //已完成
       let running = 0 // 正在请求中
       let index = 0

       function addQueue() {
           index++
           running += 1
           fetch(urls[index]).then(res => {
               requestRes[index - max] = res
           }).catch(err => {
               requestRes[index - max] = err
           }).finally(() => {
               running -= 1
               finshedCount += 1
               if (running < max && finshedCount < len) {
                   addQueue()
               }
               if (finshedCount == len) {
                   resolve(Array.from({ ...requestRes, length: len }))
               }

           })
           if (running < max && finshedCount < len) {
               addQueue()
           }
       }
       addQueue()

   })
}

bind,appply,call函数实现

  • this 的绑定顺序: new 绑定 > 显示绑定 > 隐式绑定 > 默认绑定
  • 第一个参数是指this;
  • call、apply会改变函数的this指向并执行函数,
  • bind只会改变函数的this指向并不会执行函数,返回新函数(闭包)
  • 原生的 bind 函数具有柯里化特征

Function.prototype.call = function(context) {
   if(typeof this !== "function"){
     throw new TypeError("Error")
   }
    context = context || window
    context.fn = this
    let param = [...arguments].slice(1)
    let res = context.fn(param)
    
    delete context.fn
    return res
}

Function.prototype.apply = function (context){
   if(typeof this !== "function"){
       throw new TypeError("Error")
   }
   context = context || window
   context.fn = this
   let res = context.fn()
   if(arguments[1]){
   	res = context.fn(...arguments[1])
   }
   
   delete context.fn
   return res
}


Function.prototype.bind = function (context) {
 if(typeof this !== "function"){
   throw new TypeError("Error")
 }
 const _this = this;
 const argus = [...arguments].slice(1);
 return function F(){
   //因为返回了一个函数,可以new F(),所以需要判断
   if(this instanceof F){
       return new _this(...argus,...arguments);
   }
   return _this.apply(context,argus.concat(...arguments));
 }
}

手写new的过程

  • 创建一个新的空的对象
  • 将构造函数的作用域赋给新对象(因此this就指向了这个新对象)
    • 所谓将构造函数的作用域赋给新对象,
    • 就是给这个新对象构造原型链,链接到构造函数的原型对象上,
    • 从而新对象就可以访问构造函数中的属性和方法
  • 执行构造函数中的代码(为这个新对象添加属性)
  • 如果这个函数有返回值,则返回;否则,就会默认返回新对象
function myNew() {
   // 构造函数就是我们传入的第一个参数
   var constrFn = Array.prototype.shift.call(arguments);
   var obj = Object.create(constrFn.prototype);
   var result = constrFn.apply(obj, arguments);
   return result instanceof Object? result : obj;
}

curry函数实现(函数柯里化)

function sum(...args1) {
 // 求和
 let x = args1.reduce((prev, next) => {return prev+next;})
 return function(...args2) {
   if (args2.length == 0) return x;
   // 求和
   let y = args2.reduce((prev, next) => {return prev+next;})
   return sum(x+y)
 }
}
// 测试
console.log(sum(1,2,2)(7)())

function sum2(a, b, c) {
 return a + b + c;
}

function curry(fn) {
 return function currify() {
   const args = Array.prototype.slice.call(arguments);
   return args.length >= fn.length ?
     fn.apply(null, args) :
   currify.bind(null, ...args);
 }
}

var currySum = curry(sum2);
console.log(currySum(1)(2)(3));

trottle(节流)

创建并返回一个像节流阀一样的函数,当重复调用函数的时候,至少每隔 wait毫秒调用一次该函数。对于想控制一些触发频率较高的事件有帮助。就是指连续触发事件但是在 n 秒中只执行一次函数

debounce(防抖)

触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间

发布订阅

var Event = (function(){
   var list = {},
         listen,
         trigger,
         remove;
         listen = function(key,fn){
           if(!list[key]) {
   			// 如果还没有订阅过此类消息,给该类消息创建一个缓存列表
               list[key] = [];
           }
           list[key].push(fn); // 订阅消息添加到缓存列表
       };
       trigger = function(){
           var key = Array.prototype.shift.call(arguments), // 取出消息类型名称
                fns = list[key]; // 取出该消息对应的回调函数的集合
   		// 如果没有订阅过该消息的话,则返回
           if(!fns || fns.length === 0) {
               return false;
           }
           for(var i = 0, fn; fn = fns[i++];) {
               fn.apply(this,arguments); // arguments 是发布消息时附送的参数
           }
       };
       remove = function(key,fn){
   		// 如果key对应的消息没有订阅过的话,则返回
           var fns = list[key];
   		// 如果没有传入具体的回调函数,表示需要取消key对应消息的所有订阅
           if(!fns) {
               return false;
           }
           if(!fn) {
               fns && (fns.length = 0);
           }else {
               for(var i = fns.length - 1; i >= 0; i--){
                   var _fn = fns[i];
                   if(_fn === fn) {
                       fns.splice(i,1);// 删除订阅者的回调函数
                   }
               }
           }
       };
       return {
           listen: listen,
           trigger: trigger,
           remove: remove
       }
})();
// 测试代码如下:
Event.listen("color",function(size) {
   console.log("尺码为:"+size); // 打印出尺码为42
});
Event.trigger("color",42);

数组扁平化

generator实现

数组去重

数组扁平+数组去重+排序编写一个程序将数组扁平化去并除其中重复部分数据,最终得到一个升序且不重复的数组


Array.prototype.mySelect=function(){
   console.log(this);
   return this.filter(function(value,index,arr){
    return arr.indexOf(value)===index;
   })
  }

实现文字环绕图片效果

url查询参数解析

const getParam = (name: string) => {
 let maps: any = {};
 let searchArr = [
   window.location.search,
   window.location.hash.split("?")[1] || "",
 ];
 for (let search of searchArr) {
   if (search.replace(/^\?*/, "")) {
     let param = querystring.parse(search.replace(/^\?*/, ""));
     maps = {
       ...maps,
       ...param,
     };
   }
 }
 return decodeURIComponent(maps[name] || "");
};

发布订阅模式,伪代码

深拷贝

模拟一个队列

EventEmitter(事件派发器)的实现

把setTimeout改造成Promise的sleep函数

// 函数签名
function sleep(second);

// 实现10秒之后打印“10”
sleep(5).then(i => { console.log(i)});

实现一个sleep函数

async function sleep(time){
// 这里是实现
return new Promise((res)=>{
  setTimeout(()=>{
    res()
  },time)
})
}
console.log(1)
await sleep(3000)
console.log(2)

图片懒加载

  
// onload是等所有的资源文件加载完毕以后再绑定事件
window.onload = function () {
  // 获取图片列表,即img标签列表
  var imgs = document.querySelectorAll("img");

  // 获取到浏览器顶部的距离
  function getTop(e) {
    return e.offsetTop;
  }

  // 懒加载实现
  function lazyload(imgs) {
    // 可视区域高度
    var h = window.innerHeight;
    //滚动区域高度
    var s = document.documentElement.scrollTop || document.body.scrollTop;
    for (var i = 0; i < imgs.length; i++) {
      //图片距离顶部的距离大于可视区域和滚动区域之和时懒加载
      if (h + s > getTop(imgs[i])) {
        // 真实情况是页面开始有2秒空白,所以使用setTimeout定时2s
        (function (i) {
          setTimeout(function () {
            // 不加立即执行函数i会等于9
            // 隐形加载图片或其他资源,
            //创建一个临时图片,这个图片在内存中不会到页面上去。实现隐形加载
            var temp = new Image();
            temp.src = imgs[i].getAttribute("data-src"); //只会请求一次
            // onload判断图片加载完毕,真是图片加载完毕,再赋值给dom节点
            temp.onload = function () {
              // 获取自定义属性data-src,用真图片替换假图片
              imgs[i].src = imgs[i].getAttribute("data-src");
            };
          }, 2000);
        })(i);
      }
    }
  }
  lazyload(imgs);

  // 滚屏函数
  window.onscroll = function () {
    lazyload(imgs);
  };
};

图片预加载

async function loadImage(src) {
  return new Promise((resolve) => {
    const img = new Image();
    const onload = () => resolve();
    img.onload = onload;
    img.onerror = onload;
    img.src = src;
  });
}

// 预加载所有图片
  for (let src of preLoadList) {
    await loadImage(src);
  }

概念

CDN(DNS)原理,CDN获取最近节点资源的算法是什么(ip下发)

  • cache、负载均衡、资源同步 通过在网络各处放置节点服务器所构成的在现有的互联网基础之上的一层智能虚拟网络,CDN系统能够实时地根据网络流量和各节点的连接、负载状况以及到用户的距离和响应时间等综合信息将用户的请求重新导向离用户最近的服务节点上。 其目的是使用户可就近取得所需内容,解决Internet网络拥挤的状况,提高用户访问网站的响应速度。

TCP三次握手四次挥手,tcp如何保证安全连接

互发ack包跟syn包,确认连接,建立通信,https则是在http基础上建立ssh加密层,主要是公私钥非对称加密,tcp连解建立以后浏览器与服务器相互交换公钥,同时浏览器会检验服务端的证书是否合法。确认完毕,双方传输,对传输内容使用公钥加密,并接受内容使用私钥解密

http在哪一层,tcp在哪一层,tcp与udp有啥区别?为什么tcp是可靠连接

dns查询过程,使用的协议

  • DNS 基于 UDP 的应用层协议。主要用途是将一个域名解析成 IP 地址,这个过程叫做域名解析
  • 主要查询方式:
    • 递归查询:主机到本地 DNS 服务器的查询
    • 迭代查询:本地 DNS 服务器向根服务器的查询
  • 查询过程:
    • 先去本地 hosts 中找对应地址
    • 再去域名解析器(缓存)中寻找映射关系
    • 找 DNS 服务器问是不是配置在其下的
    • DNS 服务器有没有缓存它的映射关系
    • DNS 根据配置去找到根服务器或者上一级 DNS 服务器

http、https

状态码/重定向: 如果发生了502应该怎么排查错误,304

https原理,https加密,与http的区别

请求头常见字段都有哪些。only字段

https对称加密和非对称加密:非对称加密的加解密效率是非常低的,只作用在证书验证阶段,对称加密在数据传输阶段

浏览器缓存(强缓存/协商缓存)

  • 强缓存(200)
    • Expires (http1.0),绝对时间
    • Cache-Control(http1.1)相对时间
      • private(默认值) | public | no-cache | no-store | max-age
      • 内存缓存(from memory cache):【js和图片等文件解析执行后存入】快速读取和时效性
        • 快速读取:内存缓存会将编译解析后的文件,直接存入该进程的内存中,占据该进程一定的内存资源,以方便下次运行使用时的快速读取。
        • 时效性:一旦该进程关闭,则该进程的内存则会清空。
      • 硬盘缓存(from disk cache):【css文件】将缓存写入硬盘文件中,读取时需要对硬盘文件进行I/O操作,然后重新解析,读取复杂,速度比内存缓存慢

image.png

  • 协商缓存(304)
    • Etag / If-None-Match 和 Last-Modified / If-Modified-Since
    • Etag / If-None-Match 的优先级高。

跨域

协议、域名、端口三者之间任意一个与当前页面url不同即为跨域

document.domain,JSONP,CORS(credentials ,Access-Control-Allow-Origin,Access-Control-Allow-Credentials)

js的基本类型,typeof |instanceof |prototype.toString 的区别

  • 原始类型(栈):undefined 、null、boolean 、number 、string 、symbol
  • 引用类型(堆):Object

输入URL后发生什么

  • 浏览器如何构建和渲染页面:
    • HTML解析出DOM Tree
    • CSS解析出Style Rules
    • 将二者关联生成Render Tree:布局树->分层树->分层树图块化
    • Layout/Flow 根据Render Tree计算每个节点的信息,将渲染树节点合成
    • Painting 根据计算好的信息绘制整个页面
  • 底层的渲染管线

重排(Relayout/Reflow)重绘(Repaint)

  • 避免方法:
    • js 尽量减少对样式的操作,能用 css 完成的就用 css
    • 对 dom 操作尽量少,能用 createDocumentFragment 的地方尽量用
      • 因为DOM碎片存在于内存中,并不在DOM中,所以将子元素插入到文档片段中时不会引起页面回流(对元素位置和几何上的计算),因此使用DocumentFragment可以起到性能优化的作用。
    • 如果必须要用 js 操作样式,能合并尽量合并不要分多次操作
    • 读写分离
    • resize 事件 最好加上防抖,能尽量少触发就少触发
    • 加载图片的时候,提前写好宽高
    • GPU 加速通:Canvas2D,布局合成, CSS3转换(transitions),CSS3 3D变换(transforms),WebGL和视频(video)

进程线程,操作系统中进程和线程怎么通信

  • 进程process: 是系统进行资源分配和调度的基本单位,是操作系统结构的基础,是线程的容器
  • 线程:是操作系统能够运算调度的最小单位,
  • 区别
    • 进程有自己独立的地址空间,线程共享进程的地址空间
    • 进程之间的资源是独立的,线程共享本进程的资源
  • 进程间通信:
    • 管道(包括管道和命名管道) 内存中类似于文件的模型,多进程可读写
    • 系统IPC(包括消息队列、信号量、信号、共享内存等)
    • 套接字socket 不同主机上的进程通信方式
  • 线程通信
    • 共享内存
    • 管道
  • 打开一个页面至少需要:1 个网络进程、1 个浏览器进程、1 个 GPU 进程以及 1 个渲染进程

事件循环 eventLoop

  • 为了js的单线程机制,处理并发的任务

  • 栈:先进后出,函数调用形成一个栈帧

  • 任务队列:先进先出

  • 每个线程都有他自己的事件环,浏览器也拥有自己的事件环;事件环是一种运行时机制,它像个钟表一样,每滴答一下,就去看看stack里有没有事需要处理,没有的话就去事件队列(Event Queue)看看有没有事做。每个eventloop都有一个微任务队列,微任务最初是被放到微任务队列而不是任务队列。

  • requestAnimationFrame 和setTimeout 、setInterval的关系?

执行上下文,变量提升、词法作用域、动态作用域

函数调用形成一个栈帧;

在进入执行上下文时,首先会处理函数声明,其次会处理变量声明,如果变量名称跟已经声明的形式参数或函数相同,则变量声明不会干扰已经存在的这类属性。

  • 执行栈,也叫调用栈,具有 后进先出 结构,用于存储在代码执行期间创建的所有执行上下文
  • 执行上下文的类型:
    • 全局执行上下文:默认的,最基础的执行上下文。不在任何函数中的代码都位于全局执行上下文中,执行 JS 程序,首先进入全局环境,全局环境只有一个并在关闭窗口时弹出。
    • 函数执行上下文:每次调用函数时,都会为该函数创建一个新的执行上下文
    • Eval函数执行上下文:运行在eval函数中的代码也获得了自己的执行上下文
  • 执行上下文分两个阶段创建:
    • 创建阶段:确定this的值(This Binding)->(词法环境)组件被创建 -> VariableEnvironment(变量环境)组件被创建。
    • 执行阶段:完成对所有变量的分配,最后执行代码
  1. 全局上下文的变量对象初始化是全局对象
  2. 函数上下文的变量对象初始化只包括 Arguments 对象
  3. 在进入执行上下文时会给变量对象添加形参、函数声明、变量声明等初始的属性值
  4. 在代码执行阶段,会再次修改变量对象的属性值

js闭包概念,应用(防抖,节流),闭包解决了什么问题

  • 闭包:有权访问另一个函数作用域中的变量的函数
  • 主要应用闭包场合主要是为了:保护里边的变量不受外界的影响,形成一个私有作用域
  • 闭包有三个特性:
    • 函数嵌套函数
    • 函数内部可以引用外部的参数和变量
    • 参数和变量不会被垃圾回收机制回收

原型链

  • 每个对象都有 proto 属性,此属性指向该对象的构造函数的原型。而上游的原型对象也有一个__proto__,这样就形成了原型链

commonJS和es6模块化的区别

  • CommonJS的require语法是同步的
  • CommonJS模块输出的是一个值的拷贝,ES6 模块输出的是值的引用;
  • CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。

前端性能优化

  • 减少请求资源大小或者次数
  • 减少对DOM操作
  • 基于script标签下载js文件时,可以使用defer或者async来异步加载
  • webpack优化: 使用高版本的 Webpack、多线程/多实例构建、缩小打包作用域、充分利用缓存提升二次构建速度、DLL
    • 分割配置文件
    • 生产环境:
      • 代码分离:入口起点:配置多入口,输出多个chunk; CommonsChunkPlugin去重;路由组件动态导入
      • 使用:new PreloadWebpackPlugin{rel:"prefetch"} 用于加速下一次操作
      • 压缩代码:UglifyJsPlugin和ParallelUglifyPlugin来压缩JS文件,利用cssnano(css-loader?minimize)来压缩css
    • 开发环境:
      • Dlls: 优化构建速度,将更改不频繁的代码进行单独编译

[webpack]

  • happy-pack/thread-loader:通过创建的线程池分模块构建,处理后重新传到主线程
  • CommonsChunkPlugin:分包策略,webpack 4 废除了 CommonsChunkPlugin 引入了 optimization.splitChunks。如果你的 mode 是 production,那么 webpack4 就会自动开启 Code Splitting
    • 新的 chunk 是否被共享或者是来自 node_modules 的模块
    • 新的 chunk 体积在压缩之前是否大于 30kb
    • 按需加载 chunk 的并发请求数量小于等于 5 个
    • 页面初始加载时的并发请求数量小于等于 3 个
  • 利用optimization.splitChunks 抽离 echarts
  • 用 CDN 的方式引入插件或者 UI 组件库

node多进程,进程中怎么通信,可以开启多线程吗

cookie

cookie有哪些属性(httponly/secure); cookie,session,localstorage,sessionstorage有什么区别; 怎么禁止js访问cookie

rem和em

常见的网络攻击:xss攻击和CSRF

  • XSS( 跨站脚本攻击 )
    • 将script,& < > " ' /等危险字符做过滤和转义替换(htmlspecialchars),同时尽量避免使用innerHTML,document.write,outerHTML,eval等方法,用安全性更高的textContent,setAttribute等方法做替代;
    • 开启CSP防护( Content-Security-Policy )。
    • 设置HttpOnly,
  • CSRF( 跨站请求伪造 )
    • SameSite Cookie
    • SCRF Token
    • 同源检测:Referer和Origin。但302重定向的时候,http请求不会携带Origin字段,而Referer字段会受到Referer Policy规则的限制而不发送。
    • 增加二次验证

defer和async

  • defer和async只适用于外联脚本
  • defer脚本会在DOMContentLoaded和load事件之前执行:脚本下载完且文档解析完执行
  • async会在load事件之前执行,但并不能确保与DOMContentLoaded的执行先后顺序:脚本下载完执行脚本

OSI网络七层模型

应用层(为用户的应用进程提供服务),表示层(将数据格式转换为标准格式),会话层,传输层(端口号),网络层(TCP/IP), 数据链路层(规定信号的传输格式),物理层(网线,无线电波,光)

设计模式

  • 单例模式:把我们的代码封装在一个起来,只是暴露一个入口,从而避免全部变量的污染(例如遮罩层)
  • 发布订阅模式:当对象状态发生改变时,会自动通知已经订阅过的对象(例如商城)

BOM和DOM的区别

  • DOM:文档对象模型(Document Object Model,简称DOM),描述了处理网页内容的方法和接口。最根本对象是document(window.document)。
    • e.currentTarget始终是监听事件者,即 直接调用addEventlistener那个节点
    • e.target是事件的真正发出者, 即 触发事件的节点,在click事件中就是被点击的节点。
  • BOM:浏览器对象模型(Browser Object Model),描述了与浏览器进行交互的方法和接口。由navigator、history、screen、location、window五个对象组成的,最根本对象是window。

web-push原理

是发布订阅模式,浏览器授权,去订阅服务器的消息通知,然后接收通知

判断变量是数组

1. var isArray = value instanceof Array;
2. Array.isArray(value) //ES5
3. Object.prototype.toString.call(vaule) == “[object Array]”

CSS

JS动画与CSS动画(requestAnimationFrame 和 settimeout)

  • requestAnimationFrame受系统的绘制频率影响,即屏幕分辨率 和 屏幕尺寸
  • setTimeout 与任务队列和页面渲染有关
  • requestAnimationFrame 会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率,一般来说,这个频率为每秒60帧。
  • 在隐藏或不可见的元素中,requestAnimationFrame将不会进行重绘或回流,这当然就意味着更少的的cpu,gpu和内存使用量。

css选择器

:last-child /* 选择元素最后一个孩子 /
:first-child / 选择元素第一个孩子 /
:nth-child(1) / 按照第几个孩子给它设置样式 /
:nth-child(even) / 按照偶数 /
:nth-child(odd)  / 按照奇数 /
:disabled / 选择每个禁用的E元素 /
:checked / 选择每个被选中的E元素 /
:not(selector) / 选择非 selector 元素的每个元素 /
::selection / 选择被用户选取的元素部分
伪元素:
::before {}  选择器在被选元素的前面插入内容和定义css,使用 content 属性来指定要插入的内容。
::after {} 选择器在被选元素的后面插入内容和定义css,使用 content 属性来指定要插入的内容。
:first-letter   选择该元素内容的首字母
:first-line 选择该元素内容的首行
::selection 选择被用户选取的元素部分

css权重问题(问得很细)

  • !important > 行内样式 > ID选择器 > 类选择器 | 属性选择器 | 伪类选择器 > 元素选择器
  • 权重相同时,CSS遵循就近原则。也就是说靠近元素的样式具有最大的优先级,或者说排在最后的样式优先级最大。

css提升性能的方式

  • fast-sass-loader: sass-loader 最大的问题是没有去重,而fast去重了

层叠等级

盒模型

  • box-sizing:content-box(标准盒模型:content_width)|border-box(IE盒模型:border + padding + content_width)
  • 获取盒子大小:window.getComputedStyle();offsetwidth();

position有哪些属性

实现任意角度扇形, 实现一个三角形,Css画圆有哪些方式

//三角形
.triangle {
  width: 0;
  height: 0;
  border: 100px solid transparent;
  border-bottom: 100px solid red;
}
//梯形
.trapezoid {
  width: 50px;
  height: 50px;
  border: 50px solid transparent;
  border-bottom: 50px solid red;
}
//扇形
.sector{
  transform: rotate(30deg) skewY(0deg);
}

三栏布局

css动画旋转:transform

translate():元素从其当前位置移动,根据给定的 left(x 坐标) 和 top(y 坐标) 位置参数。 + transform: translate(50px, 100px);
rotate():元素顺时针旋转给定的角度。若为负值,元素将逆时针旋转。transform: rotate(30deg);
scale()·:元素的尺寸会增加或减少,根据给定的宽度(X 轴)和高度(Y 轴)参数,也可以一个值(宽高)。transform: scale(2,4);
skew():元素翻转给定的角度,根据给定的水平线(X 轴)和垂直线(Y 轴)参数。transform: skew(30deg, 20deg);
matrix(): 把所有 2D 转换方法组合在一起,需要六个参数,包含数学函数,允许您:旋转、缩放、移动以及倾斜元素。
transform:matrix(0.866,0.5,-0.5,0.866,0,0);

filter:滤镜

React

虚拟dom, diff算法原理 复杂度O(n)

通过diff策略,虚拟DOM在比较时只比较同层次节点,将算法从传统的循环递归O(n^3)简化为O(n)

双缓冲技术:current & workinProgress ,在workInProgress上进行更新操作,等更新完毕之后,再把current指针指向workInProgress,从而丢弃旧的Fiber Tree

遍历完Fiber树之后,通过Diff算法,可以产出 EffectList,给commit阶段使用。deleteChild 删除子节点并不是真的删除这个对象,而是通过 firstEffect、lastEffect、nextEffect 属性来维护一个 EffectList(链表结构),通过 effectTag 标记当前删除操作

  • React Fiber
    • 每个 Fiber的 child 指向其第一个孩子节点,没有孩子节点则为 null
    • 每个 Fiber的 sibling 指向其下一个兄弟节点,没有则为 null
    • 每个 Fiber的 return 指向其父节点
    • 每个 Fiber有一个 index 属性表示其在兄弟节点中的排序
    • 每个 Fiber的 stateNode 指向其原生节点
    • 每个 Fiber有一个 key 属性
  • React diff算法策略
    • 针对树结构(tree diff):分层求异,对UI层的DOM节点跨层级的操作进行忽略。(数量少)
    • 针对组件结构(component diff):分组件求异,相同类生成相似树形结构、不同类生成不同树形结构,对component diff进行优化
    • 针对元素结构(element-diff):对于同一层级的一组节点,使用具有唯一性key
  • 在开发时,尽量保持稳定的DOM结构、避免将最后一个节点移动到列表首部、避免节点数量过大或更新过于频繁,能够优化渲染性能。

react渲染机制

  • 如何渲染的:调用render()函数构建一棵Dom树,
  • 怎么重新渲染的:在state/props发生改变的时候,render()函数会被再次调用渲染出另外一棵树,重新渲染所有的节点,构造出新的虚拟Dom tree跟原来的Dom tree用Diff算法进行比较,找到需要更新的地方批量改动,再渲染到真实的DOM上,由于这样做就减少了对Dom的频繁操作,从而提升的性能。

react中hooks的理解

React Hooks解决的问题是状态共享(状态逻辑复用),只共享数据处理逻辑,不会共享数据本身。不会产生 JSX 嵌套地狱问题

  • 多个状态不会产生嵌套,写法还是平铺的(renderProps 可以通过 compose 解决,可不但使用略为繁琐,而且因为强制封装一个新对象而增加了实体数量)。
  • Hooks 可以引用其他 Hooks。
  • 更容易将组件的 UI 与状态分离。

HOC(高阶组件)

image.png

axios怎么实现的,axios,ajax及fetch的区别

  • axios 原理还是属于 XMLHttpRequest, 利用Promise对结果进行处理
  • axios 可以在node.js中使用,提供了并发请求的接口,支持Promise API
  • fetch号称是ajax的替代品,它的API是基于Promise设计的,旧版本的浏览器不支持Promise,需要使用polyfill es6-promise
var Axios = {
    get: function(url) {
        return new Promise((resolve, reject) => {
            var xhr = new XMLHttpRequest();
            xhr.open('GET', url, true);
            xhr.onreadystatechange = function() {
                // readyState == 4说明请求已完成
                if (xhr.readyState == 4 && xhr.status == 200) {
                    // 从服务器获得数据
                    resolve(xhr.responseText)
                }
            };
            xhr.send();
        })
    },
}

historyAPI: History和Hash模式

react如何进行性能优化

  • function component + redux、
  • immutable、
  • React.PureComponent, React.memo
    • React.PureComponent中以浅层对比 prop 和 state 的方式来实现了shouldComponentUpdate()
    • React.memo适用于函数组件,但不适用于 class 组件
  • shouldComponentUpdate
    • 不建议在 shouldComponentUpdate() 中进行深层比较或使用 JSON.stringify()。这样非常影响效率,且会损害性能。
    • 返回 false 并不会阻止子组件在 state 更改时重新渲染
  • 使用key
  • 代码分割,减少主js的体积

算法

递归

层次遍历一个树

js实现36进制

function getNums36() {
  var nums36 = [];
  for (var i = 0; i < 36; i++) {
    if (i >= 0 && i <= 9) {
      nums36.push(i);
    } else {
      nums36.push(String.fromCharCode(i + 87));
    }
  }
  return nums36;
}

//十进制数转成36进制
function scale36(n) {
  var arr = [];
  var nums36 = getNums36();
  while (n) {
    var res = n % 36;
    //作为下标,对应的36进制数,转换成
    arr.unshift(nums36[res]);
    //去掉个位
    n = parseInt(n / 36);
  }
  return arr.join("");
}

矩阵 , 链表

树的遍历有几种方式,实现层次遍历(广度优先)

  • 广度优先(BFS):横向优先搜索
  • 深度优先(DFS)

判断对称二叉树

翻转二叉树

求二叉树的公共祖先节点

二叉树的右视图

二叉树路径总和(leetcode 112)

给定一个二叉树,返回所有从根结点到叶子结点的路径

function allPath(root) {
  var res = [];
  if (!root) return res;

  function getPath(root, arr) {
    var a = [...arr];
    if (root) {
      a.push(root.val);
      if (!root.left && !root.right) {
        res.push(a);
      } else if (root.left) {
        getPath(root.left, a);
      } else if (root.right) {
        getPath(root.right, a);
      }
    }
  }
  getPath(root, []);
  return res;
}

红黑树

两个有序链表和并成一个有序链表

function ListNode(val) {
      this.val = val;
      this.next = null;
}
/**
 * @param {ListNode} l1
 * @param {ListNode} l2
 * @return {ListNode}
 * 功能:合并两个有序链表
 * 解答:使用双指针的方法进行遍历
 */
var mergeTwoLists_2 = function(l1, l2) {
    //不需要特意判断l1、l2为空
    let head = new ListNode(0); //创建一个含有头结点的链表,随便指一个值,其第二个节点开始保存结果
    let pre = head; //遍历结果的指针,总指向最后一个节点
    while(l2 && l1){ //都不为null(空)时
        if(l1.val > l2.val){ //l1对应的值大于l2
            pre.next = l2;
            l2 = l2.next; //l2往后遍历
        }else{ //小于或者等于
            pre.next = l1;
            l1 = l1.next;
        }
        pre = pre.next;  //pre更新
    }
    //将没有比较过的l1或者l2添加到尾部
    pre.next = l1? l1: l2;//初始的时候不需要特意判断l1、l2为空,因为有一个为空会跳过while循环
    return head.next; //头结点是新建的节点,返回时,不需要该点
};

找出两个链表的交叉点

/**
 * @param {character[][]} board
 * @param {string} word
 * @return {boolean}
 */
var exist = function(board, word) {
    var row = board.length;
    var col = board[0].length;

    var dfs = function(i,j,board,word,index){
        if(i < 0 || i >= row || j < 0 || j > col || board[i][j] !== word[index]) return false; // 判断不符合条件
        if(index === word.length - 1) return true;  // word遍历完了
        var tmp = board[i][j];  // 记录到board的值
        board[i][j] = '-'      // 锁上,因为后续的递归是4个方向上的,无法保证上一个方向的值
        var res =  dfs(i - 1,j,board,word,index + 1) || dfs(i + 1,j,board,word,index + 1) || dfs(i,j - 1,board,word,index + 1) || dfs(i,j + 1,board,word,index + 1);
        board[i][j] = tmp;   // 恢复现场
        return res;
        
    }

    // 遍历整个board,找到初始位置点
    for(var i = 0;i < row; i++){
        for(var j = 0; j < col; j++){
            if(dfs(i,j,board,word,0)) return true;
        }
    }
    // 没找到
    return false
};

如何判断一个单链式有环

64匹马,8个赛道 决出最快的四匹马(挺有趣的题目,我画了个矩阵给面试官看)

找出sum大于等于target的最短连续数组的长度 要通过所有测试数据

三色龙(r,g,b) 逻辑题:每两只变色龙相遇会变成另一种,这三种满足什么数量关系可以最终全部变成一种颜色的

快速排序:找到数组中第K大的元素

排序稳定性

  • 稳定排序:冒泡 插入 归并
  • 快排的原理:分治思想,一个指针从前往后,一个指针从后往前,然后后一个指针换到前一个位置,但是这个指针又是往另一方向走的
  • 快排时间复杂度:O(nlogn),最坏和冒泡排序一样是 O(n2),空间复杂度O(logn)。
function sortQuick(arr, left, right) {
  var cur = arr[left];
  while (left < right) {
    while (left < right && arr[right] <= cur) {
      right--;
    }
    arr[left] = arr[right];
    while (left < right && arr[left] >= cur) {
      left++;
    }
    arr[right] = arr[left];
  }
  arr[left] = cur;
  return left;
}
function sort(arr, low, high, k) {
  if (low < high) {
    var index = sortQuick(arr, low, high);
    if (index - low + 1 == k) {
      return index;
    } else if (index - low + 1 > k) {
      sort(arr, index + 1, high, k);
    } else {
      sort(arr, low, index - 1, k);
    }
  }
}

数组操作

reduce(),filter(),shift(),

其他

  • 通用型的业务问题和过往的项目经历
    • web:用户权限控制(动态路由加载),跨域携带cookie,未处理消息提示,状态锁,编辑器修改源码,正则匹配批量替换,
    • h5:tab切换记录滚动,兼容ios,同一个页面只有一个正在播放视频,图片懒加载
  • 项目中的难点

[] == false

输入框,输入淘宝,对应提示信息淘宝网,淘宝会员,怎么尽快获取这个信息,有没有不发请求的实现方式。

v8引擎的编译原理,git的哈希对比算法,