2020

手写继承

  1. {
  2. {
  3. // js 实现继承
  4. function Animal() {
  5. this.eat = function () {
  6. console.log("animal can eat ...");
  7. };
  8. }
  9. function Dog() {
  10. this.break = function () {
  11. console.log("dog can break");
  12. };
  13. }
  14. Dog.prototype = new Animal();
  15. let es5 = new Dog();
  16. es5.eat(); // animal can eat ...
  17. es5.break(); // dog can break
  18. }
  19. {
  20. // class 实现 继承
  21. class Animal {
  22. constructor(name) {
  23. this.name = name;
  24. }
  25. eat() {
  26. console.log("animal can wat");
  27. }
  28. }
  29. class Dog extends Animal {
  30. constructor(name) {
  31. super(name);
  32. }
  33. break() {
  34. console.log("dog can break");
  35. }
  36. }
  37. let es6 = new Dog("Zekee");
  38. es6.eat(); // animal can wat
  39. es6.break(); // dog can break
  40. }
  41. }

instanceof 实现原理

  1. {
  2. // instanceof 主要的实现原理就是只要右边变量的 prototype 在左边变量的原型链上即可。因此,instanceof 在查找的过程中会遍历左边变量的原型链,直到找到右边变量的 prototype,如果查找失败,则会返回 false,告诉我们左边变量并非是右边变量的实例。
  3. {
  4. function new_instance_of(leftVaule, rightVaule) {
  5. let rightProto = rightVaule.prototype;
  6. leftVaule = leftVaule.__proto__;
  7. while (true) {
  8. if (leftVaule === null) return false;
  9. if (leftVaule === rightProto) return true;
  10. leftVaule = leftVaule.__proto__;
  11. }
  12. }
  13. }
  14. {
  15. const new_instance_of = function (leftVaule, rightVaule) {
  16. let proto = Object.getPrototypeOf(leftVaule);
  17. while (true) {
  18. if (proto == null) return false;
  19. if (proto === rightVaule.prototype) return true;
  20. proto = Object.getPrototypeOf(proto);
  21. }
  22. };
  23. }
  24. // 总结:使用 typeof 来判断基本数据类型是 ok 的,不过需要注意当用 typeof 来判断 null 类型时的问题,如果想要判断一个对象的具体类型可以考虑用 instanceof,但是 instanceof 也可能判断不准确,比如一个数组,他可以被 instanceof 判断为 Object。所以我们要想比较准确的判断对象实例的类型时,可以采取 Object.prototype.toString.call() 方法
  25. }

promise 限制并发数

  1. {
  2. class LimitPromise {
  3. constructor(max) {
  4. // 异步任务“并发”上限
  5. this._max = max;
  6. // 当前正在执行的任务数量
  7. this._count = 0;
  8. // 等待执行的任务队列
  9. this._taskQueue = [];
  10. }
  11. /**
  12. * 调用器,将异步任务函数和它的参数传入
  13. * @param caller 异步任务函数,它必须是async函数或者返回Promise的函数
  14. * @param args 异步任务函数的参数列表
  15. * @returns {Promise<unknown>} 返回一个新的Promise
  16. */
  17. call(caller, ...args) {
  18. return new Promise((resolve, reject) => {
  19. const task = this._createTask(caller, args, resolve, reject);
  20. if (this._count >= this._max) {
  21. // console.log('count >= max, push a task to queue')
  22. this._taskQueue.push(task);
  23. } else {
  24. task();
  25. }
  26. });
  27. }
  28. /**
  29. * 创建一个任务
  30. * @param caller 实际执行的函数
  31. * @param args 执行函数的参数
  32. * @param resolve
  33. * @param reject
  34. * @returns {Function} 返回一个任务函数
  35. * @private
  36. */
  37. _createTask(caller, args, resolve, reject) {
  38. return () => {
  39. // 实际上是在这里调用了异步任务,并将异步任务的返回(resolve和reject)抛给了上层
  40. caller(...args)
  41. .then(resolve)
  42. .catch(reject)
  43. .finally(() => {
  44. // 任务队列的消费区,利用Promise的finally方法,在异步任务结束后,取出下一个任务执行
  45. this._count--;
  46. if (this._taskQueue.length) {
  47. // console.log('a task run over, pop a task to run')
  48. let task = this._taskQueue.shift();
  49. task();
  50. } else {
  51. // console.log('task count = ', count)
  52. }
  53. });
  54. this._count++;
  55. // console.log('task run , task count = ', count)
  56. };
  57. }
  58. }
  59. // 调用器:就是把真正的执行函数和参数传入,创建返回一个新的Promise,而这个新Promise的什么时候返回,取决于这个异步任务何时被调度。Promise内部主要就是创建一个任务,判断任务是执行还是入队。
  60. // 创建任务:实际上就是返回了一个函数,将真正的执行函数放在里面执行。这里利用了Promise的finally方法,在finally中判断是否执行下一个任务,实现任务队列连续消费的地方就是这里。
  61. }

