深入理解JavaScript中的核心内容

简介: 本文全面介绍了JavaScript的基础与进阶知识。首先,概述了JavaScript作为一门脚本语言的基本概念,包括ECMAScript、DOM及BOM的作用与重要性。接着,深入探讨了JavaScript的执行流程,从编译到执行阶段,并重点讲解了JIT编译技术如何提升执行效率。随后,介绍了进阶知识点,如闭包的强大功能及其潜在的内存问题,`this`关键字的不同行为,以及事件循环机制如何管理同步与异步代码。通过本文的学习,读者不仅能掌握JavaScript的核心概念,还能深入了解其实现细节与最佳实践,为高效开发打下坚实基础。

目录

  1. JS的基本概念
  2. JS是怎么执行的
  3. JS的进阶知识点
  4. 课程总结

1. JS 的基本概念

JavaScript(简称JS)是一门脚本语言,用于为网页添加交互效果动态功能。它由三个基本部分组成: ECMAScriptDOMBOM

  • ECMAScript 是 JavaScript 的核心语言,规范了 JavaScript 的基本语法、数据类型、流程控制等。

  • DOM(文档对象模型) 是针对 XML 文档的一个 API(应用程序编程接口),也适用于 HTML 文档。它把整个页面映射为一个多层节点结构,通过 DOM API可以对页面上的任何元素进行操作

  • BOM(浏览器对象模型) 是指浏览器提供的一组 JavaScript API,通过它们可以获取和控制浏览器窗口和标签页等浏览器本身的功能。BOM 包含了很多对象,比如 window、location、navigator等。

除了这些基本概念,JavaScript 还有一些特性和技术,比如闭包、原型链、异步编程等,这些都是深入学习 JavaScript 需要了解的内容。

1.1 动态 , 弱类型

JavaScript 的变量是动态的,因为在声明变量时不需要指定变量类型,变量的类型是在程序运行过程中自动推断出来的。也就是说,同一个变量在不同的时候可以存储不同类型的值。例如,一个变量可以先存储数字类型的值,然后再存储字符串类型的值。

JavaScript 的变量是弱类型的,是因为它们的类型可以随时发生改变,而且不需要进行类型转换就能进行运算。比如,在 JavaScript 中,一个变量可以存储数字类型的值,另一个变量可以存储字符串类型的值,但是它们仍然可以进行加法操作,并且会自动将字符串转换成数字再进行计算。

这种动态和弱类型的特性使得 JavaScript 在编写灵活性高的应用程序时非常有优势,但也存在一些问题。如果不小心让变量存储了错误类型的值,可能会导致程序出错或者产生意料之外的结果,因此要格外小心处理变量类型的问题。

1.jpg

1.2 变量提升

在使用var时,下面的代码不会报错. 这是因为这个关键字声明的变量会自动提升到函数作用域顶部

function test(){
   
   
    console.log(age);
    var age = 13;
}
test() // undefined

之所以不会报错,是因为ECMAScript 运行的时候会把它看成等价于如下代码:

function test1(){
   
   
    var age;
    console.log(age);
    age = 13
}
test1()

这就是所谓的变量提升,就是把所有变量声明都拉到函数作用域的顶部.

函数声明也会被提升到作用域顶部,如下所示:

foo(); // 支持调用,输出 "bar"
function foo() {
   
   
  console.log("bar");
}

以上代码中,函数 foo 虽然在调用前定义了,但是在代码执行时已经被提升到了作用域顶部,因此可以正常调用并输出 "bar"。

let 和 const 不存在变量提升

2.JS是怎么执行的

2.1 讲解执行大概过程

2.jpg

JavaScript的执行分为两个阶段:编译和执行。

  1. 编译阶段

