JavaScript 是一种高级的、解释型的编程语言。

  • 高级:意味着它更接近人类语言(英语),而不是枯燥的机器指令(0 和 1)。
  • 解释型:意味着代码不需要复杂的“翻译”过程,浏览器拿到代码就能直接执行。

如果说HTML是房子的框架,那么CSS就是房子的装修,而JavaScript就是房子的智能系统。
和java并无关系,只是名字像而已。


一,导入JS

1.行内式

直接把代码写在 HTML 标签的属性里(比如 onclick)。

<button onclick="alert('点我干嘛?')">点我</button>

2.内部式

在 HTML 文档中使用

<script>
        let message = "欢迎来到 JS 世界";
        console.log(message);
    </script>

3.外部式

像 CSS 引入 .css 一样,JS 也有自己的 .js 文件。
在 HTML 中写:

<script src="myCode.js"></script>

意思就是从这个文件中读取。

注意,在这里,如果引入了外部文件,还想在再这里写当前网页的专属逻辑时,需要分两个标签写,写在引入外部文件的script中时就会无视内部的代码。

还有,默认情况下,他会严格按照在HTML中书写script标签的先后顺序来执行。
所以,浏览器在解析 HTML 时,如果遇到一个普通的外部 JS 标签,它会执行以下动作:

  1. 停止解析后面的 HTML。
  2. 下载这个 JS 文件。
  3. 运行这个 JS 文件。
  4. 继续解析后面的 HTML 或下一个

所以,如果你的第一个 JS 文件 main.js 体积巨大或者服务器网速极慢,第二个 <script> 里的代码就只能在那干等着,而且用户此时看到的网页可能也是卡住的、半成品的。

为了不让网页因为等 JS 而卡死,现代开发通常会给标签加两个属性:

  1. defer(最推荐,推迟执行)

    <script src="main.js" defer></script>
    <script src="other.js" defer></script>
    

    浏览器在后台默默下载 JS,不耽误继续画网页

  2. async(异步执行)

    <script src="main.js" async></script>
    

    谁先下完谁先跑。


二,基础语法

1.变量

在 JS 里,你想记住一个数字或一段话,必须把它放进变量里。

最常用以下关键字。

let age = 18;     // 声明一个叫 age 的变量,存入 18
const name = "JS"; // 声明一个叫 name 的常量

age = 19;         // 没问题,修改了变量的值
// name = "Java"; // 报错!常量不能改

还可以用var,但是需要避免使用,除非要维护遗留代码或理解特殊行为。
var声明的变量会成为window(浏览器)或global的属性,
它可以重复声明同名变量,后面的覆盖前面的。
它会被提升到作用域顶部,但只提升声明,初始化仍然在原位置,声明前访问回事undefined,let就不能在声明前使用,会报错。


2. 数据类型

JS 是一门“聪明”的语言,它会根据你写的内容自动判断类型。

1. 基本数据类型(7 种)
类型 例子 描述
String (字符串) "Hello" 文本,必须用引号(单双引号均可)
Number (数字) 100, 3.14 所有的数字,不分整数小数
Boolean (布尔) true, false 只有“对”和“错”两种状态
Undefined let x; 声明了变量但没给值,它就是“未定义”
Null null 表示“空”,通常用来清空一个变量
Symbol (符号) Symbol("id") 创建唯一标识符,常用于对象属性名,避免冲突
BigInt (大整数) 9007199254740991n 表示超大整数,末尾必须加 n
2. 引用类型(Object 及其衍生)
类型 例子 描述
Object (对象) { name: "张三" } 复杂数据,可以装很多属性
Array (数组) [1, 2, 3] 有序的数据列表,可以装多个值(类型可不相同)
Function (函数) function fn() {} 可执行的代码块,本身也是一种特殊的对象
Date (日期) new Date() 处理日期和时间
RegExp (正则) /ab+c/ 用于模式匹配
3. 如何区分数组和普通对象?
let arr = [1, 2, 3];
let obj = { 0: 1, 1: 2, 2: 3 };

