# 第 91 - 100 题

# 91、介绍下 HTTPS 中间人攻击

中间人攻击过程

  • 客户端请求服务器证书
  • charles拦截后 1.向客户端发送自己的伪证书,2.正常向服务端发送请求,3.拦截服务端返回的证书及公钥
  • 客户端通过伪证书加密数据+随机数后 发送给服务端(被中间人拦截)。
  • 中间人解密获得客户端数据和随机数,用正确的证书重新加密发送给服务端
  • 服务端返回数据,被中间人拦截,用随机数解密,用伪证书加密后发送给客户端
  • 在两端无感知的情况下获取到所有数据

防范方法:

服务端在发送浏览端的公钥中增加CA证书,浏览器可以验证CA证书的有效性。

# 92、已知数据格式,实现一个函数 fn 找出链条中所有的父级 id

const value = '112'
const fn = (value) => {
    ...
}
fn(value) // 输出 [1, 11, 112]

// TODO 用深度,怎么去判断是否非父级别,使用位数?

# 93、给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。请找出这两个有序数组的中位数。要求算法的时间复杂度为 O(log(m+n))。

示例 1:

nums1 = [1, 3]
nums2 = [2]

// 中位数是 2.0

示例 2:

nums1 = [1, 2]
nums2 = [3, 4]
// 中位数是(2 + 3) / 2 = 2.5
function getMiddle(num1, num2) {
  // TODO 分治法  
}

# 94、vue 在 v-for 时给每项元素绑定事件需要用事件代理吗?为什么?

由于 v-vor 循环中的每个点击侦听器都将使用相同地回调, 因此除非您有数百或数千行,否则这是很小的。

所以vue没用事件代理

# 95、模拟实现一个深拷贝,并考虑对象相互引用以及 Symbol 拷贝的情况

function depCopy(value) {
  if (typeof value !== 'object') {
    return value;
  }
}
const isComplexDataType = (obj) =>
  (typeof obj === "object" || typeof obj === "function") && obj !== null;

const deepClone = function (obj, hash = new WeakMap()) {
  if (obj.constructor === Date) return new Date(obj); // 日期对象直接返回一个新的日期对象

  if (obj.constructor === RegExp) return new RegExp(obj); //正则对象直接返回一个新的正则对象

  //如果循环引用了就用 weakMap 来解决
  if (hash.has(obj)) return hash.get(obj);

  // getOwnPropertyDescriptors方法返回指定对象上一个自有属性对应的属性描述符
  let allDesc = Object.getOwnPropertyDescriptors(obj);

  //遍历传入参数所有键的特性,Object.create方法创建一个新对象,使用现有的对象来提供新创建对的__proto__
  let cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc);

  //继承原型链
  hash.set(obj, cloneObj);

  for (let key of Reflect.ownKeys(obj)) {
    cloneObj[key] =
      isComplexDataType(obj[key]) && typeof obj[key] !== "function"
        ? deepClone(obj[key], hash)
        : obj[key];
  }

  return cloneObj;
};

// 下面是验证代码

let obj = {
  num: 0,

  str: "",

  boolean: true,

  unf: undefined,

  nul: null,

  obj: { name: "我是一个对象", id: 1 },

  arr: [0, 1, 2],

  func: function () {
    console.log("我是一个函数");
  },

  date: new Date(0),

  reg: new RegExp("/我是一个正则/ig"),

  [Symbol("1")]: 1,
};

Object.defineProperty(obj, "innumerable", {
  enumerable: false,
  value: "不可枚举属性",
});

obj = Object.create(obj, Object.getOwnPropertyDescriptors(obj));

obj.loop = obj; // 设置loop成循环引用的属性

let cloneObj = deepClone(obj);

cloneObj.arr.push(4);

console.log("obj", obj);

console.log("cloneObj", cloneObj);

# 96、介绍下前端加密的常见场景和方法