当JavaScript代码被加载时,JavaScript引擎会首先把它编译成字节码或机器码。编译阶段包括:

  • 词法分析:将代码按照语法规则分解成一个个单独的词汇单元(例如变量名、操作符、关键字等),这些词汇单元称为 Token。
  • 语法分析:将词法单元转换为 AST(Abstract Syntax Tree,抽象语法树)。AST是一种用于表示程序代码的树形数据结构,每个节点代表程序中的一个语言单元(如函数、语句、表达式等),可以方便地对代码进行分析和优化。
  • 代码生成:将AST转换为可执行代码(机器码或字节码),并将其存储在内存中以供后续执行。

在编译过程中,JavaScript 引擎会执行一些静态检查,如语法检查和类型检查。如果发现代码存在错误,编译阶段会立即停止并抛出错误信息。

  1. 执行阶段

编译阶段完成后,引擎开始执行代码。代码执行的具体流程是:

  • 作用域创建:在进入执行上下文时,JavaScript 引擎会创建一个新的作用域(即执行上下文),并将其加入到执行上下文栈中。
  • 变量提升:JavaScript 引擎会扫描当前作用域的所有变量和函数声明,并将它们提升到作用域的顶部,以便正确地处理变量访问和函数调用。这就是前面提到的“变量提升”机制。
  • 代码执行:JavaScript 引擎按照编译阶段生成的可执行代码进行执行,逐行解释执行代码,并根据当前状态来更新变量和对象的值。

在执行过程中,JavaScript引擎还会进行一些性能优化,如 JIT(Just-In-Time)编译、内联缓存等,以提高代码的运行速度和效率。

总体来说,JavaScript 的执行流程比较复杂,但是了解其基本原理和流程对于我们编写高效、健壮的 JavaScript 代码非常重要。

2.2 JIT(Just-In-Time)讲解

JIT(Just-In-Time)编译是一种动态编译技术,可以提高JavaScript代码的执行效率。在 JIT 编译中,JavaScript 引擎会将频繁执行的代码动态地编译成机器码并缓存起来,以便后续快速执行。

具体来说,JIT 编译分为三个阶段:

  1. 解释执行

当 JavaScript 代码被加载时,JavaScript 引擎首先对其进行解释执行,即逐行读取代码并执行相应的操作。由于解释执行需要频繁地解析和执行代码,因此效率较低。

  1. 编译阶段

在解释执行过程中,JavaScript 引擎会监测代码的执行频率,并对频繁执行的代码进行编译优化。编译过程包括 AST 解析、基础块分析、控制流分析、数据流分析等步骤,最终生成优化后的代码。这些代码被称为机器码。

  1. 优化阶段

优化阶段是 JIT 编译的核心。在每次执行频繁的代码时,JavaScript 引擎会对其进行优化,并重新生成机器码。这些优化包括内联缓存、函数内联、去除未使用的代码等。优化的目标是尽可能地减少不必要的计算、内存访问等操作,以提高代码的执行效率。

总体来说,JIT 编译技术可以大大提高 JavaScript 代码的执行效率。由于 JIT 编译需要根据代码的实际执行情况进行优化,因此它对于频繁执行的函数和循环特别有效。不过,在少量执行的代码中,JIT 编译可能会增加执行时间,因此需要按需使用。

3. JS的进阶知识点

3.1 闭包

JavaScript 中的闭包是一个非常强大的概念,很多开发者在学习 JavaScript 时都会遇到这个问题。本篇文章将介绍 JavaScript 中的闭包,同时提供一些例子来帮助您更好地理解。

什么是闭包?

首先,我们需要明白闭包是什么。简单的说,闭包是指可以访问独立变量的函数。具体来说,当一个内部函数引用了其外部函数的变量时,就形成了一个闭包。

下面的代码示例将更好地说明这一点:

// 外部函数
function outerFunction() {
   
   
  const outerVariable = '123';

  // 内部函数
  function innerFunction() {
   
   
    console.log(outerVariable);
  }

  return innerFunction;
}

const myInnerFunction = outerFunction();
myInnerFunction(); // 输出:123

