JavaScript 基础入门教程:从零开始构建前端交互和逻辑
欢迎来到 JavaScript 的世界!JavaScript 是一种强大、灵活且用途广泛的编程语言,它是现代 Web 开发的基石,使得网页不再是静态的,而是充满活力和交互性。除了前端开发,JavaScript 借助 Node.js 等技术也深入到了后端、移动应用、桌面应用甚至物联网领域。
本教程将带你从零开始,系统地学习 JavaScript 的基础知识。无需任何编程背景,只要你有好奇心和学习的热情,就能一步步掌握这门重要的语言。
预计阅读时间: 约 30-45 分钟
适合人群: 完全没有编程经验或对 JavaScript 感兴趣的初学者。
我们将涵盖以下核心主题:
- 什么是 JavaScript?为什么学习它?
- JavaScript 的运行环境与如何在网页中使用它
- 你的第一个 JavaScript 程序:Hello, World!
- JavaScript 中的注释
- 变量:存储数据的地方
- 数据类型:JavaScript 世界里的“物种”
- 运算符:对数据进行操作
- 控制流程:让程序“思考”和“行动”
- 条件语句 (
if
,else if
,else
,switch
) - 循环语句 (
for
,while
,do...while
)
- 条件语句 (
- 函数:组织和重用代码块
- 数组:有序的数据集合
- 对象:无序的键值对集合
- 作用域:变量的“可见范围”
- JavaScript 如何与网页(DOM)交互
- 事件:响应用户或系统的动作
- 一些好的编程习惯和注意事项
- 总结与下一步
1. 什么是 JavaScript?为什么学习它?
简单来说,JavaScript (简称 JS) 是一种解释型的、基于原型的、多范式的、单线程的动态编程语言。
- 解释型: 代码不需要提前编译,而是由解释器逐行执行。
- 基于原型: 一种不同于传统类继承的对象创建方式(这是稍高级的概念,入门阶段了解即可)。
- 多范式: 支持面向过程、面向对象和函数式编程等多种编程风格。
- 单线程: 同一时间只能执行一个任务(但在浏览器环境中,可以通过事件循环、Web Workers 等机制模拟并发)。
- 动态: 变量的类型可以在运行时改变。
为什么学习 JavaScript?
- 前端开发的基石: 它是实现网页动态效果、用户交互和单页应用 (SPA) 的核心技术,与 HTML (结构) 和 CSS (样式) 并称 Web 开发三大件。
- 全栈开发的可能: 借助 Node.js,JavaScript 可以用于编写服务器端代码,实现前后端同构,减少切换语言的开销。
- 跨平台开发: React Native (移动应用), Electron (桌面应用) 等框架让你可以用 JS 开发原生体验的应用。
- 庞大的生态系统: 拥有世界上最大的开源社区和极其丰富的库和框架 (如 React, Vue, Angular, Node.js, Express 等)。
- 易于入门: 语法相对灵活,且浏览器自带运行环境,学习曲线相对平缓。
总而言之,掌握 JavaScript 意味着打开了通往现代软件开发广阔世界的大门。
2. JavaScript 的运行环境与如何在网页中使用它
JavaScript 主要运行在以下环境中:
- 浏览器: 这是最常见的环境,浏览器内置了 JavaScript 引擎(如 Chrome 的 V8,Firefox 的 SpiderMonkey),负责解析和执行 JavaScript 代码,从而操作网页内容。
- Node.js: 一个基于 Chrome V8 引擎的 JavaScript 运行时环境,让 JavaScript 可以在服务器端、命令行工具等非浏览器环境运行。
- 其他: 各类运行时环境(如 Deno)、嵌入式设备等。
对于初学者,我们主要关注浏览器环境。
在网页中使用 JavaScript
有两种主要方式在 HTML 页面中引入 JavaScript:
方式一:内联 <script>
标签
直接将 JavaScript 代码写在 <script>
标签内部。
“`html
我的第一个网页
这里的内容会被 JavaScript 改变。
“`
方式二:外部 JavaScript 文件
将 JavaScript 代码写在一个单独的 .js
文件中,然后在 HTML 中通过 <script src="...">
标签引用。这是更推荐的方式,因为它分离了 HTML 结构和 JavaScript 行为,使得代码更易于管理和维护。
首先,创建一个名为 script.js
的文件:
javascript
// script.js
console.log("Hello from external script!");
document.getElementById("demo2").innerHTML = "外部文件修改了内容!";
然后,在 HTML 文件中引用它:
“`html
引用外部 JavaScript 文件
这是原始内容。
“`
重要提示: 通常推荐将 <script>
标签放在 <body>
标签的闭合标签 </body>
之前。这是因为 JavaScript 代码往往需要操作页面上的 HTML 元素(即 DOM),如果脚本在元素加载完成之前就运行,可能会找不到对应的元素而报错。将脚本放在底部可以确保大多数 HTML 元素已经被浏览器解析。或者使用 defer
或 async
属性,但这属于稍高级的主题。
浏览器开发者工具
学习 JavaScript 时,浏览器开发者工具是你最好的朋友。特别是其中的 Console (控制台)。
- 如何打开控制台:
- 在 Chrome/Firefox/Edge 中,通常按
F12
键打开开发者工具。 - 或者在页面上右键,选择“检查”或“审查元素”,然后切换到“Console”标签。
- 在 Chrome/Firefox/Edge 中,通常按
- 控制台的作用:
- 查看
console.log()
输出的信息,用于调试。 - 直接在控制台中输入并执行 JavaScript 代码,进行实时测试。
- 查看 JavaScript 运行时的错误信息。
- 查看
从现在开始,当你看到 console.log()
时,记得打开控制台查看输出。
3. 你的第一个 JavaScript 程序:Hello, World!
经典的“Hello, World!”程序通常是学习任何语言的第一步。在 JavaScript 中,我们可以通过 alert()
或 console.log()
来实现。
使用 alert()
(会在浏览器中弹出一个对话框):
javascript
alert("Hello, World!");
使用 console.log()
(会在浏览器控制台打印信息):
javascript
console.log("Hello, World!");
将上面的代码放入 <script>
标签中并在浏览器中打开 HTML 文件,看看效果。你会发现 console.log()
更常用,因为它不会打断用户的操作。
4. JavaScript 中的注释
注释是代码中不会被执行的部分,用于解释代码的功能、用途、注意事项等,极大地提高了代码的可读性。
JavaScript 支持两种注释:
-
单行注释: 以
//
开头,直到行末。javascript
// 这是一行单行注释
let message = "Hello"; // 这行代码声明了一个变量 -
多行注释 (块级注释): 以
/*
开头,以*/
结尾,可以跨越多行。javascript
/*
这是一个多行注释的例子。
它可以用来解释一段较长的代码块
或者暂时禁用多行代码。
*/
let x = 10;
let y = 20;
善用注释是一个优秀程序员的习惯。
5. 变量:存储数据的地方
变量就像是计算机内存中的小盒子,你可以把数据放进去,给盒子起个名字,然后通过名字来获取或修改里面的数据。
在 JavaScript 中,声明变量主要使用三个关键字:var
, let
, const
。
使用 var
(老旧的方式,不推荐用于新代码)
“`javascript
var age = 30; // 声明一个名为 age 的变量,并赋值 30
var name; // 只声明变量,不赋值,此时它的值是 undefined
name = “Alice”; // 给已声明的变量赋值
var age = 35; // 可以重复声明同一个 var 变量 (这是 var 的一个问题)
console.log(age); // 输出 35
“`
var
的问题在于它的作用域规则比较宽松(函数作用域),且允许重复声明,这在大型项目中容易引起混淆和错误。
使用 let
(现代推荐的方式,可变)
let
声明的变量具有块级作用域(稍后解释),不允许在同一作用域内重复声明。
“`javascript
let count = 0; // 声明并赋值
count = count + 1; // 可以修改 let 变量的值
console.log(count); // 输出 1
let city = “Beijing”;
// let city = “Shanghai”; // 错误!SyntaxError: ‘city’ has already been declared
if (true) {
let blockVar = “Inside block”;
console.log(blockVar); // 输出 “Inside block”
}
// console.log(blockVar); // 错误!ReferenceError: blockVar is not defined (blockVar 在 if 块外部不可见)
“`
let
是当你需要一个变量其值可能会改变时的首选。
使用 const
(现代推荐的方式,常量)
const
声明一个常量,常量的值一旦赋值后就不能再修改。它也具有块级作用域,不允许重复声明。
“`javascript
const PI = 3.14159; // 声明并赋值,必须在声明时赋值
// PI = 3.0; // 错误!TypeError: Assignment to constant variable.
const username = “Bob”;
// const username = “Robert”; // 错误!SyntaxError: ‘username’ has already been declared
if (true) {
const anotherConst = “Another value”;
console.log(anotherConst); // 输出 “Another value”
}
// console.log(anotherConst); // 错误!ReferenceError: anotherConst is not defined
“`
注意: const
并不是说变量指向的那个内存地址不能变,而是说变量名不能重新指向另一个不同的值。对于基本数据类型(如数字、字符串),这意味着值不能变。但对于复杂数据类型(如对象、数组),虽然不能重新把变量指向一个新的对象或数组,但可以修改这个对象或数组内部的属性或元素。
“`javascript
const person = { name: “Alice”, age: 30 };
person.age = 31; // 这是允许的,修改对象内部的属性
console.log(person.age); // 输出 31
// person = { name: “Bob” }; // 错误!不允许重新赋值整个对象
“`
总结:
- 优先使用
const
。只有当你确定变量的值需要改变时,才使用let
。 - 尽量避免使用
var
。
变量命名规则:
- 变量名可以包含字母、数字、下划线
_
和美元符号$
。 - 变量名不能以数字开头。
- 变量名区分大小写 (
age
和Age
是不同的变量)。 - 不能使用 JavaScript 的保留字 (如
if
,for
,function
,let
,const
等) 作为变量名。 - 推荐使用驼峰命名法 (camelCase),即第一个单词小写,后续单词首字母大写 (如
myVariableName
)。
6. 数据类型:JavaScript 世界里的“物种”
数据类型决定了数据能存储什么类型的信息以及可以对其进行什么操作。JavaScript 是动态类型语言,这意味着你声明变量时不需要指定其类型,变量的类型会在运行时根据赋给它的值自动确定。
JavaScript 的数据类型分为两大类:基本数据类型 (Primitive Types) 和 复杂数据类型 (Complex/Reference Types)。
基本数据类型 (7种)
基本数据类型的值是不可变的(Immutable),当你复制基本类型变量时,复制的是它的值本身。
-
String (字符串): 用于表示文本。使用单引号 (
'
)、双引号 ("
) 或反引号 (`
) 包裹。javascript
let greeting = "Hello, world!";
let anotherGreeting = '你好,世界!';
let multiLineString = `
这是
一个
多行字符串。
`;
// 使用反引号可以方便地嵌入变量 (模板字面量)
let name = "Alice";
let message = `Hello, ${name}!`; // 输出 "Hello, Alice!" -
Number (数字): 用于表示整数和浮点数。
javascript
let integer = 100;
let float = 3.14;
let bigNumber = 1e6; // 科学计数法:1 * 10^6
let hex = 0xff; // 十六进制
let octal = 0o10; // 八进制 (不常用)
let binary = 0b10; // 二进制 (不常用)
特殊的数字值:Infinity
(无穷大),-Infinity
(无穷小),NaN
(Not-a-Number,非数字)。 -
Boolean (布尔值): 只有两个值:
true
(真) 和false
(假),常用于逻辑判断。javascript
let isLogged = true;
let hasPermission = false; -
Null (空值): 表示一个空或者不存在的值。需要显式地赋值为
null
。javascript
let data = null; // 明确表示变量没有值
注意:typeof null
的结果是"object"
。这是 JavaScript 语言的一个历史遗留 bug,但它确实是基本数据类型。 -
Undefined (未定义): 表示变量已经声明但尚未赋值。
javascript
let notAssigned; // 变量声明后未赋值,其值默认为 undefined
console.log(notAssigned); // 输出 undefined
// 也可以显式赋值为 undefined (不推荐)
let explicitlyUndefined = undefined; -
Symbol (符号): ES6 新增。表示独一无二的值,常用作对象属性的键,避免属性名冲突。
javascript
const id = Symbol('uniqueId');
const anotherId = Symbol('uniqueId');
console.log(id === anotherId); // 输出 false
对于初学者,了解有这个类型即可。 -
BigInt (大整数): ES11 新增。用于表示任意大的整数,不受 Number 类型最大安全整数的限制。以
n
结尾。javascript
const bigNumber = 123456789012345678901234567890n;
console.log(typeof bigNumber); // 输出 "bigint"
对于初学者,了解有这个类型即可。
复杂数据类型 (1种主要类型)
复杂数据类型的值是可变的,当你复制复杂类型变量时,复制的是它的引用(内存地址),而不是值本身。
-
Object (对象): 用于表示复杂的数据结构,是键值对 (key-value pair) 的集合。数组、函数等在 JavaScript 底层也是对象。
“`javascript
// 字面量创建对象
let person = {
name: “Bob”, // 键 name, 值 “Bob”
age: 25, // 键 age, 值 25
isStudent: false, // 键 isStudent, 值 false
greet: function() { // 键 greet, 值是一个函数
console.log(“Hello!”);
}
};// 访问对象属性
console.log(person.name); // 使用点号表示法 输出 “Bob”
console.log(person[‘age’]); // 使用方括号表示法 输出 25// 修改对象属性
person.age = 26;
console.log(person.age); // 输出 26// 调用对象的方法 (值为函数的属性)
person.greet(); // 输出 “Hello!”
“`特殊的 Object 类型:
-
Array (数组): 有序的元素集合,索引从 0 开始。
javascript
let colors = ["red", "green", "blue"]; // 元素可以是不同类型
console.log(colors[0]); // 访问第一个元素,输出 "red"
console.log(colors.length); // 数组长度,输出 3
colors.push("yellow"); // 添加元素到末尾
console.log(colors); // 输出 ["red", "green", "blue", "yellow"] -
Function (函数): 可执行的代码块。函数也是对象的一种特殊形式。
javascript
function add(a, b) {
return a + b;
}
let sum = add(5, 3); // 调用函数,sum 的值为 8
console.log(typeof add); // 输出 "function" (尽管底层是 object)
-
使用 typeof
运算符
typeof
运算符可以用来检测变量或值的类型。
javascript
console.log(typeof 123); // 输出 "number"
console.log(typeof "hello"); // 输出 "string"
console.log(typeof true); // 输出 "boolean"
console.log(typeof null); // 输出 "object" (记住这个特例)
console.log(typeof undefined); // 输出 "undefined"
console.log(typeof Symbol('id')); // 输出 "symbol"
console.log(typeof 123n); // 输出 "bigint"
console.log(typeof {}); // 输出 "object"
console.log(typeof []); // 输出 "object" (数组是特殊的对象)
console.log(typeof function(){}); // 输出 "function" (函数是特殊的对象)
7. 运算符:对数据进行操作
运算符用于执行各种操作,如数学计算、比较、逻辑判断等。
算术运算符
用于执行数学计算:
+
:加法 / 字符串连接-
:减法*
:乘法/
:除法%
:取模 (余数)**
:幂运算 (ES6)++
:自增 (变量值加 1)--
:自减 (变量值减 1)
“`javascript
let a = 10;
let b = 3;
console.log(a + b); // 13
console.log(a – b); // 7
console.log(a * b); // 30
console.log(a / b); // 3.333…
console.log(a % b); // 1 (10 除以 3 余 1)
console.log(a ** b); // 1000 (10的3次方)
let counter = 5;
counter++; // counter 变为 6
console.log(counter); // 6
counter–; // counter 变为 5
console.log(counter); // 5
// 字符串连接
let firstName = “John”;
let lastName = “Doe”;
let fullName = firstName + ” ” + lastName; // “John Doe”
let mixed = “Result: ” + (10 + 5); // “Result: 15″ (数字会先计算,再转换为字符串连接)
let mixed2 = 10 + 5 + ” Result”; // “15 Result” (从左到右计算,先数字相加)
let mixed3 = “Result: ” + 10 + 5; // “Result: 105” (从左到右,先字符串连接)
“`
赋值运算符
用于给变量赋值:
=
:赋值+=
:加等于 (x += y
等同于x = x + y
)-=
:减等于*=
:乘等于/=
:除等于%=
:模等于**=
:幂等于
javascript
let x = 10;
x += 5; // x 现在是 15
x *= 2; // x 现在是 30
比较运算符
用于比较两个值,返回布尔值 (true
或 false
):
==
:相等 (只比较值,会进行类型转换)!=
:不相等 (只比较值)===
:严格相等 (比较值 和 类型) – 强烈推荐使用!==
:严格不相等 (比较值 和 类型) – 强烈推荐使用>
:大于<
:小于>=
:大于或等于<=
:小于或等于
“`javascript
console.log(5 == ‘5’); // true (只比较值,’5′ 转换为数字 5)
console.log(5 === ‘5’); // false (比较值和类型,一个是数字,一个是字符串)
console.log(10 != ’10’); // false
console.log(10 !== ’10’);// true
console.log(10 > 5); // true
console.log(7 < 7); // false
console.log(8 >= 8); // true
console.log(9 <= 10); // true
“`
重点: 始终优先使用严格相等 (===
) 和严格不相等 (!==
),除非你明确知道并需要进行类型转换。这可以避免很多潜在的类型转换引发的错误。
逻辑运算符
用于组合或修改布尔表达式:
&&
:逻辑与 (AND)。如果两边的条件都为true
,则结果为true
。||
:逻辑或 (OR)。如果两边的条件至少有一个为true
,则结果为true
。!
:逻辑非 (NOT)。取反。
“`javascript
let isAdult = true;
let isStudent = false;
console.log(isAdult && isStudent); // false (true && false -> false)
console.log(isAdult || isStudent); // true (true || false -> true)
console.log(!isAdult); // false (!true -> false)
“`
短路评估 (Short-circuit Evaluation):
逻辑与 (&&
) 和逻辑或 (||
) 遵循短路原则。
* 对于 &&
,如果左侧表达式为 false
,则右侧表达式不再评估,结果直接是 false
。
* 对于 ||
,如果左侧表达式为 true
,则右侧表达式不再评估,结果直接是 true
。
这在给变量设置默认值或执行依赖于条件的函数时非常有用:
“`javascript
let user = { name: “Alice” };
let username = user && user.name; // 如果 user 存在 (非 null/undefined),则取 user.name
console.log(username); // “Alice”
let settings = null;
let defaultSettings = settings || { theme: ‘light’ }; // 如果 settings 为 “假值” (null, undefined, 0, “”, false, NaN),则使用后面的默认对象
console.log(defaultSettings); // { theme: ‘light’ }
“`
三元运算符 (Conditional Ternary Operator)
一种简洁的条件判断表达式:
条件 ? 表达式1 : 表达式2
如果 条件
为 true
,则返回 表达式1
的值;否则返回 表达式2
的值。
javascript
let age = 18;
let status = (age >= 18) ? "Adult" : "Minor";
console.log(status); // 输出 "Adult"
8. 控制流程:让程序“思考”和“行动”
控制流程语句决定了程序代码执行的顺序。最常见的是条件语句(分支)和循环语句。
条件语句 (Conditional Statements)
用于根据不同的条件执行不同的代码块。
if
, else if
, else
“`javascript
let score = 85;
if (score >= 90) {
console.log(“优秀”);
} else if (score >= 75) { // 如果上一个条件不满足,检查这个
console.log(“良好”);
} else if (score >= 60) { // 如果上上个条件不满足,检查这个
console.log(“及格”);
} else { // 如果以上所有条件都不满足,执行 else 块
console.log(“不及格”);
}
“`
switch
用于基于一个变量或表达式的多个可能值来选择执行不同的代码块。通常用于替代多个连续的 else if
判断同一个变量的情况。
“`javascript
let dayOfWeek = “Tuesday”;
let message;
switch (dayOfWeek) {
case “Monday”:
message = “又是新的一周!”;
break; // break 关键字很重要,它会跳出 switch 语句
case “Tuesday”:
case “Wednesday”:
case “Thursday”:
message = “努力工作日”;
break;
case “Friday”:
message = “快到周末了!”;
break;
case “Saturday”:
case “Sunday”:
message = “美好周末!”;
break;
default: // 如果没有任何一个 case 匹配
message = “无效的日期”;
}
console.log(message); // 输出 “努力工作日”
``
break
**注意:** 如果省略,代码会“穿透”到下一个
case执行,直到遇到
break或
switch结束。这有时是需要的(如上面的周二到周四),但多数情况下你需要
break`。
循环语句 (Loop Statements)
用于重复执行一段代码块。
for
循环
最常用的循环类型,适用于你知道需要重复多少次的情况。
for (初始化表达式; 条件表达式; 更新表达式) { 代码块 }
“`javascript
// 打印数字 0 到 4
for (let i = 0; i < 5; i++) {
console.log(i);
}
// 输出: 0, 1, 2, 3, 4
// 遍历数组
const fruits = [“Apple”, “Banana”, “Cherry”];
for (let i = 0; i < fruits.length; i++) {
console.log(“我喜欢吃 ” + fruits[i]);
}
// 输出:
// 我喜欢吃 Apple
// 我喜欢吃 Banana
// 我喜欢吃 Cherry
“`
while
循环
适用于当你不确定需要循环多少次,只知道循环应该在某个条件满足时停止的情况。条件在每次循环开始前检查。
while (条件表达式) { 代码块 }
javascript
let count = 0;
while (count < 3) {
console.log("Count is: " + count);
count++; // 更新变量,否则可能导致无限循环
}
// 输出:
// Count is: 0
// Count is: 1
// Count is: 2
do...while
循环
与 while
类似,但条件在循环结束后检查。这意味着 do...while
循环至少会执行一次。
do { 代码块 } while (条件表达式);
“`javascript
let i = 0;
do {
console.log(“Value is: ” + i);
i++;
} while (i < 3);
// 输出:
// Value is: 0
// Value is: 1
// Value is: 2
// 即使条件一开始就不满足,也会执行一次
let j = 10;
do {
console.log(“This will print once.”);
} while (j < 5);
// 输出: “This will print once.”
“`
break
和 continue
break;
:立即终止整个循环的执行。continue;
:跳过当前循环的剩余代码,进入下一次循环。
javascript
for (let k = 0; k < 10; k++) {
if (k === 5) {
break; // 当 k 等于 5 时,终止循环
}
if (k % 2 !== 0) {
continue; // 当 k 是奇数时,跳过当前循环的 console.log,进入下一次循环
}
console.log(k);
}
// 输出: 0, 2, 4
// k=5 时 break 退出,k=1, 3 跳过打印
9. 函数:组织和重用代码块
函数是一段预先定义好的、可以被重复调用的代码块。它们允许我们将复杂的程序分解成更小、更易于管理的部分,并提高代码的重用性。
函数的定义
主要有两种方式定义函数:
方式一:函数声明 (Function Declaration)
“`javascript
function greet(name) { // name 是参数
console.log(“Hello, ” + name + “!”);
}
// 函数声明有“提升”(hoisting) 特性,可以在定义之前调用
greet(“Alice”); // 输出 “Hello, Alice!”
“`
方式二:函数表达式 (Function Expression)
将函数赋值给一个变量。
“`javascript
const speak = function(message) { // message 是参数
console.log(message);
};
// 函数表达式没有提升,必须先定义后调用
speak(“Good morning!”); // 输出 “Good morning!”
// greet(“Bob”); // 错误!如果在 speak 之前定义 greet,这是可以的,因为提升
// speak(“Good afternoon!”); // 如果 speak 定义在后面,这里会报错
“`
方式三:箭头函数 (Arrow Function) (ES6)
一种更简洁的函数表达式语法,特别是对于匿名函数(没有名字的函数)。
“`javascript
const multiply = (a, b) => { // a, b 是参数
return a * b;
};
const add = (a, b) => a + b; // 如果函数体只有一条 return 语句,可以省略 {} 和 return
const sayHello = () => console.log(“Hello!”); // 没有参数的函数,括号不能省略
console.log(multiply(4, 5)); // 输出 20
console.log(add(2, 3)); // 输出 5
sayHello(); // 输出 “Hello!”
``
this` 关键字相关的特殊行为,这在入门阶段可以暂时忽略,但需要知道它们和普通函数表达式略有不同。对于简单的回调函数或短小函数,箭头函数非常方便。
箭头函数有一些与
函数的调用
通过函数名后跟一对圆括号 ()
来调用函数,圆括号中可以传递参数。
javascript
greet("Charlie"); // 调用 greet 函数,并传递参数 "Charlie"
let result = add(10, 20); // 调用 add 函数,并将返回值赋给 result
console.log(result); // 输出 30
参数与返回值
- 参数 (Parameters): 函数定义时圆括号中列出的变量名,是函数内部使用的局部变量。
- 实参 (Arguments): 调用函数时传递给参数的实际值。
return
语句: 用于指定函数执行后返回的值。如果函数没有return
语句,或者return
后面没有值,函数默认返回undefined
。
“`javascript
function subtract(a, b) {
return a – b; // 返回 a 减去 b 的结果
console.log(“这行代码不会执行”); // return 语句后的代码不会被执行
}
let diff = subtract(15, 7);
console.log(diff); // 输出 8
function doNothing() {
// 没有 return 语句
}
console.log(doNothing()); // 输出 undefined
“`
函数是构建复杂程序的基础,通过函数可以将代码模块化,提高可读性和可维护性。
10. 数组:有序的数据集合
数组是一种特殊的对象,用于存储一个有序的元素集合。数组的每个元素都有一个从 0 开始的数字索引。
创建数组
“`javascript
// 数组字面量 (最常用)
const numbers = [1, 2, 3, 4, 5];
const mixedArray = [1, “hello”, true, { name: “Bob” }]; // 数组元素可以是不同类型
const emptyArray = [];
// 使用 Array 构造函数 (不常用,特别是 Array(number) 可能导致歧义)
// const arrayFromConstructor = new Array(1, 2, 3);
// const arrayWithLength = new Array(10); // 创建一个长度为 10 的空数组 (元素是 empty slots)
“`
访问数组元素
使用方括号 []
和索引来访问数组中的元素。索引从 0 开始。
javascript
const fruits = ["Apple", "Banana", "Cherry"];
console.log(fruits[0]); // 输出 "Apple" (第一个元素)
console.log(fruits[1]); // 输出 "Banana" (第二个元素)
console.log(fruits[2]); // 输出 "Cherry" (第三个元素)
console.log(fruits[3]); // 输出 undefined (索引超出范围)
数组长度
使用 length
属性获取数组的元素个数。
javascript
const colors = ["red", "green", "blue"];
console.log(colors.length); // 输出 3
修改数组元素
通过索引给数组元素重新赋值。
javascript
const numbers = [1, 2, 3];
numbers[1] = 10; // 将索引为 1 的元素改为 10
console.log(numbers); // 输出 [1, 10, 3]
向数组添加/删除元素
push()
: 在数组末尾添加一个或多个元素。pop()
: 移除数组末尾的元素,并返回该元素。unshift()
: 在数组开头添加一个或多个元素。shift()
: 移除数组开头的元素,并返回该元素。
“`javascript
const animals = [“dog”, “cat”];
animals.push(“elephant”); // 添加 “elephant” 到末尾
console.log(animals); // 输出 [“dog”, “cat”, “elephant”]
const lastAnimal = animals.pop(); // 移除并获取末尾元素
console.log(lastAnimal); // 输出 “elephant”
console.log(animals); // 输出 [“dog”, “cat”]
animals.unshift(“bird”); // 添加 “bird” 到开头
console.log(animals); // 输出 [“bird”, “dog”, “cat”]
const firstAnimal = animals.shift(); // 移除并获取开头元素
console.log(firstAnimal); // 输出 “bird”
console.log(animals); // 输出 [“dog”, “cat”]
“`
遍历数组
除了使用 for
循环,JavaScript 提供了多种遍历数组的方法:
-
for...of
循环 (ES6): 直接遍历数组的元素。javascript
const items = [10, 20, 30];
for (const item of items) {
console.log(item); // 输出 10, 20, 30
} -
forEach()
方法: 对数组的每个元素执行一次回调函数。javascript
const numbers = [1, 2, 3];
numbers.forEach(function(number, index, array) {
console.log(`Element at index ${index} is ${number}`);
});
// 使用箭头函数更简洁
numbers.forEach(number => console.log(number * 2)); // 输出 2, 4, 6
数组还有许多其他有用的方法,如 map
, filter
, reduce
, find
, findIndex
, splice
, slice
等,这些都非常常用且强大,可以在后续学习中深入了解。
11. 对象:无序的键值对集合
对象是 JavaScript 中最重要的数据结构之一,用于存储无序的键值对集合。每个键值对都表示对象的一个属性,键 (Key) 通常是字符串(或 Symbol),值 (Value) 可以是任何数据类型,包括其他对象或函数。
创建对象
最常用的方式是使用对象字面量 {}
。
javascript
const person = {
name: "Alice", // 属性 name, 值为字符串 "Alice"
age: 30, // 属性 age, 值为数字 30
isStudent: false, // 属性 isStudent, 值为布尔值 false
hobbies: ["reading", "hiking"], // 属性 hobbies, 值为一个数组
address: { // 属性 address, 值为另一个对象
city: "New York",
zip: "10001"
},
greet: function() { // 属性 greet, 值为一个函数 (称为方法)
console.log("Hello!");
},
// 也可以使用箭头函数作为方法 (注意 this 的区别,入门先忽略)
sayName: () => {
console.log("My name is " + person.name); // 这里为了简单直接访问 person.name
}
};
访问对象属性
使用点号表示法 (.
) 或方括号表示法 ([]
) 访问属性值。
“`javascript
console.log(person.name); // 输出 “Alice”
console.log(person[‘age’]); // 输出 30 (方括号中的键是字符串)
// 访问嵌套属性
console.log(person.address.city); // 输出 “New York”
console.log(person[‘address’][‘zip’]); // 输出 “10001”
// 访问数组属性中的元素
console.log(person.hobbies[0]); // 输出 “reading”
// 调用对象的方法
person.greet(); // 输出 “Hello!”
person.sayName(); // 输出 “My name is Alice”
“`
注意: 方括号表示法适用于属性名是变量或包含特殊字符(如空格、连字符)的情况。
“`javascript
let keyName = ‘age’;
console.log(person[keyName]); // 输出 30
const anotherPerson = {
“full-name”: “Bob Smith”
};
// console.log(anotherPerson.full-name); // 错误!
console.log(anotherPerson[‘full-name’]); // 输出 “Bob Smith”
“`
修改/添加/删除对象属性
“`javascript
const car = {
make: “Toyota”,
model: “Camry”
};
// 修改属性
car.model = “Corolla”;
console.log(car.model); // 输出 “Corolla”
// 添加属性
car.year = 2020;
console.log(car.year); // 输出 2020
// 删除属性
delete car.year;
console.log(car.year); // 输出 undefined
console.log(car); // 输出 { make: “Toyota”, model: “Corolla” }
“`
遍历对象属性
常用的遍历对象属性的方法:
-
for...in
循环: 遍历对象的可枚举属性键 (key)。javascript
const user = { name: "Charlie", age: 28 };
for (const key in user) {
console.log(key + ": " + user[key]);
}
// 输出:
// name: Charlie
// age: 28
注意:for...in
循环会遍历到对象原型链上的属性(除非用hasOwnProperty
检查),且遍历顺序不一定保证。对于初学者,简单对象可以使用,但更推荐使用下面的方法。 -
Object.keys()
: 返回一个包含对象自身可枚举属性键(字符串)的数组。 Object.values()
: 返回一个包含对象自身可枚举属性值 的数组。Object.entries()
: 返回一个包含对象自身可枚举属性 [键, 值] 对数组。
“`javascript
const product = { name: “Laptop”, price: 1200, brand: “Dell” };
console.log(Object.keys(product)); // 输出 [“name”, “price”, “brand”]
console.log(Object.values(product)); // 输出 [“Laptop”, 1200, “Dell”]
console.log(Object.entries(product));// 输出 [[“name”, “Laptop”], [“price”, 1200], [“brand”, “Dell”]]
// 结合 for…of 遍历
for (const key of Object.keys(product)) {
console.log(${key}: ${product[key]}
);
}
// 输出同 for…in
“`
对象是构建复杂数据结构和实现面向对象编程的基础。
12. 作用域:变量的“可见范围”
作用域定义了变量在代码中的可访问性(即在哪里可以使用这个变量)。理解作用域对于避免变量冲突和编写可预测的代码至关重要。
JavaScript 中主要有三种作用域:
-
全局作用域 (Global Scope): 在函数、块外部声明的变量属于全局作用域。它们可以在代码的任何地方被访问。
“`javascript
const globalVar = “我是全局变量”;function myFunction() {
console.log(globalVar); // 可以在函数内部访问全局变量
}myFunction();
console.log(globalVar); // 也可以在函数外部访问全局变量
``
var
在浏览器中,在最顶层使用声明的变量会成为全局对象的属性 (
window对象)。使用
let和
const` 则不会,但它们仍然是全局作用域的。 -
函数作用域 (Function Scope): 使用
var
在函数内部声明的变量具有函数作用域。它们只能在该函数内部及其嵌套的函数内部访问。“`javascript
function anotherFunction() {
var functionVar = “我是函数作用域变量”;
console.log(functionVar); // 在函数内部可以访问
}anotherFunction();
// console.log(functionVar); // 错误!ReferenceError: functionVar is not defined (在函数外部不可访问)
“` -
块级作用域 (Block Scope): 使用
let
或const
在一对花括号{}
内部声明的变量具有块级作用域。这些花括号可以是函数体、if
语句、for
循环、while
循环等。它们只能在该块内部访问。“`javascript
if (true) {
let blockLet = “我是块级作用域变量 (let)”;
const blockConst = “我是块级作用域常量 (const)”;
var functionVarInBlock = “我是 var 在块内”; // var 没有块级作用域
console.log(blockLet);
console.log(blockConst);
console.log(functionVarInBlock); // 可以在块内访问 var
}// console.log(blockLet); // 错误!ReferenceError
// console.log(blockConst); // 错误!ReferenceError
console.log(functionVarInBlock); // 可以访问! var 在块内声明,但作用域是函数或全局
``
let
这就是为什么推荐使用和
const` 的重要原因之一——它们提供了更细粒度的作用域控制,有助于防止变量名冲突和意外的变量访问。
作用域链 (Scope Chain):
当 JavaScript 查找一个变量时,它会首先在当前作用域查找。如果找不到,就会向上层作用域查找,直到全局作用域。这个查找过程形成了一个作用域链。
理解作用域是理解 JavaScript 中变量生命周期和闭包(更高级概念)的基础。
13. JavaScript 如何与网页(DOM)交互
JavaScript 在浏览器中的一个主要用途是操作网页的内容、结构和样式。这通过 文档对象模型 (DOM) 实现。DOM 是浏览器将 HTML 文档解析后创建的一个树形结构,每个 HTML 元素、文本、属性等都是 DOM 树中的一个节点。
JavaScript 可以通过 DOM API(浏览器提供的一系列方法和属性)来访问、修改、添加或删除 DOM 节点。
获取 DOM 元素
最常用的获取单个元素的方法:
document.getElementById(id)
: 根据元素的id
属性获取元素节点。
javascript
// 假设 HTML 中有一个 <p id="myParagraph">...</p>
const paragraph = document.getElementById('myParagraph');
console.log(paragraph); // 打印获取到的 <p> 元素对象
获取多个元素的方法:
document.getElementsByClassName(className)
: 根据类名获取元素集合 (HTMLCollection)。document.getElementsByTagName(tagName)
: 根据标签名获取元素集合 (HTMLCollection)。document.querySelector(selector)
: 根据 CSS 选择器获取 第一个 匹配的元素。document.querySelectorAll(selector)
: 根据 CSS 选择器获取 所有 匹配的元素集合 (NodeList)。
“`javascript
// 假设 HTML 中有多个
const elementsByClass = document.getElementsByClassName(‘myClass’);
console.log(elementsByClass); // 打印类名为 ‘myClass’ 的元素集合
// 假设 HTML 中有多个
const listItems = document.getElementsByTagName(‘li’);
console.log(listItems); // 打印所有
// 假设 HTML 中有一个
const firstItem = document.querySelector(‘#container .item’); // 获取第一个匹配的 span
console.log(firstItem);
const allItems = document.querySelectorAll(‘#container .item’); // 获取所有匹配的 span
console.log(allItems); // 打印 NodeList 集合
“`
querySelector
和 querySelectorAll
使用 CSS 选择器,非常灵活和强大,推荐优先使用。
修改 DOM 元素
获取到 DOM 元素后,可以修改其属性、内容和样式。
-
修改内容:
element.innerHTML
: 获取或设置元素的 HTML 内容(包含标签)。element.textContent
: 获取或设置元素的纯文本内容(忽略标签)。
html
<div id="contentDiv">
<p>原始内容</p>
</div>
javascript
const contentDiv = document.getElementById('contentDiv');
contentDiv.innerHTML = "<p>修改后的 <strong>HTML</strong> 内容</p>";
// contentDiv.textContent = "修改后的纯文本内容"; // 会覆盖所有 HTML -
修改属性:
element.attributeName
: 直接访问或修改标准 HTML 属性(如id
,src
,href
,className
,style
)。element.setAttribute(name, value)
: 设置任意属性。element.getAttribute(name)
: 获取属性值。element.removeAttribute(name)
: 删除属性。
html
<img id="myImage" src="old.jpg" alt="旧图片">
<a id="myLink" href="old.html">链接</a>
“`javascript
const myImage = document.getElementById(‘myImage’);
myImage.src = “new.jpg”;
myImage.alt = “新图片”;const myLink = document.getElementById(‘myLink’);
myLink.setAttribute(‘href’, ‘new.html’);
myLink.setAttribute(‘target’, ‘_blank’); // 添加 target 属性
console.log(myLink.getAttribute(‘href’)); // 输出 “new.html”
myLink.removeAttribute(‘target’); // 删除 target 属性
``
className
**注意vs
class和
style:** HTML 属性
class在 JS 中对应
className(为了避免与 JS 保留字
class冲突)。HTML 属性
style对应
element.style` 对象,可以修改其属性。html
<p id="styledText">这段文字有样式</p>
javascript
const styledText = document.getElementById('styledText');
styledText.style.color = "red"; // 设置文字颜色
styledText.style.fontSize = "20px"; // 设置字体大小 (使用驼峰命名法)
styledText.style.backgroundColor = "yellow"; // 背景色 -
修改样式类: 使用
element.classList
API 管理 CSS 类。html
<div id="myDiv" class="box"></div>
css
.highlight {
border: 2px solid yellow;
}
.hidden {
display: none;
}
javascript
const myDiv = document.getElementById('myDiv');
myDiv.classList.add('highlight'); // 添加 highlight 类
myDiv.classList.remove('box'); // 移除 box 类
myDiv.classList.toggle('hidden'); // 如果有 hidden 类则移除,没有则添加
console.log(myDiv.classList.contains('highlight')); // 检查是否包含 highlight 类
使用classList
是控制元素样式的常用且推荐的方式,因为它将样式规则定义在 CSS 中,JS 只负责添加/移除类名。
创建和添加 DOM 元素
JavaScript 可以动态创建新的 HTML 元素并添加到页面上。
“`javascript
// 1. 创建新元素
const newElement = document.createElement(‘div’);
// 2. 设置元素内容或属性
newElement.textContent = “这是一个新创建的 div。”;
newElement.style.color = “blue”;
newElement.classList.add(‘new-item’);
// 3. 找到父元素
const parentElement = document.getElementById(‘container’); // 假设有一个
// 4. 将新元素添加到父元素中
// parentElement.appendChild(newElement); // 添加到子元素的末尾
// 或者插入到特定位置
const existingElement = document.getElementById(‘existing’); // 假设有一个
…
parentElement.insertBefore(newElement, existingElement); // 将 newElement 插入到 existingElement 前面
“`
删除 DOM 元素
“`javascript
// 假设要删除上面创建的 newElement
const elementToRemove = document.getElementById(‘newCreatedDiv’); // 获取要删除的元素
if (elementToRemove) { // 检查元素是否存在
elementToRemove.parentNode.removeChild(elementToRemove); // 获取父节点,然后通过父节点移除子节点
}
// 更简单的方式 (如果知道父节点)
const parent = document.getElementById(‘container’);
const child = document.getElementById(‘childToRemove’);
if (parent && child) {
parent.removeChild(child);
}
“`
DOM 操作是前端 JavaScript 最核心的应用之一。熟练掌握如何获取、修改和操作 DOM 元素是构建交互式网页的关键。
14. 事件:响应用户或系统的动作
JavaScript 的核心能力之一是响应发生在网页上的各种事件,如用户点击按钮、鼠标移动、键盘输入、页面加载完成等。
事件监听器 (Event Listener)
我们通过给 DOM 元素添加事件监听器来指定当某个事件发生时应该执行的代码(通常是一个函数,称为事件处理程序或回调函数)。
最常用的添加事件监听器的方法是 addEventListener()
:
element.addEventListener(event, handler)
event
: 一个字符串,表示要监听的事件类型(如 ‘click’, ‘mouseover’, ‘keydown’, ‘load’ 等)。handler
: 一个函数,当事件发生时将被执行。
“`html
等待点击…
javascript
const myButton = document.getElementById(‘myButton’);
const outputParagraph = document.getElementById(‘output’);
// 添加一个点击事件监听器
myButton.addEventListener(‘click’, function() {
outputParagraph.textContent = “按钮被点击了!”;
});
// 使用箭头函数更简洁
myButton.addEventListener(‘mouseover’, () => {
console.log(‘鼠标移到按钮上了’);
});
“`
移除事件监听器:
可以使用 removeEventListener()
移除之前添加的监听器。需要注意的是,传递给 removeEventListener
的事件类型和处理函数必须与添加时完全相同。
“`javascript
function handleClick() {
console.log(“Button clicked!”);
}
myButton.addEventListener(‘click’, handleClick);
// … 某个时刻 …
myButton.removeEventListener(‘click’, handleClick); // 移除监听器
``
addEventListener(‘click’, function() { … })
这意味着如果你使用了匿名函数(如或
addEventListener(‘click’, () => { … })`),通常无法直接移除它,除非将该匿名函数赋值给一个变量。
常用事件类型:
- 鼠标事件:
click
,dblclick
,mousedown
,mouseup
,mousemove
,mouseover
,mouseout
,contextmenu
(右键) - 键盘事件:
keydown
(按键按下),keyup
(按键抬起),keypress
(字符输入) - 表单事件:
submit
,input
,change
,focus
,blur
- 文档/窗口事件:
DOMContentLoaded
(DOM 加载完成),load
(页面及所有资源加载完成),resize
,scroll
事件处理程序函数会自动接收一个事件对象 (Event Object) 作为参数,该对象包含了事件的详细信息,如鼠标位置、按下的键、目标元素等。
javascript
myButton.addEventListener('click', function(event) {
console.log("Event Type:", event.type); // "click"
console.log("Target Element:", event.target); // <button id="myButton">
console.log("Mouse X:", event.clientX); // 鼠标点击位置的 X 坐标
});
掌握事件处理是构建交互式网页的另一块重要基石。
15. 一些好的编程习惯和注意事项
- 代码可读性: 使用有意义的变量名、函数名,适当添加注释。
- 代码格式: 保持一致的缩进、空格和换行风格。可以使用 Prettier, ESLint 等工具自动化格式化和检查代码。
- 使用
let
和const
替代var
: 利用块级作用域避免潜在问题。 - 使用严格相等
===
和!==
: 避免类型转换带来的意外行为。 - 分号: JavaScript 大多数情况下可以省略分号(有自动插入分号机制),但为了避免歧义和保持一致性,许多开发者仍然选择始终使用分号。选择一种风格并坚持下去。
- 严格模式 (
'use strict';
): 在脚本或函数顶部加上'use strict';
可以启用严格模式。严格模式会禁用一些不安全或不推荐的语法,并抛出更多错误,有助于编写更健壮的代码。推荐在所有新代码中使用严格模式。 - 学习如何调试: 掌握使用浏览器开发者工具的控制台 (Console) 和断点 (Sources 标签) 进行调试。
console.log()
是最简单的调试工具。
“`javascript
‘use strict’; // 启用严格模式
function calculateArea(radius) {
// 更好的变量名
const PI = 3.14159;
let area = PI * radius * radius; // 使用 let 和 const
// if (area = 100) { … } // 在严格模式下,这里可能会报错或警告,因为它看起来像赋值而不是比较
return area;
}
console.log(calculateArea(5));
“`
16. 总结与下一步
恭喜你!你已经学习了 JavaScript 最核心的基础知识,包括变量、数据类型、运算符、控制流程、函数、数组、对象、作用域以及如何与网页进行基本交互(DOM 和事件)。这些是所有 JavaScript 应用的基石。
但这仅仅是一个开始!JavaScript 世界非常广阔,接下来你可以继续学习:
- 深入 DOM 操作: 学习创建、插入、删除元素的更多方法,处理表单,操作 CSS 样式等。
- 事件高级主题: 事件冒泡、事件捕获、事件委托。
- 异步 JavaScript: 定时器 (
setTimeout
,setInterval
), Promises, Async/Await (处理耗时操作,如网络请求)。 - 面向对象编程 (OOP): 构造函数、原型、类 (ES6)。
- 更高级的数组和对象方法:
map
,filter
,reduce
,bind
,call
,apply
等。 - 模块化: 如何组织大型项目的代码 (ES Modules)。
- 错误处理:
try...catch
。 - 现代 JavaScript 特性 (ES6+): 解构赋值、展开语法、迭代器、生成器等。
- Node.js: 学习如何在服务器端使用 JavaScript。
- 前端框架/库: React, Vue, Angular 等,它们能帮助你更高效地构建复杂用户界面。
最重要的是: 实践!多写代码,尝试解决小问题,做一些小的网页交互练习。遇到问题时,学会使用搜索引擎(如 Google, Bing)搜索,查阅官方文档(MDN Web Docs 是极好的资源),并在社区(如 Stack Overflow)寻求帮助。
编程是一个持续学习和实践的过程。坚持下去,你会发现 JavaScript 能为你打开无数精彩的可能性!
希望这篇详细的教程对你有所帮助。祝你在学习 JavaScript 的旅程中一切顺利!