首先,加密的目的,简而言之就是将明文转换为密文、甚至转换为其他的东西, 用来隐藏明文内容本身,防止其他人直接获取到敏感明文信息、或者提高其他人获取到明文信息的难度。

  • 用户名、密码
  • 防止爬虫
  1. 对称加密
  2. 非对称加密

# 97、React 和 Vue 的 diff 时间复杂度从 O(n^3) 优化到 O(n) ,那么 O(n^3) 和 O(n) 是如何计算出来的?

// 临时放一个回答
原问题标题“React 和 Vue 的 diff 时间复杂度从 O(n^3) 优化到 O(n) ,那么 O(n^3) 和 O(n) 是如何计算出来的? ”

这里的n指的是页面的VDOM节点数,这个不太严谨。如果更严谨一点,我们应该应该假设
变化之前的节点数为m,变化之后的节点数为n。

React 和 Vue 做优化的前提是“放弃了最优解“,本质上是一种权衡,有利有弊。

倘若这个算法用到别的行业,比如医药行业,肯定是不行的,为什么?

React 和 Vue 做的假设是:

检测VDOM的变化只发生在同一层
检测VDOM的变化依赖于用户指定的key
如果变化发生在不同层或者同样的元素用户指定了不同的key或者不同元素用户指定同样的key,
React 和 Vue都不会检测到,就会发生莫名其妙的问题。

但是React 认为, 前端碰到上面的第一种情况概率很小,第二种情况又可以通过提示用户,让用户去解决,因此
这个取舍是值得的。 没有牺牲空间复杂度,却换来了在大多数情况下时间上的巨大提升。
明智的选择!

基本概念
首先大家要有个基本概念。

其实这是一个典型的最小编辑距离的问题,相关算法有很多,比如Git中
,提交之前会进行一次对象的diff操作,就是用的这个最小距离编辑算法。

leetcode 有原题目 // 下方有链接
如果想明白这个O(n^3), 可以先看下这个。

对于树,我们也是一样的,我们定义三种操作,用来将一棵树转化为另外一棵树:

删除:删除一个节点,将它的children交给它的父节点

插入:在children中 插入一个节点

修改:修改节点的值

事实上,从一棵树转化为另外一棵树,我们有很多方式,我们要找到最少的。

直观的方式是用动态规划,通过这种记忆化搜索减少时间复杂度。

算法
由于树是一种递归的数据结构,因此最简单的树的比较算法是递归处理。

确切地说,树的最小距离编辑算法的时间复杂度是O(n^2m(1+logmn)),
我们假设m 与 n 同阶, 就会变成 O(n^3)。

leetcode 编辑距离 (opens new window)

# 98、写出如下代码的打印结果

function changeObjProperty(o) {
  o.siteUrl = "http://www.baidu.com"
  o = new Object()
  o.siteUrl = "http://www.google.com"
} 
let webSite = new Object();
changeObjProperty(webSite);
console.log(webSite.siteUrl);

http://www.baidu.com

# 99、编程算法题

用 JavaScript 写一个函数,输入 int 型,返回整数逆序后的字符串。
如:输入整型 1234,返回字符串“4321”。
要求必须使用递归函数调用,不能用全局变量,输入函数必须只有一个参数传入,必须返回字符串。
function reverse(num) {
  const str = num.toString();
  return str.length <= 1 ? str : reverse(str.slice(1)) + str.slice(0, 1); 
}

# 100、请写出如下代码的打印结果

function Foo() {
    Foo.a = function() {
        console.log(1)
    }
    this.a = function() {
        console.log(2)
    }
}
Foo.prototype.a = function() {
    console.log(3)
}
Foo.a = function() {
    console.log(4)
}
Foo.a();
let obj = new Foo(); // 核心点:此处调用了Foo函数,重新赋值替换Foo.a
obj.a();
Foo.a();

// 4 2 1

Last Updated: 4/17/2022, 2:01:49 PM