在上面的示例代码中,outerFunction 函数返回了 innerFunction 函数。这意味着 innerFunction 可以访问 outerFunction 中定义的变量。因此,当 myInnerFunction() 被调用时,它会输出 I am outside!

闭包的优点

闭包的最大优点是它们可以帮助我们隐藏或封装数据。这使得我们可以编写很多高效和安全的代码。其中一个优点是,闭包可以“记住”其父级函数中的数据,即使该函数已经退出并且不再存在。

下面是一个例子,它使用闭包来实现私有变量:

function createCounter() {
   
   
  let count = 0;
  return function() {
   
   
    count++;
    console.log(count);
  }
}

const counterA = createCounter();
counterA(); // 输出:1
counterA(); // 输出:2

const counterB = createCounter();
counterB(); // 输出:1

在这个例子中,createCounter 函数返回一个函数,该函数可以递增计数器并输出值。由于 count 变量只在 createCounter 函数内部定义,因此外部代码无法直接访问它。这使得我们可以安全地保护数据,并确保对其进行处理的代码仅在闭包范围内。

闭包的缺点

虽然闭包非常有用,但它们也有一些缺点。其中,最大的问题是它们可能会浪费内存。JavaScript 中的垃圾收集器将不会回收闭包中未使用的变量。如果你创建了很多这样的闭包,那么就可能导致内存泄漏和性能问题

下面是一个稍微复杂的例子,它演示了在 JavaScript 中使用闭包的一些缺点:

function createBigObject() {
   
   
  const bigObject = new Array(70000).fill('x').join('');
  return function() {
   
   
      console.log(bigObject);
  }
}

const myBigObjectFunction = createBigObject();
myBigObjectFunction();

在这个例子中,createBigObject 函数返回了一个包含大量数据的闭包。每次调用 myBigObjectFunction 都会输出这个巨大的字符串。由于 JavaScript 不会回收未使用的闭包变量,因此可能会导致内存泄漏和性能问题

结论

在本文中,我们介绍了 JavaScript 中的闭包概念,并提供了几个示例来更好地理解它们。闭包是强大而有用的,但也需要小心使用,以避免出现内存泄漏和性能问题。在正确使用闭包的情况下,它们可以使代码更加灵活,可重用,并且能够实现很多高效、安全的功能

3.2 This

普通函数this 指向Window

this 在 JavaScript 中是一个非常重要的概念。它指的是函数所属的对象,具体取决于函数的调用方式。下面是一些常见的使用方式

1. 作为对象的方法 :

const person = {
   
   
  name: 'Alice',
  sayHello() {
   
   
    console.log(`你好,我的名字是 ${
     
     this.name}`);
  }
};

person.sayHello(); // 输出 "你好,我的名字是 Alice"

在这个例子中,this 指的是 person 对象

2. 作为构造函数:

function Person(name) {
   
   
  this.name = name;
}

const alice = new Person('Alice');
console.log(alice.name); // 输出 "Alice"

在这个例子中,this 指的是 Person 构造函数创建的新对象。

3. 使用 call 或 apply 显式地设置 this:


function sayHello() {
   
   
  console.log(`你好,我的名字是 ${
     
     this.name}`);
}

const person1 = {
   
    name: 'Alice' };
const person2 = {
   
    name: 'Bob' };

sayHello.call(person1); // 输出 "你好,我的名字是 Alice"
sayHello.apply(person2); // 输出 "你好,我的名字是 Bob"

在这个例子中,call 和 apply 被用来将 this 分别设置为 person1 和 person2。

3.3 事件循环

当 JavaScript 运行时,它会将代码分为两类:同步代码和异步代码。同步代码是按顺序执行的,而异步代码则是在后台执行的,不会阻塞主线程。事件循环是 JavaScript 处理异步代码的机制

事件循环是一个不断运行的循环,它会检查消息队列中是否有待处理的消息。如果有,它会将消息从队列中取出并执行。如果没有,它会等待新的消息到达。