箭头函数跟普通函数的区别

函数扩展

flex 1 全写

  1. /* 等价于 */
  2. #app {
  3. flex-grow: 1;
  4. flex-shrink: 1;
  5. flex-basis: 0%;
  6. }
  7. /* 分别代表了所定义flex盒子的拉伸因子、收缩规则、基础宽度。 */

vue 双向绑定原理

vue学习记录

https 实现原理(越详细越好)

GraphQL 如何优化请求速度

GraphQL为什么会提升性能

GraphQL能够根据页面展示需求请求所需要的数据,不会有冗余的数据,传输效率上会更高

浏览器渲染页面过程

  • 从耗时的角度,浏览器请求、加载、渲染一个页面,时间花在下面五件事情上:
  • DNS 查询
  • TCP 连接
  • HTTP 请求即响应
  • 服务器响应
  • 客户端渲染

渲染机制

如何性能优化

  • DNS解析时间: domainLookupEnd - domainLookupStart
  • TCP建立连接时间: connectEnd - connectStart
  • 白屏时间: responseStart - navigationStart
  • dom渲染完成时间: domContentLoadedEventEnd - navigationStart
  • 页面onload时间: loadEventEnd - navigationStart

    CDN 优化有哪些

缓存有哪些,区别是什么

  • http 缓存 (强缓存和协商缓存)
  • 浏览器缓存 ( Cookie, LocalStorage, SessionStorage )

页面性能类

手写 bind、reduce

  1. {
  2. {
  3. // bind
  4. // 箭头函数的 this 永远指向它所在的作用域
  5. // 函数作为构造函数用 new 关键字调用时,不应该改变其 this 指向,因为 new绑定 的优先级高于 显示绑定 和 硬绑定
  6. {
  7. var mybind = function (thisArg) {
  8. if (typeof this !== "function") {
  9. throw TypeError("绑定必须在函数上调用");
  10. }
  11. // 拿到参数,为了传给调用者
  12. const args = Array.prototype.slice.call(arguments, 1),
  13. // 保存 this
  14. var that = this;
  15. // 构建一个干净的函数,用于保存原函数的原型
  16. var nop = function () { };
  17. // 绑定的函数
  18. var bound = function () {
  19. // this instanceof nop, 判断是否使用 new 来调用 bound
  20. // 如果是 new 来调用的话,this的指向就是其实例,
  21. // 如果不是 new 调用的话,就改变 this 指向到指定的对象 o
  22. return that.apply(
  23. this instanceof nop ? this : thisArg,
  24. args.concat(Array.prototype.slice.call(arguments))
  25. );
  26. };
  27. // 箭头函数处理:由于箭头函数没有 prototype,箭头函数this永远指向它所在的作用域
  28. if (this.prototype) {
  29. nop.prototype = this.prototype;
  30. }
  31. // 修改绑定函数的原型指向
  32. bound.prototype = new nop();
  33. return bound;
  34. };
  35. }
  36. // 测试
  37. const bar = function () {
  38. console.log(this.name, arguments);
  39. };
  40. bar.prototype.name = "bar";
  41. const foo = { name: "foo" };
  42. const bound = bar.mybind(foo, 22, 33, 44);
  43. new bound(); // bar, [22, 33, 44]
  44. bound(); // foo, [22, 33, 44]
  45. }
  46. }

防抖截流

utils.js

遍历树,求树的最大层数。求某层最多的节点数

node 跟浏览器的 event loop 区别

JS运行机制

node 进程之间如何通讯

node 开启进程的方法有哪些,区别是什么

Node.js 通过提供 cluster、child_process API 创建子进程的方式来赋予Node.js “多线程”能力。但是这种创建进程的方式会牺牲共享内存,并且数据通信必须通过json进行传输。(有一定的局限性和性能问题)
基于此 Node.js V10.5.0 提供了 worker_threads,它比 child_process 或 cluster更轻量级。 与child_process 或 cluster 不同,worker_threads 可以共享内存,通过传输 ArrayBuffer 实例或共享 SharedArrayBuffer 实例来实现。

node 如何部署的

pm2,docker容器化等

node check 阶段做了什么,触发了什么事件

