JavaScript 入门第一天:从变量到对象与调试
从一组 JavaScript 入门练习出发,梳理变量、数据类型、字符串、对象和断点调试的基本思路。
刚开始学习 JavaScript 时,最重要的不是急着记住大量 API,而是建立一套能解释代码的基本模型:数据放在哪里、语句按什么顺序执行、出错时该看什么。
这一组练习从浏览器中的一段 <script> 开始,依次走过变量、字符串、数据类型、对象和调试。下面把这些分散的知识点整理成一篇可复习的笔记。
JavaScript 在浏览器里做什么
浏览器环境中的 JavaScript 通常可以分成三部分:
- ECMAScript(ES):语言本身的语法和规则,例如变量、运算符、控制结构、函数和对象。
- DOM:用 JavaScript 读取和修改 HTML 文档的接口。
- BOM:用 JavaScript 使用浏览器能力的接口,例如地址、历史记录和窗口。
入门阶段先把 ES 打牢最划算。HTML 和 CSS 负责页面结构与样式,JavaScript 负责把数据、逻辑和交互组织起来。
在 HTML 中可以直接写脚本,也可以通过 src 引入外部文件:
<script src="./js/index.js"></script>
带有 src 的 <script> 应只负责加载外部文件,不要再在标签内部混写另一段 JavaScript。调试时,console.log() 的输出在浏览器开发者工具的 Console 面板中查看;alert() 虽然能快速弹窗,但不适合作为日常输出方式。
变量不是数学等号
程序离不开数据的读取和修改。可以把变量理解成一个有名字的容器:先声明,再把值放进去。
let x
x = 50
let y = (x + 10) * 1.4
let z = y * 2 / 3 - 5
console.log(z)
这里的 = 是赋值,不是数学里的“左右相等”。x = 50 的意思是把 50 放进 x;而 y = y + 1 则是先读取旧的 y,计算后再把结果写回 y。
这也解释了一个常见错误:
10 = x // 错误:赋值号左侧不能是常量
变量要先声明再使用。在严格模式下,直接使用从未声明的名字会得到 ReferenceError。多个变量可以分别写,也可以在同一条声明中写清楚:
let a = 10,
b = 20,
c = 30
let、const 与命名
现代 JavaScript 中,局部变量通常使用 let 或 const:
let score = 10
score = 20
const siteName = 'Myth'
// siteName = 'Other' // 错误:const 声明的绑定不能重新赋值
当一个变量后续需要重新赋值时用 let;不需要重新赋值时优先用 const。var 是较早的声明方式,规则与作用域行为和 let 不同,入门代码里应避免把它与 let 混用。
变量名、函数名和对象属性名都属于标识符。基础规则包括:不能以数字开头;除字母、数字外只使用 $ 和 _;不能使用关键字;并且大小写敏感。多单词命名采用驼峰形式更易读:
let marginLeft = 100
let getElementById = '获取 id'
书写习惯也很重要:运算符两侧留空格、语句保持清晰、对象或多行声明保持一致缩进。代码首先是给人读的,其次才是给机器执行的。
数据类型:先看值,再看 typeof
变量的类型由它当前保存的值决定。学习这组练习时,可以先熟悉数字、字符串、布尔值、undefined、null 和对象;现代 JavaScript 还提供 symbol 和 bigint。
let count = 10
let title = '你好'
let ready = true
let pending
let emptyNode = null
let profile = { name: '幂幂' }
console.log(typeof count) // 'number'
console.log(typeof title) // 'string'
console.log(typeof ready) // 'boolean'
console.log(typeof pending) // 'undefined'
console.log(typeof profile) // 'object'
NaN 的名字是 “Not a Number”,但它的 typeof 结果仍是 number;Infinity 也属于 number。这两个结果在调试计算表达式时很常见。
console.log(typeof NaN) // 'number'
console.log(typeof Infinity) // 'number'
typeof 不是万能的:数组和 null 的结果都会是 'object',这是 JavaScript 的历史行为。因此,看到 'object' 时还需要结合上下文判断具体是什么值。
字符串、引号与模板字符串
文字必须放在引号中,单引号、双引号和反引号都可以表示字符串。外层引号要避开内容中的同类引号,或者改用反引号。
字符串可以用 + 拼接:
const letter = '好'
const greeting = '你' + letter + '吗?'
反引号创建的模板字符串在插入变量时更直观:
const letter = '好'
const greeting = `你${letter}吗?`
const className = 'active'
const message = '你好'
const html = `<div class="${className}">${message}</div>`
空字符串 '' 仍然是字符串。像 100 + '' 这样的表达式会得到字符串 '100';这能说明类型转换的存在,但实际业务代码不应依赖这种隐式转换来表达意图。
还要区分变量名和普通文本:
const str = '你好'
alert('str') // 弹出字符 s、t、r
alert(str) // 弹出变量保存的“你好”
对象:把相关数据放在一起
纯对象用 {} 创建,内部由多个“属性名: 属性值”组成。对象适合描述一组相关信息:
const person = {
cname: '杨幂',
ename: 'Yang Mi',
age: 35,
height: '166.5 cm',
}
点号可以读取、修改和新增属性,delete 可以删除属性:
console.log(person.age)
person.age = 36
person.country = '中国'
delete person.height
属性值也可以是另一个对象,访问时就继续使用点号:
const profile = {
name: '幂幂',
family: {
brother: {
name: '开挂',
},
},
}
console.log(profile.family.brother.name)
读取不存在的对象属性通常得到 undefined,但不能继续访问 undefined 或 null 的属性:
const user = { name: '幂幂' }
console.log(user.age) // undefined
// console.log(user.age.value) // TypeError
这类错误的关键不是死记错误信息,而是回到变量的当前值:中间那一层是否真的存在?
程序按步骤执行,调试也按步骤进行
浏览器解析 HTML 时遇到 <script>,会先检查 JavaScript 是否有语法错误;语法正确后再按顺序执行。运行期间发生引用错误或类型错误,出错语句之后的代码通常不会继续执行。
面对一段看不懂的代码,不要只盯着最终输出。可以逐行记录变量的值,或者在关键位置加入 debugger:
let num = 10
debugger
num = num + 1 // 11
debugger
num = num + 1 // 12
console.log(num)
在浏览器开发者工具打开的情况下,debugger 会暂停执行。此时可以观察当前作用域、变量值和调用栈,再单步执行下一行。它比猜测“为什么结果不对”可靠得多。
用练习巩固三个问题
第一天的练习可以反复围绕这三个问题做:
- 这行代码执行前后,变量的值分别是什么? 例如
n + 1只计算但不写回n,而n = n + 1才会改变n。 - 这是值本身,还是字符串形式的文字?
3 * 2 + 5与'3 * 2 + 5'的结果完全不同。 - 对象的哪一层可能不存在? 在读取嵌套属性之前,先确认上一层不是
undefined或null。
变量交换也是很好的基础练习。用一个临时变量保存其中一个值,再完成两次赋值:
let x = 10
let y = 20
const temporary = x
x = y
y = temporary
console.log(x) // 20
console.log(y) // 10
从“能看懂每一行的值变化”开始,再学习条件、循环、函数和 DOM,JavaScript 的后续内容会顺得多。