每个 Web 开发者都应该知道的 JavaScript 概念2025年4月19日 | 阅读 8 分钟 调用栈调用栈是JavaScript解释器用于跟踪程序中正在进行(执行)的函数的数据结构。JavaScript中函数的执行顺序与你调用它们的顺序相同。后进先出栈,最后推入调用栈的函数将最先执行。 根据ECMAScript规范,调用栈是执行上下文的一部分。每次调用函数时,都会创建一个新的执行上下文并将其添加到栈顶。函数完成后,其执行上下文将从栈中移除并执行。这有助于同步代码执行,因为每个函数调用都必须在执行下一个函数之前完成。 原始类型根据ECMAScript规范,JavaScript语言有六种原始数据类型:字符串、数字、bigint、布尔值、undefined和symbol。这些是不可变类型,你不能更改它们的值。Null是一种特殊的原始类型,表示空值,意味着故意缺少任何对象值。 原始类型直接赋值给变量,当你对原始类型执行任何操作时,你处理的是实际值。原始类型没有属性或方法,因此JavaScript会自动将原始值包装到包装对象中。 值类型和引用类型根据ECMAScript,值类型直接存储在变量访问的位置。例如num、string、bool、undefined、big、symbol和null这些值类型,当你向变量赋值时,值本身就被存储了。 隐式、显式、名义、结构和鸭子类型根据ECMAScript规范ECMA-262,JavaScript是一种动态类型语言,因为类型与值相关联而不是与变量相关联,并且类型检查在运行时完成。JavaScript有几种不同的类型处理策略 - 隐式类型(或类型强制转换):这是指JavaScript在需要时自行决定将一种数据类型转换为另一种数据类型。在执行操作时,语言可能会将一种数据类型转换为另一种数据类型,例如JavaScript中的字符串转换为数字。这可以使某些代码更简单,但如果不小心处理,也可能产生意想不到的结果。
- 显式类型:它与隐式类型不同,它是一种使用Number()、String()或Boolean()等函数将值从一种类型转换为另一种类型的代码。
- 名义类型:JavaScript本身不实现名义类型。但是TypeScript是JavaScript的超集,它包含此功能,有助于在开发时检查类型错误。
- 结构类型:这种类型系统基于数据的结构或属性。JavaScript是一种结构类型语言,这意味着只要两个对象共享相同的结构(即,相同的属性和方法集),它们就是兼容的。
- 鸭子类型:在这种风格中,对象的适用性由是否存在某些属性和方法决定,而不是由对象的实际类型决定。鸭子类型是JavaScript中最常用的类型系统之一,因为对象的类型是由对象所拥有的属性决定的,而不是它们声明的类型。
== vs === vs typeof实际上,根据ECMAScript规范,JavaScript具有严格相等运算符 (===) 和宽松相等运算符 (==),因此这两个值之间的运算符行为不同。以下是详细说明 - == (宽松相等):此运算符比较两个值,但在比较之前它会将两个值转换为相同的类型。当你使用的值类型不同时,JavaScript会在比较它们之前尝试将其中一个值视为相同的类型,这通常会返回意想不到的结果。
- === (严格相等):此运算符比较值和类型,不进行类型强制转换。如果两个值的类型不匹配,则比较将返回false。
- typeof 运算符:此运算符用于检查变量的类型。尽管它是一致的,但它也有一些奇怪之处,例如,由于Messenger JS实现的“历史错误”,typeof null返回一个字符串“object”。
函数作用域、块级作用域和词法作用域ECMAScript规范概述了三种主要的作用域类型 - 函数作用域:如果你在方法中使用var声明一个变量,那么它只在该方法中可用。此作用域阻止在声明变量的函数外部访问变量。
- 块级作用域:使用let和const声明的变量具有块级作用域,即它们只在声明它们的特定块{}中可用(例如循环或条件语句)。
- 词法作用域:描述了如何根据变量在内存中的位置评估变量访问。函数是词法作用域的,这意味着它们可以访问在定义它们的范围中声明的变量。
表达式 vs 语句表达式产生值,而语句指示做某事,例如变量赋值或控制流。函数声明会被提升,可以在代码中定义之前调用,但函数表达式永远不允许我们在定义它们之前调用它们。 IIFE、模块和命名空间IIFE是定义后立即运行的函数。IIFE可以创建局部作用域和模块模式,有助于防止污染全局命名空间。JavaScript模块使开发人员能够将代码组织成可重用的组件,使用import和export与其他文件共享。命名空间提供了一种将相关函数、变量和对象分组到单个唯一名称下的方法,这有助于避免命名冲突。 消息队列和事件循环消息队列和事件循环使JavaScript的异步编程模型得以工作。请求到消息队列后,事件循环将始终检查队列中是否有要执行的任务。正是事件循环使JavaScript成为非阻塞运行时,事件循环不断监视调用栈和队列以处理以下异步任务。 setTimeout、setInterval 和 requestAnimationFrame- setTimeout:这用于在定义的时间(毫秒)后运行特定的代码部分或回调函数。这用于在短时间延迟后执行的事情,例如显示消息,在设定的等待时间后执行操作。JavaScript是单线程和事件驱动的,因此超时永远不会精确。
- setInterval:它以指定的间隔(毫秒)连续运行一段代码或回调函数。它非常适合定期发生的操作,例如时钟更新或数据轮询。这很好用,但如果使用效率低下,可能会损害网站的性能。
- requestAnimationFrame:此函数允许你在下次浏览器重绘之前执行操作,并使执行与屏幕的刷新率同步。通常这用于动画,它比setTimeout或setInterval更快,并且提供流畅的视觉效果。与另外两个相比,这个效率更高,因为它会自动适应显示帧速率,并在浏览器选项卡失去焦点时停止。
JavaScript 引擎JavaScript引擎是解释和运行JavaScript代码的程序。常见的引擎有Google V8(用于Chrome和Node.js)和Mozilla的SpiderMonkey(存在于Firefox中)。这些引擎使用即时 (JIT) 编译来优化代码的执行,将JavaScript转换为机器码以实现高性能。 位运算符、类型化数组和数组缓冲区- 位运算符:例如位运算符表示对二进制格式数字(位,即0和1)的操作。一些常见的运算符是AND (&)、OR (|)、XOR (^) 和 NOT (~)。它们通常用于基本操作,如标志设置、掩码和位切换,提供低级、二进制高效的数据操作。
- 类型化数组:类型化数组在语言中支持二进制数据处理。它们只是用于处理存储在ArrayBuffer对象中的原始二进制数据的视图,你可以在其中操作整数(Int8Array、Int16Array等)或浮点数(Float32Array、Float64Array)作为示例。类型化数组在性能敏感的应用程序(如WebGL或多媒体)中非常有用。
- 数组缓冲区:ArrayBuffer是通用的、固定长度的原始二进制数据缓冲区。它提供了一种分配和管理非结构化内存的机制,以后可以通过类型化数组进行操作和访问。这使开发人员能够更有效地操作二进制流,促进文件操作、编码/解码和硬件交互等工作。
DOM和布局树文档对象模型(DOM)是你的HTML文档的树形表示,它允许JavaScript修改内容、样式和结构。布局树由DOM和CSSOM(CSS对象模型)构建,并定义元素应如何显示以及应放置在哪里(大小和位置)。 工厂和类工厂和类是JavaScript中用于创建对象的设计模式。工厂是返回新函数的函数,提供灵活性和动态行为。ES6中引入的类提供了一种定义具有属性和方法的对象蓝图的语法方式,支持继承和封装。 this、call、apply和bind- this:this关键字指向当前正在执行该函数的对象。它的值取决于执行上下文:在方法中,它指向调用该方法的对象;在常规函数中,它默认为全局对象(或严格模式下的undefined)。this的行为可以是动态的,并受函数调用方式的影响。
- call:call方法用于调用具有定义this值的函数。你还可以以逗号分隔列表的形式向函数传递一些参数。当你想借用方法或动态改变this的上下文时,call方法很容易实现。
- apply:此函数的工作方式与call函数相同。但它接受数组或类数组对象作为参数,而不是逗号分隔列表。当你的参数本身已经在一个数组中时,这特别有用。
- bind:bind函数有自己的this值,也可以预设一些参数。与call和apply不同,它不会立即调用函数,而是将函数用作构造函数。当你希望永久将上下文绑定到函数时,这很有帮助,主要用于事件处理程序或回调。
闭包闭包是指一个函数即使在其词法作用域之外执行,也仍然可以访问其词法作用域。它们允许数据封装,提供私有变量和持久状态(例如,在回调函数、工厂函数中常见)。 高阶函数高阶函数是接受另一个函数作为参数,或返回另一个函数作为结果的函数。它们提高了模块化和抽象性,并经常用于map、filter和reduce等实用工具。 递归重复执行某项操作的技术,函数调用自身的过程。虽然它可以用于遍历树,但它仍然需要基本情况以避免进入无限循环。
|