node 如何处理错误的

大多数异步API发生错误,采用callback方式来处理异常,其中callback的第一个参数就是err,如果第一个参数为null,而非err的话,则正确执行后面指令,反之为error的话,就会处理相应错误。

前端模块化的理解

  • 功能分治,利维护
  • 复用,利开发

隐式转换

  1. {
  2. {
  3. // ToString (注:ToString不是对象的toString方法,而是指其他类型的值转换为字符串类型的操作)
  4. // null:转为"null"
  5. // undefined:转为"undefined"
  6. // 布尔类型:true和false分别被转为"true"和"false"
  7. // 数字类型:转为数字的字符串形式,如10转为"10", 1e21转为"1e+21"
  8. // 数组:转为字符串是将所有元素按照","连接起来,相当于调用数组的Array.prototype.join()方法,如[1, 2, 3]转为"1,2,3",空数组[]转为空字符串,数组中的null或undefined,会被当做空字符串处理
  9. // 普通对象:转为字符串相当于直接使用Object.prototype.toString(),返回"[object Object]"
  10. String(null); // 'null'
  11. String(undefined); // 'undefined'
  12. String(true); // 'true'
  13. String(10); // '10'
  14. String(1e21); // '1e+21'
  15. String([1, 2, 3]); // '1,2,3'
  16. String([]); // ''
  17. String([null]); // ''
  18. String([1, undefined, 3]); // '1,,3'
  19. String({}); // '[object Objecr]'
  20. }
  21. {
  22. // ToNumber (指其他类型转换为数字类型的操作)
  23. // null: 转为0
  24. // undefined:转为NaN
  25. // 字符串:如果是纯数字形式,则转为对应的数字,空字符转为0, 否则一律按转换失败处理,转为NaN
  26. // 布尔型:true和false被转为1和0
  27. // 数组:数组首先会被转为原始类型,也就是ToPrimitive,然后在根据转换后的原始类型按照上面的规则处理,关于ToPrimitive,会在下文中讲到
  28. // 对象:同数组的处理
  29. Number(null); // 0
  30. Number(undefined); // NaN
  31. Number("10"); // 10
  32. Number("10a"); // NaN
  33. Number(""); // 0
  34. Number(true); // 1
  35. Number(false); // 0
  36. Number([]); // 0
  37. Number(["1"]); // 1
  38. Number({}); // NaN
  39. }
  40. {
  41. // ToBoolean (指其他类型转换为布尔类型的操作)
  42. // js中的假值只有false、null、undefined、空字符、0和NaN,其它值转为布尔型都为true
  43. Boolean(null); // false
  44. Boolean(undefined); // false
  45. Boolean(""); // flase
  46. Boolean(NaN); // flase
  47. Boolean(0); // flase
  48. Boolean([]); // true
  49. Boolean({}); // true
  50. Boolean(Infinity); // true
  51. }
  52. {
  53. // ToPrimitive (指对象类型类型(如:对象、数组)转换为原始类型的操作)
  54. // 注:对于不同类型的对象来说,ToPrimitive的规则有所不同,比如Date对象会先调用toString
  55. // 当对象类型需要被转为原始类型时,它会先查找对象的valueOf方法,如果valueOf方法返回原始类型的值,则ToPrimitive的结果就是这个值
  56. // 如果valueOf不存在或者valueOf方法返回的不是原始类型的值,就会尝试调用对象的toString方法,也就是会遵循对象的ToString规则,然后使用toString的返回值作为ToPrimitive的结果
  57. Number([]); // 0
  58. Number(["10"]); //10
  59. const obj1 = {
  60. valueOf() {
  61. return 100;
  62. },
  63. toString() {
  64. return 101;
  65. },
  66. };
  67. Number(obj1); // 100
  68. const obj2 = {
  69. toString() {
  70. return 102;
  71. },
  72. };
  73. Number(obj2); // 102
  74. const obj3 = {
  75. toString() {
  76. return {};
  77. },
  78. };
  79. Number(obj3); // TypeError
  80. // 对象类型在ToNumber时会先ToPrimitive,再根据转换后的原始类型ToNumber
  81. // Number([]), 空数组会先调用valueOf,但返回的是数组本身,不是原始类型,所以会继续调用toString,得到空字符串,相当于Number(''),所以转换后的结果为"0"
  82. // 同理,Number(['10'])相当于Number('10'),得到结果10
  83. // obj1的valueOf方法返回原始类型100,所以ToPrimitive的结果为100
  84. // obj2没有valueOf,但存在toString,并且返回一个原始类型,所以Number(obj2)结果为102
  85. // obj3的toString方法返回的不是一个原始类型,无法ToPrimitive,所以会抛出错误
  86. }
  87. {
  88. // ==
  89. // 只要布尔类型参与比较,该布尔类型的值首先会被转换为数字类型
  90. // 根据布尔类型的ToNumber规则,true转为1,false转为0
  91. {
  92. false == 0; // true
  93. true == 1; // true
  94. true == 2; // false
  95. }
  96. // 数字类型和字符串类型的相等比较
  97. // 当数字类型和字符串类型做相等比较时,字符串类型会被转换为数字类型
  98. // 根据字符串的ToNumber规则,如果是纯数字形式的字符串,则转为对应的数字,空字符转为0, 否则一律按转换失败处理,转为NaN(NaN和任何值都不相等,包括本身)
  99. {
  100. 0 == ""; // true
  101. 1 == "1"; // true
  102. 1e21 == "1e21"; // true
  103. Infinity == "Infinity"; // true
  104. true == "1"; // true
  105. false == "0"; // true
  106. false == ""; // true
  107. }
  108. // 对象类型和原始类型的相等比较
  109. // 当对象类型和原始类型做相等比较时,对象类型会依照ToPrimitive规则转换为原始类型
  110. {
  111. "[object Object]" == {}; // true
  112. "1,2,3" == [1, 2, 3]; // true
  113. }
  114. // null、undefined和其他类型的比较
  115. // null和undefined宽松相等的结果为true,其次null和undefined都是假值
  116. // false转为0,然后呢? 没有然后了,ECMAScript规范中规定null和undefined之间互相宽松相等(==),并且也与其自身相等,但和其他所有的值都不宽松相等(==)。
  117. {
  118. null == false; // false
  119. undefined == false; // false
  120. null == undefined; // true
  121. }
  122. // {
  123. // [] == ![] // true
  124. // [] == 0 // true
  125. // [2] == 2 // true
  126. // ['0'] == false // true
  127. // '0' == false // true
  128. // [] == false // true
  129. // [null] == 0 // true
  130. // null == 0 // false
  131. // [null] == false // true
  132. // null == false // false
  133. // [undefined] == false // true
  134. // undefined == false // false
  135. // }
  136. {
  137. // 定义一个变量a,使得下面的表达式结果为true; a == 1 && a == 2 && a == 3
  138. {
  139. const a = {
  140. // 定义一个属性来做累加
  141. inx: 1,
  142. valueOf() {
  143. return this.inx++;
  144. },
  145. };
  146. console.log(a == 1 && a == 2 && a == 3); // true
  147. }
  148. {
  149. const a = {
  150. // 定义一个属性来做累加
  151. inx: 1,
  152. toString() {
  153. return this.inx++;
  154. },
  155. };
  156. console.log(a == 1 && a == 2 && a == 3); // true
  157. }
  158. }
  159. }
  160. }

