深入理解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

目录
相关文章
|
7月前
|
前端开发 JavaScript 开发者
深入理解JavaScript:从基础到高级应用
深入理解JavaScript:从基础到高级应用
119 0
|
2月前
|
自然语言处理 JavaScript 前端开发
深入理解JavaScript中的闭包:原理与实战
【10月更文挑战第12天】深入理解JavaScript中的闭包:原理与实战
|
2月前
|
JavaScript 前端开发 开发者
深入理解JavaScript原型链:从基础到进阶
【10月更文挑战第13天】深入理解JavaScript原型链:从基础到进阶
30 0
|
7月前
|
JavaScript 前端开发 算法
【JavaScript技术专栏】深入理解JavaScript垃圾回收机制
【4月更文挑战第30天】本文深入解析JavaScript的垃圾回收机制,旨在帮助开发者理解其工作原理。内容涵盖垃圾回收的概念、标记阶段、清除阶段,以及优化策略如增量回收、分代回收和并行回收。此外,还介绍了引用计数、标记-清除等常见垃圾回收算法,并讨论了内存泄漏的原因及解决方法,强调理解垃圾回收对编写高效代码的重要性。
179 0
|
7月前
|
前端开发 JavaScript
前端深入理解JavaScript面向对象编程与Class
随着JavaScript的发展,ECMAScript 6(ES6)引入了许多新的语言特性和语法糖,其中包括了面向对象编程的Class(类)机制。Class提供了一种更简洁、更直观的方式来定义对象和操作对象的行为。本文将介绍ES6中Class的概念、语法和特性,并通过示例代码来说明其实际应用。
|
存储 缓存 自然语言处理
深入理解JavaScript闭包:从概念到实践
深入理解JavaScript闭包:从概念到实践
116 0
|
JSON JavaScript 前端开发
深入理解JavaScript-一切皆对象
深入理解JavaScript-一切皆对象
206 0
深入理解JavaScript-一切皆对象
|
自然语言处理 前端开发 JavaScript
深入理解JavaScript-开篇
深入理解JavaScript-开篇
101 0
|
JavaScript 前端开发 API
深入理解JavaScript- new 做了什么
上文我们就 Object 进行分析阐述,聊到了对象的创建,其中 new 是创建对象的重要关键字,这节我们讲讲 new,聊一聊 new 是做什么用的
184 0
|
JavaScript 前端开发 开发者
深入理解JavaScript函数
深入理解JavaScript函数
121 0
下一篇
DataWorks