🚀 JavaScript 基础(第 03 周):理解每个开发者都应该掌握的概念

“编写 JavaScript 代码是一回事,但理解幕后发生的事情才是让你成为更好的开发者的关键。”

当我刚开始学习 JavaScript 时,我知道如何声明变量和编写函数。然而,我经常会问自己以下问题:

  • 为什么有三种声明变量的方式?
  • 什么是提升(hoisting)?
  • JavaScript 是如何执行我的代码的?
  • 为什么内部函数可以访问其父函数的变量?
  • this 关键字究竟指的是什么?
  • 为什么开发者总是谈论编写干净的代码?

本周,我专注于理解这些 JavaScript 核心概念,而不是简单地记忆语法。

在这篇文章中,我将以初学者友好的方式,通过示例和实际解释来讲解每个概念。


📚 涵盖的主题

  • 变量(varletconst
  • 提升(Hoisting)
  • 词法作用域(Lexical Scope)
  • 执行上下文(Execution Context)
  • 调用栈(Call Stack)
  • 闭包(Closures)
  • this 绑定
  • DRY 原则
  • KISS 原则

让我们从头开始。


📦 JavaScript 中的变量

什么是变量?

变量是一个用于在内存中存储数据的命名容器

与其反复编写相同的值,我们将其存储在变量中,并在需要时重用它。

例如,

let name = "Sai";

console.log(name);

Enter fullscreen mode Exit fullscreen mode

输出

Sai

Enter fullscreen mode Exit fullscreen mode

这里,

  • let → 变量声明关键字
  • name → 变量名
  • "Sai" → 存储的值

为什么需要变量?

想象一下编写这样的代码:

console.log("Sai");
console.log("Sai");
console.log("Sai");

Enter fullscreen mode Exit fullscreen mode

如果值发生变化,则必须更新每一处。

使用变量,

let name = "Sai";

console.log(name);
console.log(name);
console.log(name);

Enter fullscreen mode Exit fullscreen mode

现在只需修改一行即可更新所有使用处。

变量提升了:

  • 可读性
  • 可重用性
  • 可维护性

变量的类型

JavaScript 提供了三种声明变量的方式。

  • var
  • let
  • const

虽然这三种方式都用于创建变量,但它们的行为有所不同。


var

var 是在 JavaScript 最初版本中引入的。

特性:

  • 函数作用域
  • 可以重新声明
  • 可以重新赋值
  • 会被提升

示例

var city = "Hyderabad";

var city = "Bangalore";

console.log(city);

Enter fullscreen mode Exit fullscreen mode

输出

Bangalore

Enter fullscreen mode Exit fullscreen mode

因为 var 允许重新声明,它可能会意外覆盖已有的值。


let

let 是在 ES6 中引入的。

特性

  • 块级作用域
  • 不能重新声明
  • 可以重新赋值

示例

let age = 20;

age = 21;

console.log(age);

Enter fullscreen mode Exit fullscreen mode

输出

21

Enter fullscreen mode Exit fullscreen mode


const

当变量不应被重新赋值时,使用 const

特性

  • 块级作用域
  • 不能重新声明
  • 不能重新赋值
const PI = 3.14;

console.log(PI);

Enter fullscreen mode Exit fullscreen mode

输出

3.14

Enter fullscreen mode Exit fullscreen mode


📈 提升(Hoisting)

什么是提升?

提升是 JavaScript 在执行代码前先处理声明的行为。

这并不意味着代码在物理上发生了移动。相反,在内存创建阶段,JavaScript 会在执行开始前准备好变量和函数。

示例

console.log(language);

var language = "JavaScript";

Enter fullscreen mode Exit fullscreen mode

输出

undefined

Enter fullscreen mode Exit fullscreen mode

内部 JavaScript 会这样处理:

var language;

console.log(language);

language = "JavaScript";

Enter fullscreen mode Exit fullscreen mode

使用 letconst 声明的变量也会被提升,但它们会留在暂时性死区(TDZ)中,直到被初始化。


🌍 词法作用域

作用域决定了变量可以在哪里被访问。

词法作用域意味着内部函数可以访问其外部函数中声明的变量,因为它在代码中的位置。

示例

function outer(){

    let message="Hello";

    function inner(){

        console.log(message);

    }

    inner();

}

outer();

Enter fullscreen mode Exit fullscreen mode

输出

Hello

Enter fullscreen mode Exit fullscreen mode

JavaScript 首先在 inner() 内部搜索。如果找不到变量,它会查找外部函数。

这个搜索过程被称为作用域链


⚙️ 执行上下文

在 JavaScript 执行任何代码之前,它会创建一个执行上下文

可以将其视为 JavaScript 的工作区。

它存储:

  • 变量
  • 函数
  • 作用域
  • this 的值

每个执行上下文都有两个阶段。

内存创建阶段

在此阶段,

  • 变量被分配内存。
  • var 变量被初始化为 undefined
  • 函数声明被存储。

执行阶段

在此阶段,

  • 变量获得值。
  • 语句逐行执行。
  • 函数调用会创建新的执行上下文。

📚 调用栈

调用栈跟踪函数的执行。

它遵循后进先出(LIFO)原则。

示例

function first(){

    second();

}

function second(){

    console.log("Inside Second");

}

first();

Enter fullscreen mode Exit fullscreen mode

输出

Inside Second

Enter fullscreen mode Exit fullscreen mode

执行流程

first()



second()



console.log()



Return



Return

Enter fullscreen mode Exit fullscreen mode


🔒 闭包

JavaScript 最强大的特性之一是闭包。

当内部函数即使在外部函数执行完毕后仍能记住其外部函数的变量时,就会创建闭包。

示例

function counter(){

    let count = 0;

    return function(){

        count++;

        console.log(count);

    }

}

const increment = counter();

increment();

increment();

increment();

Enter fullscreen mode Exit fullscreen mode

输出

1
2
3

Enter fullscreen mode Exit fullscreen mode

闭包通常用于:

  • 计数器
  • 数据隐私
  • 事件处理器
  • 模块模式

🎯 理解 this

this 关键字指的是调用函数的对象。

它的值取决于函数是如何被调用的

隐式绑定

const user = {

    name:"Sai",

    greet(){

        console.log(this.name);

    }

}

user.greet();

Enter fullscreen mode Exit fullscreen mode

输出

Sai

Enter fullscreen mode Exit fullscreen mode


显式绑定

JavaScript 提供了 call()apply()bind() 来显式决定 this 应该指向什么。

function greet(){

    console.log(this.name);

}

const user={

    name:"Sai"

};

greet.call(user);

Enter fullscreen mode Exit fullscreen mode

输出

Sai

Enter fullscreen mode Exit fullscreen mode


new 绑定

function Student(name){

    this.name=name;

}

const s1=new Student("Sai");

console.log(s1.name);

Enter fullscreen mode Exit fullscreen mode

输出

Sai

Enter fullscreen mode Exit fullscreen mode


箭头函数

箭头函数没有自己的 this

相反,它们从其周围的作用域继承 this


✨ DRY 原则

DRY 代表

不要重复自己(Don't Repeat Yourself)

与其重复代码,

console.log("Welcome Sai");

console.log("Welcome Rahul");

Enter fullscreen mode Exit fullscreen mode

不如写成

function greet(name){

    console.log(`Welcome ${name}`);

}

greet("Sai");

greet("Rahul");

Enter fullscreen mode Exit fullscreen mode

好处

  • 可重用的代码
  • 易于维护
  • 减少重复

💡 KISS 原则

KISS 代表

保持简单,愚蠢(Keep It Simple, Stupid)

简单的代码比复杂的代码更容易理解。

与其写成

function isEven(num){

    if(num%2===0){

        return true;

    }

    else{

        return false;

    }

}

Enter fullscreen mode Exit fullscreen mode

不如写成

function isEven(num){

    return num%2===0;

}

Enter fullscreen mode Exit fullscreen mode

简单的代码是:

  • 易于调试
  • 易于维护
  • 易于理解

🎯 要点总结

完成本周的学习后,我明白了:

  • 变量用于存储数据。
  • 提升发生在内存创建阶段。
  • 词法作用域决定了变量在哪可以被访问。
  • 执行上下文为代码执行提供了环境。
  • 调用栈管理函数的执行。
  • 闭包在函数完成后保留变量。
  • this 的值取决于函数是如何被调用的。
  • 遵循 DRY 和 KISS 原则有助于编写干净且可维护的代码。

📝 结论

学习 JavaScript 不仅仅是编写代码——而是理解语言在幕后是如何工作的。执行上下文、作用域、闭包和 this 绑定等概念解释了我们的代码为什么会以这种方式运行。通过将这些基础知识与 DRY 和 KISS 等干净的编码原则相结合,我们可以编写不仅正确,而且易于阅读、维护和扩展的代码。