console.log(Array.isArray(arr));   // true
console.log(Array.isArray(obj));   // false
4. typeof 的常见陷阱
typeof "hello"      // "string"
typeof 123          // "number"
typeof true         // "boolean"
typeof undefined    // "undefined"
typeof null         // "object"   ❌ JS 遗留的 bug
typeof {}           // "object"
typeof []           // "object"   ❌ 数组也返回 "object"
typeof function(){} // "function" ✅
typeof Symbol()     // "symbol"
typeof 123n         // "bigint"

判断 null 的正确方法value === null
判断数组的正确方法Array.isArray(value)


3.运算符

和python并无什么不同。。。

算术运算符(加减乘除 + 取余)

  • +:加法
  • -:减法
  • *:乘法
  • /:除法
  • %:取余数(例如 10 % 3 的结果是 1

赋值运算符

  • =:把右边的值赋给左边的变量(例如 x = 10

比较运算符

  • >:大于
  • <:小于
  • ===:严格相等(值和类型都必须相同,推荐使用
  • ==:宽松相等(会尝试自动转换类型再比较,容易产生 Bug,不推荐使用

注意:判断相等一定要用 ===,不要用 ==

逻辑运算符

  • &&(AND):两者都为真,结果才为真
  • ||(OR):只要有一个为真,结果就为真
  • !(NOT):取反(真变假,假变真)

4. 流程控制

和 Python 也差不多,就是格式的不同(Python 用缩进和冒号,JS 用花括号 {} 和分号)。

① 条件判断:if...else
let score = 85;

if (score >= 90) {
    console.log("优秀");
} else if (score >= 60) {
    console.log("及格");
} else {
    console.log("不及格");
}
② 多分支精确匹配:switch

当有多个固定值需要匹配时,比多个 else if 更清晰。

let grade = "B";

switch (grade) {
    case "A":
        console.log("90分以上");
        break;   // 跳出 switch,不加会继续往下执行
    case "B":
        console.log("80分以上");
        break;
    case "C":
        console.log("70分以上");
        break;
    default:
        console.log("需要努力");
}

switch 使用严格相等 === 判断,注意 break 的穿透问题。

③ 循环结构
循环类型 适用场景 示例代码
for 知道循环次数(最常用) for (let i = 0; i < 5; i++) { ... }
while 条件前置,循环次数未知 while (条件) { ... }
do...while 至少执行一次再判断 do { ... } while (条件);

示例:

// for 循环
for (let i = 0; i < 5; i++) {
    console.log("当前数字是:" + i);
}

// while 循环
let i = 0;
while (i < 5) {
    console.log(i);
    i++;
}

// do...while (至少执行一次)
let j = 10;
do {
    console.log("至少运行一次");
    j++;
} while (j < 5);
④ 循环控制:breakcontinue
  • break:立即跳出整个循环(或 switch)。
  • continue:跳过本次循环的剩余代码,直接进入下一次迭代。
for (let i = 0; i < 5; i++) {
    if (i === 2) continue;   // 跳过 i=2 时的输出
    if (i === 4) break;      // 到 i=4 时完全停止循环
    console.log(i);          // 输出 0, 1, 3
}
⑤ 专门用于遍历的循环
循环 适用对象 示例
for...in 对象的(属性名) for (let key in obj) { ... }
for...of 可迭代对象的值(数组、字符串、Map、Set) for (let val of arr) { ... }
// 对象用 for...in
let person = { name: "张三", age: 18 };
for (let key in person) {
    console.log(key, person[key]);
}

// 数组用 for...of
let arr = [10, 20, 30];
for (let value of arr) {
    console.log(value);  // 10, 20, 30
}

for...in 遍历数组会得到索引(字符串),通常不推荐;遍历数组推荐用 forfor...of

补充,Map和Set是什么?forEach方法?

在上面提到for……of的时候有说他们是可迭代对象,但是之前没遇到过。

  1. Map(映射)是一种键值对的集合,任何值都可以作为键或者值,它比传统的普通对象更灵活。
    map.size可以获取大小,相比较{}来说,它频繁增删键值对更好。
    使用set(键,值)添加键值对,get获取,has判断,clear清空。
  2. Set是一种值的有序集合,它像是python中的集合那样,会自动去重。
    也有size属性,使用add添加,has判断存在,delete删除,clear清空。
  3. forEach 并不是所有可迭代对象(iterable)都有的方法。它只存在于特定的内置类型(Array, Map, Set 等)。
    总之,它可以用来很方便的获取到value,key并通过箭头函数来做处理。

5. 函数 (Functions):代码的“模具”

函数是把一段常用的代码封装起来,给它起个名字,可以重复调用。

// 1. 定义函数
function sayHi(userName) {
    console.log("你好," + userName);
}

// 2. 调用函数
sayHi("阿强"); 
sayHi("小红");
① 返回值:return

函数可以返回一个结果,调用者可以拿到这个值继续使用。如果没有 return,函数默认返回 undefined

function add(a, b) {
    return a + b;   // 返回计算结果
}

let result = add(3, 5);
console.log(result);  // 8

return 会立刻结束函数执行,后面的代码不再运行。

② 参数默认值

如果调用时没有传某个参数,可以给它一个默认值。

function greet(name = "匿名用户") {
    console.log("Hello, " + name);
}

greet("阿强");   // Hello, 阿强
greet();         // Hello, 匿名用户
③ 函数表达式:把函数赋给变量

函数也可以当作值,赋给变量。这种写法叫函数表达式

const sayHi = function(userName) {
    console.log("你好," + userName);
};

sayHi("阿强");

函数表达式与函数定义的区别:函数定义会被“提升”,可以在定义前调用;函数表达式必须先赋值后调用。

④ 箭头函数 (Arrow Function):ES6 简洁写法

语法更短,并且它内部的 this 行为与普通函数不同(适合回调场景)。

// 普通写法
const add1 = function(a, b) {
    return a + b;
};

// 箭头函数写法
const add2 = (a, b) => a + b;   // 单行可省略 { return }

console.log(add2(3, 5));  // 8

箭头函数简化规则:

  • 只有一个参数时,括号可省略:x => x * 2
  • 函数体只有一条 return 语句时,可省略 {}return
⑤ 函数作用域

函数内部声明的变量,外部无法访问(局部变量)。函数可以访问外部(全局)变量。

let globalVar = "我是全局的";

function test() {
    let localVar = "我是局部的";
    console.log(globalVar);  // ✅ 可以访问全局
}

test();
console.log(localVar);  // ❌ 报错:localVar is not defined
⑥ 回调函数:把函数当作参数传给另一个函数

这是 JavaScript 中非常常见的模式,例如事件处理、定时器、数组遍历。

function processUserInput(callback) {
    let name = "张三";
    callback(name);   // 调用传进来的函数
}

// 传入一个匿名函数作为回调
processUserInput(function(name) {
    console.log("你好," + name);
});

箭头函数常用于回调:

[1, 2, 3].forEach(item => {
    console.log(item * 2);
});
⑦ 立即执行函数 (IIFE)

定义后立刻执行,常用于创建一个独立的作用域,避免污染全局。

(function() {
    let temp = "只能在内部访问";
    console.log("函数已执行");
})();
⑧ 函数声明与函数表达式的区别(提升)
类型 示例 能否在定义前调用
函数声明 function foo() {} ✅ 可以(被提升)
函数表达式 const foo = function() {} ❌ 不可以
// 函数声明 - 可以提前调用
sayHello();   // 正常运行
function sayHello() { console.log("Hello"); }

// 函数表达式 - 提前调用会报错
sayHi();      // ❌ Cannot access 'sayHi' before initialization
const sayHi = function() { console.log("Hi"); };
⑨ 常见内置函数举例
函数 作用 示例
console.log() 打印输出 console.log("hello")
parseInt() 字符串转整数 parseInt("3.14")3
parseFloat() 字符串转浮点数 parseFloat("3.14")3.14
setTimeout() 延迟执行 setTimeout(() => console.log("1秒后"), 1000)
setInterval() 循环定时执行 setInterval(() => console.log("每2秒"), 2000)
⑩ 各场景推荐
场景 推荐写法
简单单行计算 箭头函数(省略 {}return
需要自己的 this 或不熟悉 this 规则 普通函数(function
需要提前调用(提升) 函数声明
作为另一个函数的参数(回调) 箭头函数 或 匿名函数表达式
需要立即执行并隔离作用域 IIFE(立即执行函数)

6. 对象(Object):复杂数据的容器

在 JS 中,对象是属性(变量)和方法(函数)的集合。它就像一个真实的物体,既有状态(长什么样),又有行为(能做什么)。


6.1 创建与属性访问(语法细节)

创建对象最标准的方式是 字面量。但在访问属性时,有两种非常重要的语法:

let user = {
    name: "张三",
    "my-age": 18, // 含有特殊字符的键必须加引号
    city: "北京"
};

// 1. 点表示法 (Dot Notation) —— 最常用
console.log(user.name); 

// 2. 方括号表示法 (Bracket Notation) —— 两种必选场景:
// 场景 A:键名不符合变量命名规则(如含有横杠、数字开头)
console.log(user["my-age"]); 

// 场景 B:键名存在变量里(动态访问)
let key = "city";
console.log(user[key]); // 等同于 user.city


6.2 属性的动态增删

JS 的对象是动态的,你可以在程序运行的任何时候修改它的结构。

let car = { brand: "Tesla" };

// 新增
car.color = "红色"; 

// 修改
car.brand = "BYD";

// 删除 (使用 delete 关键字)
delete car.color; 

console.log(car.color); // 输出 undefined,因为已经被删除了


6.3 对象的方法与 this 关键字

方法是存储在对象属性中的函数。this 关键字是对象的“自称”。

let player = {
    nickname: "头号玩家",
    level: 10,
    // ES6 简写方法语法
    upgrade() {
        // this 指向调用这个方法的对象
        this.level++; 
        console.log(`${this.nickname} 升级了!当前等级:${this.level}`);
    }
};

player.upgrade();

使用字面量的方式创建对象,更适合创建一个独立,唯一存在的单例,不需要复用。
待会要讲的class才适合多个相似对象,它和python中的class很像。


6.4 遍历与静态工具方法

当你有一堆数据存在对象里时,你需要手段把它们拿出来。

方法 作用 返回值类型
for...in 遍历对象所有的键名 循环执行
Object.keys(obj) 获取对象所有键名 数组 (Array)
Object.values(obj) 获取对象所有键值 数组 (Array)

示例代码:

let scores = { math: 90, english: 80 };

// 遍历
for (let subject in scores) {
    console.log(`${subject} 的分数是 ${scores[subject]}`);
}

// 快速检查
console.log(Object.keys(scores)); // ["math", "english"]


6.5 现代类与继承 (Class & Inheritance)

这是目前大型项目中最主流的写法。它让代码看起来更有条理。

// 1. 类:对象的蓝图
class User {
    constructor(name, email) {
        this.name = name;
        this.email = email;
    }
    showInfo() {
        console.log(`用户: ${this.name}, 邮箱: ${this.email}`);
    }0
}

// 2. 继承:在蓝图基础上扩展
class Admin extends User {
    constructor(name, email, role) {
        super(name, email); // 必须先调用 super,继承父类的初始化逻辑
        this.role = role;
    }
    // 重写父类方法
    showInfo() {
        super.showInfo(); // 先执行父类的逻辑
        console.log(`权限级别: ${this.role}`);
    }
}

const boss = new Admin("老王", "wang@test.com", "超级管理员");
boss.showInfo();


6.6 核心必读:解构赋值 (Destructuring)

这是 ES6 引入的极简语法,现在几乎随处可见。

const settings = { theme: "Dark", fontSize: "14px", showSidebar: true };

// 以前:let theme = settings.theme;
// 现在:直接从对象中“提取”出同名变量
const { theme, fontSize } = settings;

console.log(theme); // "Dark"

更多推荐