js 语法(保姆级教程)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

截止目前, 星球 内专栏累计输出 90w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 3100+ 小伙伴加入学习 ,欢迎点击围观

前言

在现代 Web 开发中,JavaScript(JS)作为核心语言,扮演着不可或缺的角色。无论是构建交互式网页、开发桌面应用,还是后端服务,掌握其语法基础都是开发者的核心能力。本文将从变量、函数、作用域到高级特性,系统性地解析 JS 语法的关键知识点,并通过实际案例帮助读者理解。无论你是编程新手还是有一定经验的开发者,都能在此找到适合自己的学习路径。


变量与数据类型:构建程序的基石

变量声明的三种方式

在 JS 中,变量通过 varletconst 声明。这三者的核心区别在于作用域和可变性:

  • var:存在函数作用域,且会“变量提升”(即声明提前到函数顶部)。
  • letconst:属于块级作用域(如 {} 内),且不会变量提升。const 声明的变量不可重新赋值,但对象/数组内容可修改。

案例 1:比较 varlet 的作用域差异

function example() {
  var a = 10;
  if (true) {
    let b = 20;
    console.log(a); // 输出 10(var 的函数作用域)
    console.log(b); // 输出 20(let 的块级作用域)
  }
  console.log(b); // 报错:b 未定义(超出块级作用域)
}

数据类型:原始类型与引用类型

JS 数据类型分为 原始类型(Primitive Types)和 引用类型(Reference Types):

  • 原始类型numberstringbooleannullundefinedsymbol(ES6 新增)、bigint
  • 引用类型:对象(如 ObjectArrayFunction 等),通过内存地址引用值。

比喻:原始类型像“书本的 ISBN 号”,直接存储具体值;而引用类型像“书架的编号”,指向存储内容的地址。

案例 2:理解值传递的差异

// 原始类型:值直接复制  
let num1 = 5;
let num2 = num1; // 复制值为 5  
num2 = 10;  
console.log(num1); // 5(原始值不共享)  

// 引用类型:地址共享  
let arr1 = [1, 2];  
let arr2 = arr1;  
arr2.push(3);  
console.log(arr1); // [1, 2, 3](修改通过地址关联)

函数:代码复用的核心

函数的三种定义方式

  1. 函数声明

    function add(a, b) {  
      return a + b;  
    }  
    

    函数提升(Hoisting)使其可在声明前调用。

  2. 函数表达式

    const multiply = function(a, b) {  
      return a * b;  
    };  
    

    需先定义后调用,无变量提升。

  3. 箭头函数(ES6):

    const subtract = (a, b) => a - b;  
    

    简洁语法,无 this 绑定(继承外层 this)。

案例 3:箭头函数简化回调

const numbers = [1, 2, 3];  
const doubled = numbers.map(num => num * 2); // 输出 [2,4,6]  

参数默认值与剩余参数

ES6 引入了参数默认值和剩余参数语法,提升函数灵活性:

function greet(name = "Guest", ...args) {  
  console.log(`Hello, ${name}!`);  
  console.log("Additional args:", args);  
}  
greet("Alice", "Welcome", "to JS"); // 输出:Hello Alice! 和 ["Welcome", "to JS"]

作用域与闭包:理解变量的“可见性”

作用域类型

  1. 全局作用域:在函数外部声明的变量,全局可用。
  2. 函数作用域:由 function 定义的独立作用域。
  3. 块级作用域:通过 {}let/const 定义的局部作用域。

案例 4:作用域嵌套

let globalVar = "I'm global";  

function outer() {  
  const outerVar = "Outer";  
  if (true) {  
    let innerVar = "Inner";  
    console.log(globalVar, outerVar, innerVar); // 全局、外层、内层变量均可见  
  }  
  console.log(innerVar); // 报错:innerVar 未定义(超出块级作用域)  
}  

闭包:变量的“隐形包裹”

闭包是函数与对其词法环境(变量作用域)的引用。通过闭包可实现数据隐私和状态保留:

function createCounter() {  
  let count = 0;  
  return function() {  
    count++;  
    return count;  
  };  
}  
const counter = createCounter();  
console.log(counter()); // 1  
console.log(counter()); // 2 (count 的值被闭包保留)

对象与类:面向对象的实现

对象的字面量与方法

JS 对象是键值对集合,可通过字面量快速创建:

const person = {  
  name: "Alice",  
  greet() {  
    return `Hello, my name is ${this.name}`;  
  }  
};  
console.log(person.greet()); // 输出 "Hello, my name is Alice"

类与继承(ES6)

ES6 引入 class 语法,简化面向对象编程:

class Animal {  
  constructor(name) {  
    this.name = name;  
  }  
  speak() {  
    return `${this.name} makes a sound`;  
  }  
}  

class Dog extends Animal {  
  speak() {  
    return `${this.name} barks`; // 覆写父类方法  
  }  
}  

const myDog = new Dog("Buddy");  
console.log(myDog.speak()); // 输出 "Buddy barks"

高级语法特性

解构赋值与扩展运算符

ES6 提供简洁语法处理数据结构:

// 解构赋值  
const { name, ...rest } = { name: "Alice", age: 30, city: "NY" };  
console.log(name); // Alice  
console.log(rest); // { age:30, city:"NY" }  

// 扩展运算符  
const arr1 = [1, 2];  
const arr2 = [...arr1, 3, 4]; // [1,2,3,4]

Promise 与异步编程

处理异步操作时,Promiseasync/await 是核心工具:

function fetchData() {  
  return new Promise((resolve, reject) => {  
    setTimeout(() => resolve("Data loaded!"), 1000);  
  });  
}  

async function start() {  
  try {  
    const result = await fetchData();  
    console.log(result); // 1秒后输出 "Data loaded!"  
  } catch (error) {  
    console.error(error);  
  }  
}  
start();

常见问题与最佳实践

变量提升陷阱

console.log(x); // 输出 undefined(var 被提升但未初始化)  
var x = 10;  

解决方案:优先使用 letconst 避免意外行为。

类型判断与严格比较

console.log(1 == "1"); // true(类型转换)  
console.log(1 === "1"); // false(严格比较)  

建议:始终使用 ===!== 避免隐式类型转换错误。


结论

掌握 JS 语法不仅是代码书写的起点,更是构建复杂应用的基石。从变量、函数到闭包、类,每个概念都在实际开发中发挥关键作用。本文通过案例和比喻,帮助读者逐步理解语法背后的逻辑。建议读者通过实践项目巩固知识,例如尝试用闭包实现计数器,或用 async/await 编写 API 请求。记住,语法是工具,而解决问题的能力才是核心——保持好奇心,持续探索 JS 的无限可能。

最新发布