腾讯云面试总结

手写深拷贝?

  • 递归、判断类型、检查环(也叫循环引用)、需要忽略原型
//用JSON方式实现: 使用JSON.stringify()以及JSON.parse()
//它是不可以拷贝 undefined , function, RegExp 等等类型的
var obj = {
  a:1,
  b:2,
  c:3
}

var objString = JSON.stringify(obj);
var object = JSON.parse(objString);

//Object.assign(target,source):只能实现一层对象的深拷贝,支持不了对象嵌套
var obj1 = {
  a:1,b:2,c:3
}

var obj2 = Object.assign({},obj1);

//递归拷贝
function deepClone(target){
  let res;
  if(typeof tartget === 'object'){
    if(Array.isArray(target)){
      res = [];
      for(let i in target){
        res.push(deepClone(target[i]));
      }
    }else if(target === null || target === undefined){
      res = target;
    }else{
      res = {};
      for(let i in target){
        res[i] = deepClone(target[i]);
      }
    }
  }else{
    res = target;
  }
  return res;
}

手写节流函数、手写防抖函数

//节流函数:一段时间执行一次之后,就不执行第二次
//注意,有些地方认为节流函数不是立刻执行的,而是在冷却时间末尾执行的(相当于施法有吟唱时间),那样说也是对的
function throttle(fn,delay){
  let canUse = true;
  return function(){
    if(canUse){
      fn.apply.call(this,arguments);
      canUse = false;
      setTimeout(()=>canUse=true,delay);
    }
  }
}
const throttled = throttle(()=>console.log('hi'));
throttled();

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

手写斐波那契数列并优化

  • 斐波那契数列:0, 1, 1, 2, 3, 5, 8, 13, 21, …
  • 在种子数字 0 和 1 之后,后续的每一个数字都是前面两个数字之和
//使用递归计算大数字时,性能会非常低
function fibonacci(n){
  if(n === 0 || n === 1) return n;
  return fibonacci(n-1)+fibonacci(n-2);
}

//对中间求得的变量值,进行存储的话,就会大大减少函数被调用的次数
le fibonacci1 = function(){
  let arr = [0,1];
  return function(n){
    let result = arr[n];
    if(typeof result !== 'number'){
      result = fibonacci1(n-1) + fibonacci1(n-2);
      arr[n] = result;
    }
    return result;
  }
}();

//递推法
function fibonacci2(n){
  let current = 0;
  let next = 1;
  let temp;
  for(let i = 0 ; i < n; i++){
    temp = current;
    current = next;
    next += temp;
  }
  console.log(`fibonacci(${n}, ${next}, ${current + next})`);
  return current;
}

function fibonacci3(n) {
  let current = 0;
  let next = 1;
  for(let i = 0; i < n; i++) {
    [current, next] = [next, current + next];
  }
  return current;
}

//尾调用优化:在ES6规范中,有一个尾调用优化,可以实现高效的尾递归方案 ,ES6的尾调用优化只在严格模式下开启,正常模式是无效的
//尾调用:某个函数的最后一步是调用另一个函数
'use strict';
function fib(n,current = 0,next = 1){
  if(n === 0) return 0;
  if(n === next) return next;
  return fib(n-1,next,current+next);
}

手写flat

//先不考虑深度问题,实现方式如下:
var flat = function(arr){
  let res = [],
    flatMap = (arr)=>{
      arr.map((element,index,array)=>{
        if(Object.prototype.toString.call(element).slice(8,-1) === 'Array'){
          flatMap(element);
        }else{
          res.push(element);
        }
      })
    }

  flatMap(arr);
  return res;
}

//深度实现
var flat1 = function(arr,depth){
  let res = [],depthArg = depth || 1,
      depthNum = 0,
      flatMap = (arr)=>{
        arr.map((element,index,array)=>{
          if(Object.prototype.toString.call(element).slice(8,-1) === 'Array'){
            if(depthNum < depthArg){
              depthNum++;
              flatMap(element);
            }else{
              res.push(element);
            }
          }else{
            res.push(element);
            if(index === array.length - 1) depthNum = 0;
          }
        })
      }
  flatMap(arr);
  return res;
}

手写bind

//先在函数原型上建立一个名为bind1的函数:

Function.prototype.bind1 = function(){
  //将参数拆解为数组
  const args = Array.prototype.slice.call(arguments);
  //获取this(数组的第一项)
  const t = args.shift();
  //找到fn1.bind(...)中的this,赋给self
  const self = this;
  //处理返回的函数
  return function(){
    return self.apply(t,args);
  }
}


function fn(a,b,c){
  console.log('this', this); // { x: 100 }
  console.log(a, b, c); // 10 20 30
  return 'this is fn';
}

const fn1 = fn.bind1({ x: 100 }, 10, 20, 30);
const res = fn1();
console.log(res); // this is fn1