下面是一个简单的例子,演示了事件循环的工作原理:


console.log('start');

setTimeout(() => {
   
   

  console.log('timeout');

}, 0);

Promise.resolve().then(() => {
   
   

  console.log('promise');

});

console.log('end');

输入内容:

start ==> end ==> promise ==> timeout

解释:

首先输出 start 和 end,然后遇到 Promise.resolve(),将其放入微任务队列中,继续执行,遇到 setTimeout,将其放入宏任务队列中,最后输出 promise。由于 setTimeout 的延时时间为 0,因此会在当前宏任务执行完毕后立即执行,输出 timeout。

结论:

微任务队列先于 宏任务队列先执行

4. 课程总结

3.jpg

目录
相关文章
|
4月前
|
存储 机器学习/深度学习 人工智能
阿里云环境下 Runway 深度部署:从技术原理到 AIGC 视频生成落地
Runway作为AI视频生成标杆,融合扩散模型与多模态技术,依托潜空间优化与时空注意力机制,实现高效高质视频生成。结合阿里云算力与API生态,支持版权合规、运镜控制与多模态联动,广泛应用于影视、广告与游戏领域,推动内容创作智能化升级。
769 0
|
SQL 安全 前端开发
让你彻底了解SQL注入、XSS和CSRF
了解SQL注入、XSS和CSRF
372 7
|
12月前
|
NoSQL 前端开发 Redis
单点登录云平台子系统集成方式
单点登录云平台子系统集成方式
447 0
|
量子技术
量子雷达:隐身技术的挑战者与未来防御系统
【9月更文挑战第19天】量子雷达凭借其突破隐身技术、高灵敏度及抗干扰性的优势,正成为未来防御系统的关键组成部分。本文深入探讨了量子雷达如何挑战传统隐身技术,并介绍了其在反隐身作战、导弹防御及空间探测等领域的广阔应用前景。随着技术进步,量子雷达将彻底改变现代战争模式,提升防御体系的效能。中国在这一领域已取得显著进展,展现出量子雷达的强大潜力。
|
传感器 Go 智能硬件
使用Golang开发硬件驱动
使用Golang开发硬件驱动
|
Cloud Native 安全 数据中心
|
负载均衡 Java 网络架构
Spring Cloud原理详解
介绍了Spring Cloud的原理和核心组件,包括服务注册与发现、配置管理、负载均衡、断路器、智能路由、分布式消息传递、分布式追踪和服务熔断等,旨在帮助开发人员快速构建和管理微服务架构中的分布式系统。
614 0
|
供应链 监控 安全
物联网(IoT)的安全性挑战及其应对策略
【5月更文挑战第9天】物联网(IoT)的快速发展带来设备多样性和安全漏洞,易受DDoS攻击、数据隐私泄露及供应链威胁。为应对挑战,需强化设备安全设计,建立认证和授权机制,加密数据传输,实施安全事件监控及加强供应链管理,以确保IoT的健康发展。
1025 3
|
存储 机器学习/深度学习 人工智能
迎接AI挑战:构建新一代AI网络基础设施
随着人工智能(AI)技术的飞速发展,AI模型的复杂度和数据规模急剧增加,对基础设施的需求提出了前所未有的挑战。传统的互联网基础设施已难以满足AI技术对高性能计算、大规模数据处理和低延迟网络的需求,从而催生了新一代AI基础设施的诞生。本文旨在深入探讨新一代AI基础设施的特点、优势,并介绍其在混合云环境下的应用方案。
|
Linux Docker 异构计算
模型部署 — PaddleNLP 基于 Paddle Serving 快速使用(服务化部署 - Docker)— 图像识别 + 信息抽取(UIE-X)
模型部署 — PaddleNLP 基于 Paddle Serving 快速使用(服务化部署 - Docker)— 图像识别 + 信息抽取(UIE-X)
463 0

热门文章

最新文章