数字在计算机怎么储存的

webpack 优化

webpack 的 require 是如何查找依赖的

webpack 如何实现动态加载

webpack 插件原理,如何写一个插件

给你一个项目,从头开始你怎么考虑

前言

工作流做了哪些事情

如何提升效率与性能

页面性能类

未来的规划是什么

跨域有哪些

通信类

安全类

安全类

变量提升 let const var 区别

  • let不允许在相同作用域内重复声明同一个变量,即同一个作用域内不允许出现名称相同的变量。
  • const用于声明常量,一旦声明,必须立即赋值,且以后不可更改。
  • const命令两个注意点:
    • 1.const 声明之后必须马上赋值,否则会报错
    • 2.const 简单类型一旦声明就不能再更改,复杂类型(数组、对象等)指针指向的地址不能更改,内部数据可以更改。
    • 解释:对象是引用类型,这里的返回值是对象的指针,指向这个对象存储的这个指针,这个指针是不变的,但是,对象本身是可以变的

链表与数组的区别

链表如何遍历

链表

script 标签中 async 跟 defer 的区别

  • 注:网上说 defer不会阻碍页面渲染,但自己测试iOS与safari会白屏,其效果跟script标签一样。

页面性能类

时针与分针计算 夹角问题(Tencent)

  1. function angle(h, m) {
  2. if (h < 24 && m < 60) {
  3. // 时针一小时30度,一分钟0.5度
  4. const a = (h % 12) * 30 + m * 0.5;
  5. // 分针一分钟6度
  6. const b = m * 6;
  7. return Math.abs(a - b);
  8. }
  9. }
  10. console.log(angle(9, 0)); // 270

2021