├── LICENSE └── README.md /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 张松 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 前端知识清单 2 | 3 | 题目来源 @ConardLi 大神在掘金发表的[一名【合格】前端工程师的自检清单](https://juejin.im/post/5cc1da82f265da036023b628)。看到评论说希望能有答案,而自己又正好需要系统的补全前端知识地图。 4 | 5 | 题目顺序有些微调,把相关题目放在一起。同时对部分题目有所更改 🤔 6 | 7 | 简单的答案会直接写到对应的题目下面,而篇幅过长的答案会写入 issues;如果网上已有的答案将会直接粘贴对应的连接。答案有误欢迎大家指出,与君共勉,一起成长。 8 | 9 | ## 系统学习指南 10 | 11 | > 切记一定要系统学习,知识清单只是用来差缺补漏的。 12 | 13 | **博客推荐:** 14 | 1. JavaScript 基础这块推荐去看下 @冴羽 大大的博客,可以帮你系统的学习。[链接地址](https://github.com/mqyqingfeng/Blog)。 15 | 16 | **推荐书单:** 17 | 1. [JavaScript高级程序设计](https://book.douban.com/subject/10546125/) 18 | 2. [深入理解ES6](https://book.douban.com/subject/27072230/) 19 | 20 | ## 知识清单 21 | 22 | --- 23 | 24 | ### 一、JavaScript 基础 25 | 26 | > 前端工程师吃饭的家伙,深度、广度一样都不能差。 27 | 28 | #### 变量和类型 29 | 30 | ###### 1.`JavaScript`规定了几种语言类型?难度:⭐️ 31 | 32 |
答案 33 |

34 | 35 | JavaScript语言类型分为两类:基础类型和引用类型; 36 | 37 | 基础类型: `Number`, `String`, `Boolean`, `Null`, `Undefined`, `Symbol` (ES6新增),`BigInt` 38 | 39 | 引用类型: `Object`; 40 | 41 |

42 |
43 | 44 | --- 45 | 46 | ###### 2.`JavaScript`对象的底层数据结构是什么?难度:⭐️⭐️⭐️⭐️⭐️ 47 | 48 |
答案 49 |

50 | 这题难度有点大,看不明白可以先跳过。[从Chrome源码看JS Object的实现](https://zhuanlan.zhihu.com/p/26169639),[V8中的快速属性访问](https://blog.csdn.net/szengtal/article/details/79054762) 51 |

52 |
53 | 54 | --- 55 | 56 | ###### 3.`Symbol`类型在实际开发中的应用、可手动实现一个简单的`Symbol`?难度:⭐️⭐️⭐️ 57 | 58 |
59 | 答案 60 |

61 | 62 | > 在我看来,symbol更多是应用于es6规范中,由于它的值唯一的特性,可以解决变量名,属性名冲突的问题,并切Symbol提出了一些属性和方法,用于过渡以及实现一些特殊的用途,比如对象的迭代器,instanceof的拓展等等。 63 | 64 | 实际使用 `symnol` 主要是使用它的唯一性特性;用来创建 `class` 类的私有属性(ps: 半私有,可以使用 `getOwnPropertySymbols`)。 65 | 66 | [手动实现 `Symbol`](https://github.com/mqyqingfeng/Blog/issues/87)。 67 | 68 |

69 |
70 | 71 | --- 72 | 73 | ###### 4.`JavaScript`中的变量在内存中的具体存储形式? 难度:⭐️⭐️ 74 | 75 |
答案 76 |

77 | 根据变量值得类型区分存储形式: 78 | 79 | 1. 基础类型使用栈存储;基础类型赋值会从新创建一个基础值。 80 | 2. 引用类型使用堆存储;栈中只是存储该对象的在堆中的地址。引用类型赋值只是把堆地址赋给新的变量。 81 | 82 |

83 |
84 | 85 | --- 86 | 87 | ###### 5.理解值类型和引用类型 难度:⭐️⭐️ 88 | 89 |
答案 90 |

91 | 92 | [JS基本类型与引用类型知多少](https://juejin.im/post/595616ea5188250da205da91) 93 |

94 |
95 | 96 | --- 97 | 98 | ###### 6.`null`和`undefined`的区别?难度:⭐️⭐️ 99 | 100 |
答案 101 |

102 | 103 | `undefined` 是 `Undefined` 类型的值,表示未定义。任何变量在赋值前都是 `Undefined` 类型,值为 `undefined` 。由于`undefined` 只是全局作用域下的一个属性(变量),并非关键字。`undefined` **属性的属性特性** 104 | 105 | | 属性名 | 属性值 | 106 | | :----------: | :----: | 107 | | writable | false | 108 | | enumerable | false | 109 | | configurable | false | 110 | 111 | 全局作用下的 `undefined` 不能被重写,而在函数作用域内是可以随意改写 `undefined` 的。这也是建议使用 `void 0` 来表示 `undefined` 的来源 112 | 113 | ```javascript 114 | window.undefined = 1; // false 115 | function setUndefiend(){ 116 | let undefined = 1; 117 | console.log(undefined); // 1 118 | console.log(undefined === void 0); // false 119 | } 120 | setUndefined(); 121 | ``` 122 | 123 | `Null` 类型也只有一个值,就是 `null`,它的语义表示空值,与 `undefined` 不同,`null` 是 `JavaScript` 关键字,所以在任何代码中,你都可以放心用 `null` 关键字来获取 `null` 值。 124 |

125 |
126 | 127 | --- 128 | 129 | ###### 7.至少可以说出三种判断`JavaScript`数据类型的方式,以及他们的优缺点,如何准确的判断数组类型?难度:⭐️⭐️ 130 | 131 |
答案 132 |

133 | 134 | 135 | | 方法 | 优点 | 缺点 | 136 | | :----------------------------: | :-----------------------------------------: | :----------------------------------------------------------: | 137 | | typeof | 简单,对基础类型检测新能好。 | 只能校验基础类型,而且typeof null === 'object' (JS 设计初的bug) | 138 | | Object.prototype.toString.call | 所有类型都能检测 | 写起来比较繁琐,性能不如 typeof 好; | 139 | | instanceof | 能检测出引用类型 | 不能检测出基础类型 | 140 | | constructor | 基本能检测所有的类型(除了null和undefined) | constructor易被修改,不可靠 | 141 | 142 | [性能对比](https://jsperf.com/js-type-check) 143 | 144 | [详情使用方法连接](https://github.com/mqyqingfeng/Blog/issues/28) 145 |

146 |
147 | 148 | --- 149 | 150 | ###### 8.基本类型对应的内置对象,以及他们之间的装箱拆箱操作 难度:⭐️⭐️⭐️ 151 | 152 |
答案 153 |

154 | 155 | ### 装箱转换 156 | 157 | 每一种基本类型 Number、String、Boolean、Symbol 在对象中都有对应的类(null, undefined 除外),所谓装箱转换,正是把基本类型转换为对应的对象,它是类型转换中一种相当重要的种类。 158 | 159 | **注意:** 160 | 161 | 1. 装箱机制会频繁产生临时对象,在一些对性能要求较高的场景下,我们应该尽量避免对基本类型做装箱转换。 162 | 2. Symbol 是不能直接使用 new 操作符调用的。不过我们可以装箱机制得到一个 Symbol 对象。 163 | 3. 每一类装箱对象皆有私有的 Class 属性,这些属性可以用 Object.prototype.toString 获取. 164 | 165 | ```JS 166 | var symbolObject = (function(){ return this; }).call(Symbol("a")); 167 | 168 | console.log(typeof symbolObject); //object 169 | console.log(symbolObject instanceof Symbol); //true 170 | console.log(symbolObject.constructor == Symbol); //true 171 | 172 | // ==== 也可以使用 Object 函数显示调用装箱能力; 173 | var symbolObject = Object(Symbol("a")); 174 | 175 | console.log(Object.prototype.toString.call(symbolObject)); //[object Symbol] 176 | 177 | ``` 178 | ### 拆箱转换 179 | 180 | 在 JavaScript 标准中,规定了 ToPrimitive 函数,它是对象类型到基本类型的转换(即,拆箱转换)。 181 | 182 | 对象到 String 和 Number 的转换都遵循“先拆箱再转换”的规则。通过拆箱转换,把对象变成基本类型,再从基本类型转换为对应的 String 或者 Number。 183 | 184 | 拆箱转换会尝试调用 valueOf 和 toString 来获得拆箱后的基本类型。如果 valueOf 和 toString 都不存在,或者没有返回基本类型,则会产生类型错误 TypeError。 185 | 186 | String 类型会优先调用 toString ; 187 | 188 | 在 ES6 之后,还允许对象通过显式指定 @@toPrimitive Symbol 来覆盖原有的行为。 189 | 190 | 191 | ```JS 192 | var o = { 193 | valueOf : () => {console.log("valueOf"); return {}}, 194 | toString : () => {console.log("toString"); return {}} 195 | } 196 | 197 | o * 2 198 | // valueOf 199 | // toString 200 | // TypeError 201 | 202 | String(o) 203 | // toString 204 | // valueOf 205 | // TypeError 206 | 207 | o[Symbol.toPrimitive] = () => {console.log("toPrimitive"); return "hello"} console.log(o + "") // toPrimitive // hello 208 | 209 | ``` 210 |

211 |
212 | 213 | --- 214 | 215 | ###### 9.可能发生隐式类型转换的场景以及转换原则,应如何避免或巧妙应用 216 | 217 |
答案 218 |

219 | 220 | > 编码时应尽可能地将类型转换表达清楚,以免给别人留坑。类型转换越清晰,代码可读性越高,更容易理解。 221 | 222 | `+` 运算符 223 | 224 | `-` `*` `/` 强制将其他类型转化为数字类型 225 | 226 | `==` 宽松 (loose equals ) 类型转换(ps: 不建议使用,规则真心有点复杂,感兴趣可以去看下 《你不知道的JavaScript 中卷》 1.4章) 227 | 228 | **隐式强制类型转换为布尔值** 229 | 230 | 1. `if(...)`语句中的条件判断表达式 231 | 2. `for(...;...;...)`语句中的条件判断表达式(第二个) 232 | 3. `while(...)` 和 `do...while(...)` 循环中的条件判断表达式 233 | 4. `?:` 中的条件判断表达式 234 | 5. 逻辑运算符 `||` 和 `&&` 左边的操作数 235 | 236 | | 类型 | Null | Undefined | Boolean(true) | Boolean(false) | Number | String | Symbol | Object | 237 | | ------- | --------- | ----------- | ------------- | -------------- | -------------------------- | ------------------ | ---------- | ---------- | 238 | | Boolean | false | false | - | - | [0, NaN] - false
true | ''-false
true | true | true | 239 | | Number | 0 | NaN | 1 | 0 | - | *StringToNumber* | | *拆箱操作* | 240 | | String | 'null' | 'undefined' | 'true' | 'false' | *NumberToString* | - | TypeError | *拆箱操作* | 241 | | Object | TypeError | TypeError | *装箱操作* | *装箱操作* | *装箱操作* | *装箱操作* | *装箱操作* | - | 242 | 243 |

244 |
245 | 246 | --- 247 | 248 | ###### 10.出现小数精度丢失的原因,`JavaScript`可以存储的最大数字、最大安全数字,`JavaScript`处理大数字的方法、避免精度丢失的方法 难度:⭐️⭐️⭐️⭐️ 249 | 250 |
答案 251 |

252 | 253 | [JavaScript 浮点数陷阱及解法](https://github.com/camsong/blog/issues/9) 254 |

255 |
256 | 257 | --- 258 | 259 | #### 原型和原型链 260 | 261 | 262 | ##### 1.instanceof的底层实现原理,手动实现一个instanceof 难度:⭐️⭐️ 263 | 264 |
答案 265 |

266 | 267 | > instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。 268 | 269 | 因为 instanceof 是个关键字,我们只能写一个函数 instanceofFn 来模拟 instanceof 功能。 270 | 271 | ```JS 272 | /* 273 | * obj 需要测试的实例 274 | * Constructor 构造函数 275 | */ 276 | var instanceofFn = function (obj, Constructor){ 277 | // 实例的 __proto__ 属性指向构造函数的 prototype 属性。 278 | return obj.__proto__.constructor === Constructor ? true: false; 279 | } 280 | function Foo(){}; 281 | var foo = new Foo(); 282 | var boo = {}; 283 | 284 | console.log(instanceofFn(foo, Foo)) // true 285 | console.log(instanceofFn(boo, Foo)) //false 286 | ``` 287 | 288 |

289 |
290 | 291 | --- 292 | 293 | ##### 2.可以描述new一个对象的详细过程,手动实现一个new操作符 难度:⭐️⭐️ 294 | 295 |
答案 296 |

297 | 298 | new 作为关键字会进行如下的操作: 299 | 300 | 1. 创建一个空的简单JavaScript对象(即{}); 301 | 2. 链接该对象(即设置该对象的构造函数)到另一个对象 ; 302 | 3. 将步骤1新创建的对象作为this的上下文 ; 303 | 4. 如果该函数没有返回对象,则返回this。 304 | 305 | ```JS 306 | 307 | function objectFactory(Constructor){ 308 | var obj = new Object(); 309 | var args = Array.prototype.slice(1) 310 | obj.__proto__ = Constructor.prototype; 311 | var ret = Constructor.apply(obj,args); 312 | return typeof ret === 'object' ? ret: obj; 313 | } 314 | 315 | ``` 316 | 317 |

318 |
319 | 320 | --- 321 | 322 | ##### 3.创建对象的方法以及他们的优缺点 难度:⭐️⭐️ 323 | 324 |
答案 325 |

326 | 327 | 详细的请看 《JavaScript 高级程序设计》 328 | 329 | [JavaScript 深入之创建对象的多种方式以及优缺点](https://github.com/mqyqingfeng/Blog/issues/15) 330 | 331 |

332 |
333 | 334 | --- 335 | 336 | ##### 4.实现继承的几种方式以及他们的优缺点 难度:⭐️⭐️ 337 | 338 |
答案 339 |

340 | 341 | 详细的请看 《JavaScript 高级程序设计》 342 | 343 | [JavaScript深入之继承的多种方式和优缺点](https://github.com/mqyqingfeng/Blog/issues/16) 344 |

345 |
346 | 347 | --- 348 | 349 | ##### 5.理解es6 class构造以及继承的底层实现原理 难度:⭐️⭐️⭐️ 350 | 351 |
答案 352 |

353 | 354 | [ES6 系列之 Babel 是如何编译 Class 的(上)](https://github.com/mqyqingfeng/Blog/issues/105) 355 | 356 | [ES6 系列之 Babel 是如何编译 Class 的(下)](https://github.com/mqyqingfeng/Blog/issues/106) 357 | 358 |

359 |
360 | 361 | --- 362 | 363 | #### 作用域和闭包 364 | 365 | 366 | ##### 1.理解词法作用域和动态作用域 难度:⭐️⭐️ 367 | 368 |
答案 369 |

370 | 371 | ## 作用域 372 | 373 | 作用域是指程序源代码中定义变量的区域。 374 | 375 | 作用域规定了如何查找变量,也就是确定当前执行代码对变量的访问权限。 376 | 377 | JavaScript 采用词法作用域(lexical scoping),也就是静态作用域。 378 | 379 | ### 词法作用域 380 | 381 | 函数的作用域在函数定义的时候就决定了。JavaScript 采用的是词法作用域。 382 | 383 | ### 动态作用域 384 | 385 | 函数的作用域是在函数调用的时候才决定的。 386 | 387 | ## 示例 388 | 389 | ```JS 390 | var name ="logic" 391 | 392 | function getName(){ 393 | return name; 394 | } 395 | 396 | function getNameWrap(){ 397 | var name = 'wind' 398 | return getName() 399 | } 400 | 401 | console.log(getNameWrap()) // 输出 logic 。因为 JavaScript 是词法作用域,在函数定义时就确定了变量的访问。 402 | 403 | ``` 404 | 405 |

406 |
407 | 408 | --- 409 | 410 | ##### 2.理解变量对象(活动对象) 难度:⭐️⭐️⭐️ 411 | 412 |
答案 413 |

414 | 415 | 变量对象是与执行上下文相关的数据作用域,存储了在上下文中定义的变量和函数。 416 | 417 | 全局对象:是宿主环境预定义的对象,作为 JavaScript 的全局函数和全局属性的占位符。通过使用全局对象,可以访问所有其他所有预定义的对象、函数和属性。全局上下文中的变量对象就是全局对象,即全局对象是作用域链的头。 418 | 419 | 执行上下文的代码会分成两个阶段进行处理: 分析(通过 arguments 属性初始化)和执行 420 | 421 | ### 分析 422 | 423 | 1. 函数的所有形参 424 | 1. 由名称和对应值组成的一个变量对象的属性创建 425 | 2. 没有实参,属性值设为 undefined 426 | 2. 函数声明 427 | 1. 由名称和对应值组成一个变量对象的属性 428 | 2. 如果变量名已经存在,直接替换该属性 429 | 3. 变量声明 430 | 1. 由名称和对应值( undefined ) 组成一个变量对象的属性创建 431 | 2. 如果变量名称已经声明的形参或函数相同,则变量声明无效 432 | 433 | ### 执行 434 | 435 | 在代码执行阶段,会顺序执行代码,根据代码,修改变量对象的值。 436 | 437 | ### 示例 438 | 439 | ```JS 440 | function foo(a,b){ 441 | var a = 2; 442 | function c(){}; 443 | var d = function (){} 444 | } 445 | foo(1) 446 | // 分析阶段 447 | AO = { 448 | arguments:{ 449 | 0: 1, 450 | 1: undefined, 451 | length: 2 452 | }, 453 | a: 1, 454 | b: undefined, 455 | c: reference to function c(){}, 456 | d: undefined 457 | } 458 | 459 | // 代码执行 460 | AO = { 461 | arguments:{ 462 | 0: 1, 463 | 1: undefined, 464 | length: 2 465 | }, 466 | a: 2, 467 | b: undefined, 468 | c: reference to function c(){}, 469 | d: reference to FunctionExpression "d" 470 | } 471 | 472 | ``` 473 | 474 |

475 |
476 | 477 | --- 478 | 479 | ##### 3.理解JavaScript的作用域链 难度:⭐️⭐️⭐️ 480 | 481 |
答案 482 |

483 | 484 | **定义**: 当查找变量的时候,会先从当前上下文的变量查找,如果没有找到,就从父级(词法层面的父级)执行上下文的变量对象查找,一直找到全局上下文的变量对象,也就是全局对象。由多个执行上下文的变量对象构成的链,就是作用域链。 485 | 486 | **用途**: 保证对执行环境有权访问的所有变量和函数的有序访问。 487 | 488 | ### 函数式如何保存自己的作用域链 489 | 490 | 函数有一个内部属性 [[scope]] ,当函数创建的时候,就会保存所有的父变量对象到其中。 491 | 492 | [[scope]] 可以理解为所有父级变量对象的层级链 493 | 494 | ### 示例 495 | 496 | ```JS 497 | function foo(){ 498 | function bar(){ 499 | ... 500 | } 501 | } 502 | 503 | // 函数创建时 504 | 505 | foo.[[scope]] = [ 506 | globalContext.VO 507 | ] 508 | 509 | bar.[[scope]] = [ 510 | fooContext.AO, 511 | globalContext.VO 512 | ] 513 | ``` 514 | 515 |

516 |
517 | 518 | --- 519 | 520 | 521 | ##### 4.this的原理以及几种不同使用场景的取值 难度:⭐️⭐️⭐️ 522 | 523 |
答案 524 |

525 | 526 | ### 显示绑定 527 | 528 | call、apply、bind 可以显示的修改函数的 this 指向 529 | 530 | ### 隐士绑定 531 | 532 | 1. 全局上下文 533 | 1. this 指向 window,严格模式下为 undefined 534 | 2. 直接调用函数 535 | 1. this 指向 window,严格模式下为 undefined 536 | 3. 作为对象的方法调用 537 | 1. obj.foo()。 this 指向对象 obj 538 | 4. DOM 事件的绑定 539 | 1. onclick和addEventerListener中 this 默认指向绑定事件的元素。 540 | 5. new 构造函数绑定 541 | 1. 构造函数中的 this 指向实例对象 542 | 6. 箭头函数 543 | 1. 箭头函数没有 this, 因此也不能绑定。 544 | 2. 在箭头函数里的 this 会指向 外层的非箭头函数的 this。 545 | 546 | ### this 的原理 547 | 548 | [JavaScript深入之从ECMAScript规范解读this](https://github.com/mqyqingfeng/Blog/issues/7) 549 | 550 |

551 |
552 | 553 | --- 554 | 555 | ##### 5.理解JavaScript的执行上下文和执行上下文栈 难度:⭐️⭐️⭐️ 556 | 557 |
答案 558 |

559 | 560 | ### 执行上下文 561 | 562 | 当 JavaScript 代码执行一段可执行代码(executable code)时,会创建对应的执行上下文(execution context)。 563 | 564 | 执行上下有三个重要属性(也就是前面 3 题所说的内容) 565 | 566 | 1. 变量对象 567 | 2. 作用域链 568 | 3. this 569 | 570 | **执行代码** 571 | 572 | 1. 全局代码 573 | 2. 函数 574 | 3. eval 575 | 576 | ### 执行上下文栈 577 | 578 | 每个函数都会创建执行上下文,执行上下文栈(Execution context stack,ECS)就是 JavaScript 引擎创建出来管理执行上下文的。 579 | 580 | 执行上下文栈是有全局上下文初始化,由于执行代码首先是全局代码。全局上下文永远在执行上下文栈中的最底下,只有等程序关闭才释放。 581 | 582 | #### 示例 583 | 584 | ``` JS 585 | function foo(){ 586 | function bar(){} 587 | bar() 588 | } 589 | foo() 590 | 591 | // 假设 ECS 是个数组 592 | // 第一步 初始化, 全局代码的上下文 globalContext 593 | ECS = [ 594 | globalContext 595 | ] 596 | 597 | // 第二步 执行 foo 598 | 599 | ECS = [ 600 | globalContext, 601 | fooContext 602 | ] 603 | // 第三步 执行 bar 604 | 605 | ECS = [ 606 | globalContext, 607 | fooContext, 608 | barContext 609 | ] 610 | 611 | //第四步 bar 执行完成, 释放 barContext 612 | ECS = [ 613 | globalContext, 614 | fooContext 615 | ] 616 | 617 | // 第五步 foo 执行完成, 释放 fooContext 618 | ECS = [ 619 | globalContext 620 | ] 621 | 622 | ``` 623 | 624 |

625 |
626 | 627 | --- 628 | 629 | ##### 6.闭包的实现原理和作用,可以列举几个开发中闭包的实际应用 难度:⭐️⭐️⭐️ 630 | 631 |
答案 632 |

633 | 634 | ### 闭包的定义 635 | 636 | > 函数与对其状态即词法环境(lexical environment)的引用共同构成闭包(closure)。也就是说,闭包可以让你从内部函数访问外部函数作用域。 637 | 638 | 1. 从理论角度:所有的函数。因为它们都在创建的时候就将上层上下文的数据保存起来了。哪怕是简单的全局变量也是如此,因为函数中访问全局变量就相当于是在访问自由变量,这个时候使用最外层的作用域。 639 | 2. 从实践角度:以下函数才算是闭包: 640 | 即使创建它的上下文已经销毁,它仍然存在(比如,内部函数从父函数中返回) 641 | 在代码中引用了自由变量 642 | 643 | ### 闭包的作用 644 | 645 | 闭包通常用来创建内部变量,使得这些变量不能被外部随意修改,同时又可以通过指定的函数接口来操作。 646 | 647 | ### 实际应用场景 648 | 649 | 1. 保护函数内的变量安全:如迭代器、生成器。 650 | 651 | 2. 在内存中维持变量:如果缓存数据、柯里化。 652 | 653 |

654 |
655 | 656 | --- 657 | 658 | ##### 7.理解栈溢出和内存泄漏的原理,如何防止 难度:⭐️⭐️⭐️ 659 | 660 |
答案 661 |

662 | 663 | ### 栈溢出 664 | 665 | 代码执行前都会进行编译和创建执行上下文。而管理执行上下文的叫做执行上下文栈。栈有个特点就是**后进先出**。同时执行上下文栈是有大小限制的。当执行上下文栈大小超过限制就会产生栈溢出错误。 666 | 667 | [JavaScript深入之执行上下文栈](https://github.com/mqyqingfeng/Blog/issues/4) 668 | 669 | #### 如何防止 670 | 671 | 递归函数很容易出现这种情况,把递归调用的形式改造成其他形式,或者使用加入定时器的方法来把当前任务拆分为其他很多小任务。 672 | 673 | **错误示例** 674 | 675 | ```JS 676 | function add(a,b){ 677 | add(a,b) 678 | } 679 | add(1,2) 680 | ``` 681 | 682 | ### 内存泄漏 683 | 684 | > JavaScript是在创建变量(对象,字符串等)时自动进行了分配内存,并且在不使用它们时“自动”释放。 释放的过程称为垃圾回收。不在使用,没有释放的内存,称为内存泄漏 685 | 686 | #### 3种常见内存泄漏 687 | 688 | 1. 意外的全局变量 689 | 2. 脱离 DOM 的引用 690 | 3. 闭包 691 | 692 | 示例代码 693 | 694 | ``` JS 695 | // 意外全局变量 696 | function foo(arg) { 697 | bar = "this is a hidden global variable"; 698 | } 699 | 700 | // 脱离 DOM 的引用 701 | 702 | const button = document.getElementById('button'); 703 | document.body.removeChild(button); 704 | // 此时,仍旧存在一个全局的 #button 的引用,button 元素仍旧在内存中,不能被 GC 回收。 705 | 706 | // 闭包 707 | (function (){ 708 | let num = 1; 709 | return function add(a,b){ 710 | return a+b; 711 | } 712 | })() 713 | 714 | ``` 715 | 716 |

717 |
718 | 719 | --- 720 | 721 | ##### 8.理解模块化解决的实际问题,可列举几个模块化方案并理解其中原理 难度:⭐️⭐️⭐️⭐️ 722 | 723 |
答案 724 |

725 | 726 | [ES6 系列之模块加载方案](https://github.com/mqyqingfeng/Blog/issues/108) 727 | 728 |

729 |
730 | 731 | ### 执行机制 732 | 733 | 734 | ##### 1.为何try里面放return,finally还会执行,理解其内部机制 735 | 736 | 737 | ##### 2.JavaScript如何实现异步编程,可以详细描述EventLoop机制 738 | 739 | 740 | ##### 3.宏任务和微任务分别有哪些 741 | 742 | 743 | ##### 4.可以快速分析一个复杂的异步嵌套逻辑,并掌握分析方法 744 | 745 | 746 | ##### 5.使用Promise实现串行 747 | 748 | 749 | ##### 6.Node与浏览器EventLoop的差异 750 | 751 | 752 | ##### 7.如何在保证页面运行流畅的情况下处理海量数据 753 | 754 | 755 | ### 语法和API 756 | 757 | 758 | ##### 1.理解 ECMAScript 和 JavaScript 的关系 759 | 760 | 761 | ##### 2.熟练运用 es5、es6 提供的语法规范, 762 | 763 | 764 | ##### 3.熟练掌握 JavaScript 提供的全局对象(例如Date、Math)、全局函数(例如decodeURI、isNaN)、全局属性(例如Infinity、undefined) 765 | 766 | 767 | ##### 4.熟练应用 map、reduce、filter 等高阶函数解决问题 768 | 769 | 770 | ##### 5.setInterval 需要注意的点,使用 setTimeout 实现 setInterval 771 | 772 | 773 | ##### 6.JavaScript提供的正则表达式API、可以使用正则表达式(邮箱校验、URL解析、去重等)解决常见问题 774 | 775 | 776 | ##### 7.JavaScript异常处理的方式,统一的异常处理方案 777 | 778 | --- 779 | 780 | ## 二、HTML和CSS 781 | 782 | ### HTML 783 | 784 | 1.从规范的角度理解HTML,从分类和语义的角度使用标签 785 | 786 | 787 | 2.常用页面标签的默认样式、自带属性、不同浏览器的差异、处理浏览器兼容问题的方式 788 | 789 | 790 | 3.元信息类标签(head、title、meta)的使用目的和配置方法 791 | 792 | 793 | 4.HTML5离线缓存原理 794 | 795 | 796 | ### CSS 797 | 798 | 799 | ##### 1.CSS盒模型,在不同浏览器的差异 难度:⭐️ 800 | 801 |
答案 802 |

803 | 804 | - 定义了盒的四个部分 —— margin, border, padding, and content 805 | - 标准盒模型 806 | - 如果你给盒设置 `width` 和 `height`,实际设置的是 *content box*。 padding 和 border 再加上设置的宽高一起决定整个盒子的大小。 807 | - IE盒模型(box-sizing: border-box) 808 | - 如果你给盒设置 `width` 和 `height`,包含了content box + padding + border 809 | 810 |

811 |
812 | 813 | 2.BFC实现原理,可以解决的问题,如何创建BFC 难度:⭐️⭐️ 814 | 815 |
答案 816 |

817 | 818 | - 参考文章 [10 分钟理解 BFC 原理](https://zhuanlan.zhihu.com/p/25321647), [CSS深入理解流体特性和BFC特性下多栏自适应布局](https://www.zhangxinxu.com/wordpress/2015/02/css-deep-understand-flow-bfc-column-two-auto-layout/) 819 | 820 | - 特性:具有 BFC 特性的元素可以看作是隔离了的独立容器,容器里面的元素不会在布局上影响到外面的元素,并且 BFC 具有普通容器所没有的一些特性。 821 | - 如何产生 BFC 822 | - 浮动元素(非 float: none) 823 | - 绝对定位元素(position: absolute || fixed) 824 | - 行内元素(display: inline-block) 825 | - 表格元素 (display: table-cell || table-caption) 826 | - overflow 值不为 visible 元素 827 | - 弹性元素: display: flex 828 | - 网格元素: display: grid 829 | - BFC 实际用途 830 | - 清除浮动 831 | - 解除外边距折叠 832 | - 自适应布局: 左浮动 + bfc overflow: hidden; 833 | 834 |

835 |
836 | 837 | --- 838 | 839 | 3.水平垂直居中的方案、可以实现6种以上并对比它们的优缺点 难度:⭐️⭐️ 840 |
答案 841 |

842 | 843 | - 参考文章:[水平垂直居中](https://github.com/yanhaijing/vertical-center),[16种方法实现水平居中垂直居中](https://juejin.im/post/58f818bbb123db006233ab2a) 844 | - 居中元素定宽高 845 | - absolute + 负 margin + top:50%; left:50%; 846 | - absolute + margin:auto + top:0;left: 0;bottom:0; right:0; 847 | - absolute + cacl(50%- el.width) 848 | - 居中元素不定宽高 849 | - absolute + transform: translate(-50%, -50%) 850 | - 父元素设置: wirting-mode: vertical-lr;text-align: center: 子元素包一层设置 wirting-mode: horizontal-tb;text-aligin: center; 851 | - 父元素设置: line-height: 300px;子元素设置: display: inline-block; 852 | - 父元素设置: display: table-cell; vertical-align: middle; 853 | - 父元素设置: display: flex; justify-content:center;align-items:center; 854 | - 父元素设置: display: grid; 子元素设置: align-self: center; justify-self: center; 855 | 856 |

857 |
858 | 859 | --- 860 | 861 | 862 | 4.PostCSS、Sass、Less的异同,以及使用配置,至少掌握一种 863 | 864 | 865 | 5.CSS模块化方案、如何配置按需加载、如何防止CSS阻塞渲染 866 | 867 | 868 | 6.掌握一套完整的响应式布局方案 869 | 870 | 871 | ## 三、计算机基础 872 | 873 | > 关于编译原理,不需要理解非常深入,但是最基本的原理和概念一定要懂,这对于学习一门编程语言非常重要 874 | 875 | ### 编译原理 876 | 877 | 878 | 1.理解代码到底是什么,计算机如何将代码转换为可以运行的目标程序 879 | 880 | 881 | 2.正则表达式的匹配原理和性能优化 882 | 883 | 884 | 3.如何将JavaScript代码解析成抽象语法树(AST) 885 | 886 | 887 | 4.base64的编码原理 888 | 889 | 890 | 5.几种进制的相互转换计算方法,在JavaScript中如何表示和转换 891 | 892 | 893 | ### 网络协议 894 | 895 | 1.理解什么是协议,了解TCP/IP网络协议族的构成,每层协议在应用程序中发挥的作用 896 | 897 | 898 | 2.三次握手和四次挥手详细原理,为什么要使用这种机制 难度:⭐️⭐️⭐️ 899 | 900 |
答案 901 |

902 | 903 | ### 为什么会使用机制? 904 | 905 | 用于保证传输的可靠性 906 | 907 | ### 三次握手 908 | 909 | 所谓三次握手(Three-way Handshake),是指建立一个 TCP 连接时,需要客户端和服务器总共发送3个包。 910 | 911 | 三次握手的目的是连接服务器指定端口,建立 TCP 连接,并同步连接双方的序列号和确认号,交换 TCP 窗口大小信息。在 socket 编程中,客户端执行 connect() 时。将触发三次握手。 912 | 913 | - 第一次握手(SYN=1, seq=x): 914 | 915 | 客户端发送一个 TCP 的 SYN 标志位置1的包,指明客户端打算连接的服务器的端口,以及初始序号 X,保存在包头的序列号(Sequence Number)字段里。发送完毕后,客户端进入 SYN_SEND 状态。 916 | 917 | - 第二次握手(SYN=1, ACK=1, seq=y, ACKnum=x+1): 918 | 919 | 服务器发回确认包(ACK)应答。即 SYN 标志位和 ACK 标志位均为1。服务器端选择自己 ISN 序列号,放到 Seq 域里,同时将确认序号(Acknowledgement Number)设置为客户的 ISN 加1,即X+1。 发送完毕后,服务器端进入 SYN_RCVD 状态。 920 | 921 | - 第三次握手(ACK=1,ACKnum=y+1) 922 | 923 | 客户端再次发送确认包(ACK),SYN 标志位为0,ACK 标志位为1,并且把服务器发来 ACK 的序号字段+1,放在确定字段中发送给对方,并且在数据段放写ISN的+1。发送完毕后,客户端进入 ESTABLISHED 状态,当服务器端接收到这个包时,也进入 ESTABLISHED 状态,TCP 握手结束。 924 | 925 | ### 四次挥手 926 | 927 | TCP 的连接的拆除需要发送四个包,因此称为四次挥手(Four-way handshake)。 928 | 929 | - 第一次挥手(FIN=1,seq=x) 930 | 931 | 假设客户端想要关闭连接,客户端发送一个 FIN 标志位置为1的包,表示自己已经没有数据可以发送了,但是仍然可以接受数据。 932 | 933 | 发送完毕后,客户端进入 FIN_WAIT_1 状态。 934 | 935 | - 第二次挥手(ACK=1,ACKnum=x+1) 936 | 937 | 服务器端确认客户端的 FIN 包,发送一个确认包,表明自己接受到了客户端关闭连接的请求,但还没有准备好关闭连接。 938 | 939 | 发送完毕后,服务器端进入 CLOSE_WAIT 状态,客户端接收到这个确认包之后,进入 FIN_WAIT_2 状态,等待服务器端关闭连接。 940 | 941 | - 第三次挥手(FIN=1,seq=y) 942 | 943 | 服务器端准备好关闭连接时,向客户端发送结束连接请求,FIN 置为1。 944 | 945 | 发送完毕后,服务器端进入 LAST_ACK 状态,等待来自客户端的最后一个ACK。 946 | 947 | - 第四次挥手(ACK=1,ACKnum=y+1) 948 | 949 | 客户端接收到来自服务器端的关闭请求,发送一个确认包,并进入 TIME_WAIT状态,等待可能出现的要求重传的 ACK 包。 950 | 951 | 服务器端接收到这个确认包之后,关闭连接,进入 CLOSED 状态。 952 | 953 |

954 |
955 | 956 | 957 | 3.有哪些协议是可靠,TCP有哪些手段保证可靠交付 958 | 959 | 960 | 4.DNS的作用、DNS解析的详细过程,DNS优化原理 961 | 962 | 963 | 5.CDN的作用和原理 964 | 965 | 966 | 6.HTTP请求报文和响应报文的具体组成,能理解常见请求头的含义,有几种请求方式,区别是什么 967 | 968 | 969 | 7.HTTP所有状态码的具体含义,看到异常状态码能快速定位问题 970 | 971 | 972 | 8.HTTP1.1、HTTP2.0带来的改变 973 | 974 | 975 | 9.HTTPS的加密原理,如何开启HTTPS,如何劫持HTTPS请求 976 | 977 | 978 | 10.理解WebSocket协议的底层原理、与HTTP的区别 979 | 980 | 981 | ### 设计模式 982 | 983 | 984 | 1.熟练使用前端常用的设计模式编写代码,如单例模式、装饰器模式、代理模式等 985 | 986 | 987 | 2.发布订阅模式和观察者模式的异同以及实际应用 988 | 989 | 990 | 3.可以说出几种设计模式在开发中的实际应用,理解框架源码中对设计模式的应用 991 | --------------------------------------------------------------------------------