JavaScript:底层原理与面试深度知识点
第十四阶段底层原理与面试深度知识点JS 执行上下文、执行栈、变量对象 VO、活动对象 AO执行上下文是什么执行上下文是 JavaScript 执行一段代码时的运行环境它定义了变量、函数声明和 this 值的作用域。有三种类型全局执行上下文、函数执行上下文和 eval 执行上下文较少用。为什么它为代码的执行提供了一个独立的环境确保变量和函数在正确的作用域内访问和执行避免不同部分代码之间的干扰。怎么工作当 JavaScript 引擎开始执行代码时首先创建全局执行上下文并压入执行栈。每当调用一个函数就会为该函数创建一个新的执行上下文并压入栈顶函数执行完毕后其执行上下文从栈顶弹出。执行栈是什么也叫调用栈是一种后进先出LIFO的数据结构用于存储执行上下文。为什么它管理着代码的执行顺序保证函数调用和返回的正确流程。怎么工作函数调用时其执行上下文被压入栈顶成为当前正在执行的上下文。函数执行结束该上下文从栈顶弹出控制权返回给之前的上下文。变量对象 VO是什么在全局执行上下文中变量对象就是全局对象在浏览器中是window。在函数执行上下文中变量对象是一个包含函数参数、函数声明和变量声明的对象。为什么它存储了当前执行上下文中的所有变量和函数声明方便在执行过程中查找和访问。怎么工作在函数执行上下文创建阶段会扫描函数的参数、函数声明和变量声明将它们添加到变量对象中。变量声明会被提升值为undefined函数声明会被完整提升。活动对象 AO是什么活动对象是函数执行上下文中的变量对象当函数进入执行阶段变量对象被激活就成为活动对象。为什么在执行阶段活动对象用于存储函数执行过程中的实际数据与创建阶段的变量对象有所不同它包含了函数实际执行时的参数值等。怎么工作在函数执行上下文中创建阶段的变量对象在进入执行阶段后成为活动对象此时函数参数被赋值变量开始实际参与运算。作用域链创建过程是什么作用域链是一个由多个作用域对象组成的链表用于在查找变量和函数时确定访问顺序。为什么它决定了在何处查找变量和函数保证了代码在不同作用域下的正确访问。怎么创建每个执行上下文都有一个与之关联的作用域链。函数执行上下文的作用域链其最前端是该函数的活动对象AO如果存在闭包还会包含父函数的活动对象依次向上直到全局执行上下文的变量对象VO。例如functionouter(){letouterVarouter;functioninner(){letinnerVarinner;console.log(outerVar);}inner();}outer();在inner函数执行时其作用域链为inner 函数的 AO- outer 函数的 AO- 全局 VO。当查找outerVar时先在inner函数的 AO 中找没找到则到outer函数的 AO 中找最终找到。原型链完整查找流程是什么原型链是通过原型对象将对象连接起来的链状结构用于实现继承和属性查找。为什么它使得对象可以访问其原型对象上的属性和方法实现代码复用和继承机制。怎么查找当访问一个对象的属性或方法时首先在该对象自身的属性中查找。如果没找到就到其原型对象__proto__中查找。如果原型对象中也没找到就继续到原型对象的原型对象中查找以此类推直到找到或到达Object.prototype如果Object.prototype中也没有则返回undefined。例如functionAnimal(name){this.namename;}Animal.prototype.speakfunction(){return${this.name}makes a sound.;};functionDog(name,breed){Animal.call(this,name);this.breedbreed;}Dog.prototypeObject.create(Animal.prototype);Dog.prototype.constructorDog;constmyDognewDog(Buddy,Golden Retriever);console.log(myDog.speak());这里myDog对象查找speak方法先在自身找没找到通过__proto__到Dog.prototype找还没找到再通过Dog.prototype.__proto__到Animal.prototype找到。Event Loop 完整执行流程浏览器 Node 对比浏览器中的 Event Loop是什么浏览器的 Event Loop 用于协调事件、用户交互、脚本执行、渲染和网络请求等。为什么由于 JavaScript 是单线程的Event Loop 机制使得 JavaScript 可以处理异步操作不会阻塞主线程。怎么执行执行栈Call Stack同步任务在此执行当函数调用时函数压入栈顶执行完毕后弹出。任务队列Task Queue也叫宏任务队列异步操作如setTimeout、setInterval、DOM 事件等完成后其回调函数会被放入任务队列。微任务队列Microtask Queue一些异步操作如Promise.then、MutationObserver等的回调会被放入微任务队列。流程首先执行栈中的同步任务当执行栈为空时检查微任务队列依次执行微任务队列中的任务直到微任务队列为空。然后从任务队列中取出一个宏任务放入执行栈执行执行完后再次检查微任务队列并执行如此循环。Node 中的 Event Loop是什么Node.js 的 Event Loop 同样用于处理异步操作但内部实现和浏览器略有不同。为什么为 Node.js 提供异步 I/O 等功能使其能高效处理大量并发请求。怎么执行Node 的 Event Loop 有多个阶段如timers处理setTimeout和setInterval、I/O callbacks处理大部分 I/O 回调、idle, prepare系统内部使用、poll获取新的 I/O 事件、check执行setImmediate回调、close callbacks处理关闭的回调如socket.on(close,...)。流程进入timers阶段检查是否有到期的定时器执行其回调。然后进入I/O callbacks阶段执行一些系统操作的回调。接着进入poll阶段等待新的 I/O 事件若有则执行其回调。如果poll队列为空且有setImmediate回调则进入check阶段执行setImmediate回调。最后进入close callbacks阶段处理关闭相关回调。在每个阶段执行完后都会执行微任务队列中的任务。对比浏览器的 Event Loop 相对简单主要围绕 DOM 相关事件和网络请求等。Node 的 Event Loop 更复杂需要处理各种 I/O 操作和系统调用。在 Node 中setImmediate和process.nextTick是特殊的异步操作。process.nextTick的回调会在当前阶段所有同步任务和微任务执行完毕后立即执行优先级高于setImmediate。而setImmediate的回调会在poll阶段空闲时或I/O操作完成后执行。宏任务微任务优先级细节宏任务常见的宏任务有setTimeout、setInterval、setImmediateNode 环境、I/O操作、UI rendering浏览器环境等。宏任务在一个事件循环中当执行栈为空且微任务队列为空时从宏任务队列中取一个任务放入执行栈执行。微任务常见的微任务有Promise.then、MutationObserver浏览器环境、process.nextTickNode 环境等。微任务在执行栈为空时立即执行即在当前宏任务执行完后下一个宏任务执行前会先执行微任务队列中的所有任务。所以微任务优先级高于宏任务。例如console.log(同步任务);setTimeout((){console.log(宏任务 setTimeout);},0);Promise.resolve().then((){console.log(微任务 Promise.then);});输出顺序为“同步任务”- “微任务 Promise.then”- “宏任务 setTimeout”。函数提升、变量提升底层原理函数提升是什么在 JavaScript 代码执行前函数声明会被提升到其所在作用域的顶部使得在函数声明之前就可以调用该函数。为什么这是 JavaScript 引擎为了方便函数调用而采用的机制让开发者在代码组织上更灵活。底层原理在执行上下文创建阶段函数声明会被完整地添加到变量对象VO或活动对象AO中而不仅仅是声明提升函数体也会被提升。例如func();functionfunc(){console.log(函数被调用);}这里func函数在声明之前被调用因为函数声明被提升到了作用域顶部。变量提升是什么变量声明会被提升到其所在作用域的顶部但变量的值不会被提升即变量在声明前访问值为undefined。为什么同样是为了在代码执行前预先处理变量声明保证代码执行逻辑的一致性。底层原理在执行上下文创建阶段变量声明会被添加到变量对象VO或活动对象AO中初始值设为undefined。例如console.log(a);vara10;这里会输出undefined因为var a被提升但a 10不会提升。Promise 微任务调度机制是什么Promise 是 JavaScript 中处理异步操作的一种方式其then方法的回调会被放入微任务队列。为什么这样可以保证在当前宏任务执行完后以异步且优先于下一个宏任务的方式执行then回调使得异步操作的处理更有序和高效。怎么调度当Promise被resolve或reject时then方法注册的回调函数不会立即执行而是被放入微任务队列。当执行栈为空时JavaScript 引擎会检查微任务队列并依次执行其中的回调函数。例如console.log(同步任务);Promise.resolve().then((){console.log(Promise 微任务);});console.log(同步任务);输出顺序为“同步任务”- “同步任务”- “Promise 微任务”。类与原型底层等价转换class 本质是语法糖是什么ES6 的class语法是基于原型继承的语法糖它让 JavaScript 的面向对象编程更接近传统面向对象语言的写法但底层仍然是基于原型的机制。为什么class语法提供了更简洁、直观的面向对象编程方式同时保持了 JavaScript 基于原型继承的核心特性方便开发者理解和编写面向对象代码。底层等价转换例如// class 写法classAnimal{constructor(name){this.namename;}speak(){return${this.name}makes a sound.;}}// 等价的原型写法functionAnimalProto(name){this.namename;}AnimalProto.prototype.speakfunction(){return${this.name}makes a sound.;};这里class Animal本质上就是对AnimalProto函数及其原型操作的一种语法封装class的constructor对应函数的参数和内部赋值class的方法定义对应在原型上添加方法。可迭代协议、迭代器协议底层规范可迭代协议是什么可迭代协议定义了对象如何成为可迭代对象。一个对象要成为可迭代对象必须实现Symbol.iterator方法该方法返回一个迭代器对象。例如constmyArray[1,2,3];constiteratormyArray[Symbol.iterator]();console.log(iterator.next());console.log(iterator.next());console.log(iterator.next());console.log(iterator.next());这里数组是可迭代对象因为它实现了Symbol.iterator方法。为什么可迭代协议使得对象可以在for...of循环、Array.from等场景中使用统一了对象的遍历方式。迭代器协议是什么迭代器协议定义了迭代器对象的行为。迭代器对象必须有一个next方法该方法返回一个包含value和done属性的对象。value是迭代的当前值done是一个布尔值表示迭代是否结束。为什么它为对象的迭代提供了一种标准的接口使得不同类型的对象都能以一致的方式进行遍历。模块化加载机制ES Module 与 CommonJS 差异加载时机ES Module静态加载在编译阶段就确定模块的依赖关系和导入导出。这使得可以进行一些编译时优化如 tree- shaking去除未使用的代码。例如import{func}from./module.js;// 这里在编译时就确定了依赖关系CommonJS动态加载在运行时才确定模块的依赖关系和导出。例如constmodulerequire(./module.js);// 这里在运行时才加载模块导出方式ES Module有默认导出export default和具名导出export。例如// 具名导出exportconstfunc1()console.log(Func1);// 默认导出exportdefault()console.log(Default export);CommonJS通过module.exports或exports导出。例如functionfunc1(){console.log(Func1);}exports.func1func1;// 或者module.exports{func1:function(){console.log(Func1);}};导入方式ES Module使用import导入。例如import{func1}from./module.js;importmyDefaultfrom./module.js;CommonJS使用require导入。例如const{func1}require(./module.js);constmyModulerequire(./module.js);循环依赖处理ES Module在静态分析阶段就处理循环依赖对于已经加载但未完全求值的模块会先使用其部分值等模块完全求值后再更新。CommonJS如果遇到循环依赖在加载模块时会先返回一个未完全初始化的模块对象可能导致获取到的值不正确。例如// a.jsconstbrequire(./b.js);console.log(a.js:,b.value);exports.valuea 的值;// b.jsconstarequire(./a.js);console.log(b.js:,a.value);exports.valueb 的值;在 CommonJS 中a.js中打印b.value可能得到undefined而 ES Module 对这种情况有更合理的处理方式。实战练习混合多层 Promise、定时器、同步代码手动推导打印顺序将 class 语法手动转换为原生构造函数 原型代码思考题变量提升底层原理是什么Promise.then 为什么属于微任务ES Module 加载是同步还是异步

相关新闻