http2和http1.1的区别?

  • HTTP1.0和HTTP1.1的区别
  1. 缓存处理,在HTTP1.0中主要使用header里的If-Modified-Since,Expires来做为缓存判断的标准,HTTP1.1则引入了更多的缓存控制策略例如Entity tag,If-Unmodified-Since, If-Match, If-None-Match等更多可供选择的缓存头来控制缓存策略。
  2. 带宽优化及网络连接的使用,HTTP1.0中,存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能,HTTP1.1则在请求头引入了range头域,它允许只请求资源的某个部分,即返回码是206(Partial Content),这样就方便了开发者自由的选择以便于充分利用带宽和连接
  3. 错误通知的管理,在HTTP1.1中新增了24个错误状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除
  4. Host头处理,在HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。HTTP1.1的请求消息和响应消息都应支持Host头域,且请求消息中如果没有Host头域会报告一个错误(400 Bad Request)
  5. 长连接,HTTP 1.1支持长连接(PersistentConnection)和请求的流水线(Pipelining)处理,在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟,在HTTP1.1中默认开启Connection: keep-alive,一定程度上弥补了HTTP1.0每次请求都要创建连接的缺点
  • HTTPS与HTTP的区别
  1. HTTPS协议需要到CA申请证书,一般免费证书很少,需要交费
  2. HTTP协议运行在TCP之上,所有传输的内容都是明文,HTTPS运行在SSL/TLS之上,SSL/TLS运行在TCP之上,所有传输的内容都经过加密的
  3. HTTP和HTTPS使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443
  4. HTTPS可以有效的防止运营商劫持,解决了防劫持的一个大问题
  • http2和http1.1的区别
  1. 新的二进制格式(Binary Format),HTTP1.x的解析是基于文本。基于文本协议的格式解析存在天然缺陷,文本的表现形式有多样性,要做到健壮性考虑的场景必然很多,二进制则不同,只认0和1的组合。基于这种考虑HTTP2.0的协议解析决定采用二进制格式,实现方便且健壮
  2. 多路复用(MultiPlexing),即连接共享,即每一个request都是是用作连接共享机制的。一个request对应一个id,这样一个连接上可以有多个request,每个连接的request可以随机的混杂在一起,接收方可以根据request的 id将request再归属到各自不同的服务端请求里面
  3. header压缩,如上文中所言,对前面提到过HTTP1.x的header带有大量信息,而且每次都要重复发送,HTTP2.0使用encoder来减少需要传输的header大小,通讯双方各自cache一份header fields表,既避免了重复header的传输,又减小了需要传输的大小
  4. 服务端推送(server push),同SPDY一样,HTTP2.0也具有server push功能

tcp和udp的区别?

  • 1.基于连接与无连接;
  • 2.对系统资源的要求(TCP较多,UDP少)
  • 3.UDP程序结构较简单
  • 4.流模式与数据报模式
  • 5.TCP保证数据正确性,UDP可能丢包,TCP保证数据顺序,UDP不保证。

TCP三次握手过程

  • 第一次握手:主机A通过向主机B 发送一个含有同步序列号的标志位的数据段给主机B,向主机B 请求建立连接,通过这个数据段, 主机A告诉主机B 两件事:我想要和你通信;你可以用哪个序列号作为起始数据段来回应我。
  • 第二次握手:主机B 收到主机A的请求后,用一个带有确认应答(ACK)和同步序列号(SYN)标志位的数据段响应主机A,也告诉主机A两件事:我已经收到你的请求了,你可以传输数据了;你要用那个序列号作为起始数据段来回应我
  • 第三次握手:主机A收到这个数据段后,再发送一个确认应答,确认已收到主机B 的数据段:"我已收到回复,我现在要开始传输实际数据了,这样3次握手就完成了,主机A和主机B 就可以传输数据了。

3次握手的特点

没有应用层的数据 ,SYN这个标志位只有在TCP建立连接时才会被置1 ,握手完成后SYN标志位被置0

TCP断开进行四次握手

第一次: 当主机A完成数据传输后,将控制位FIN置1,提出停止TCP连接的请求 ;
第二次: 主机B收到FIN后对其作出响应,确认这一方向上的TCP连接将关闭,将ACK置1;
第三次: 由B 端再提出反方向的关闭请求,将FIN置1 ;
第四次: 主机A对主机B的请求进行确认,将ACK置1,双方向的关闭结束.。

es module和common.js的区别

  • CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
  • CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
  • CommonJs 是单个值导出,ES6 Module可以导出多个
  • CommonJs 是动态语法可以写在判断里,ES6 Module 静态语法只能写在顶层
  • CommonJs 的 this 是当前模块,ES6 Module的 this 是 undefined

vue原理

vue原理

webpack原理

webpack原理

babel原理

babel原理

xss原理

xss原理

async await原理

async await原理

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xiaobangsky

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值