JavaScript学习笔记
文章目录
- JS基础
- 检测当前运行环境
- 浏览器端 JavaScript组成:(3个部分)
- Node Demo:读文件并返回大小
- JS语法
- 变量声明 `let / const / var`
- 数据类型
- 栈内存和堆内存
- Undefined:未定义类型
- Null:空对象
- 运算符
- 流程控制
- 函数
- 作用域与闭包
- 对象
- 数组
- 模块化
- 异步基础
- 错误处理
JS基础
检测当前运行环境
console.log(typeof window === 'undefined' ? 'Node' : 'Browser');
进阶做法:用 globalThis
console.log(globalThis.window ? 'Browser' : 'Node');
globalThis
是所有环境都存在的统一顶级对象;在 Node 里 globalThis.window
为 undefined
,在浏览器里指向 window
本身。
浏览器端 JavaScript组成:(3个部分)
组成 | 作用 | 核心接口 | Demo |
---|---|---|---|
ECMAScript | 语言本体 (语法 + 内建对象) | Array , Promise | Math.random() |
DOM (Document Object Model) | 操作页面元素 | document.querySelector , Element.classList | js document.body.style.background='lavender'; |
BOM (Browser Object Model) | 操作浏览器窗口/历史/地址 | location , history , navigator | js alert(location.href); |
Node Demo:读文件并返回大小
// size.js
//运行命令 :node size.js Test111.txt
import { statSync } from 'fs';
import { resolve } from 'path';const file = resolve(process.argv[2]);
const { size } = statSync(file);
console.log(`File size: ${size} bytes`);
输出:
File size: 57 bytes
文件不存在
node size.js nofile.txt
会抛出错误并终止执行:
Error: ENOENT: no such file or directory, stat '/F:zyx/nofile.txt'
未提供参数 (process.argv[2]
是 undefined
就会有这个问题)
TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received undefined
JS语法
变量声明 let / const / var
要点:
-
let
和const
拥有块级作用域,存在「暂时性死区」(TDZ),禁止在声明前访问。 -
const
声明后不可重新赋值,但若值是对象,其属性仍可变。 -
var
具函数作用域并会变量提升,容易造成混淆,通常仅维护旧代码时使用。
{console.log(typeof value); // ⚠️ ReferenceError(TDZ),这就是在声明之前访问了,声明在下一句!let value = 42;const PI = 3.1416;// PI = 3; // ⚠️ TypeError: Assignment to constant variable
}
在进入花括号后,value
处于 TDZ,尝试访问即抛错。
PI
一旦赋值后不可再次赋值。
输出:(TDZ声明前访问)
ReferenceError: Cannot access 'value' before initialization
数据类型
要点:
- 基本数据类型:
number
(含NaN
、Infinity
)、string
、boolean
、undefined
、null
、symbol
、bigint
- 引用数据类型:
Object
(Array
、Function
、自定义对象……) - 注意:内置对象 Function、Array、Date、RegExp、Error 等都是属于 Object 类型。也就是说,除了那七种基本数据类型之外,其他的,都称之为 Object 类型。
console.log(typeof null); // 为何是 "object"?
console.log(typeof 10n); // "bigint"
console.log(typeof Symbol()); // "symbol"
早期实现遗留导致 typeof null
返回 "object"
Symbol()
生成独一无二的标识符,用于对象私有键。
基本数据类型举例:
var a = 23;
var b = a;a++;console.log(a); // 打印结果:24
console.log(b); // 打印结果:23
a 和 b 都是基本数据类型,让 b 等于 a,然后改变 a 的值之后,发现 b 的值并没有被改变。
引用数据类型举例:
var obj1 = new Object();
obj1.name = 'smyh';// 让 obj2 等于 obj1
var obj2 = obj1;// 修改 obj1 的 name 属性
obj1.name = 'vae';console.log(obj1.name); // 打印结果:vae
console.log(obj2.name); // 打印结果:vae
obj1 和 obj2 都是引用数据类型,让 obj2 等于 obj1,然后修改 obj1.name 的值之后,发现 obj2.name 的值也发生了改变。
栈内存和堆内存
JS 中,所有的变量都是保存在栈内存中的。
基本数据类型:
基本数据类型的值,**直接保存在栈内存中。**值与值之间是独立存在,修改一个变量不会影响其他的变量。
引用数据类型:
对象是保存到堆内存中的。每创建一个新的对象,就会在堆内存中开辟出一个新的空间;而变量保存了对象的内存地址(对象的引用),保存在栈内存当中。如果两个变量保存了同一个对象的引用,当一个通过一个变量修改属性时,另一个也会受到影响。
这一点和py是很像的。
Undefined:未定义类型
Undefined 类型的值只有一个,就是 undefind。比如 var a = undefined
。
使用 typeof 检查一个 undefined 值时,会返回 undefined。
undefined 的出现有以下几种情况:
- 变量已声明,未赋值(未初始化),一个变量如果只声明了,但没有赋值,此时它的值就是
undefined
。 - 变量未声明(未定义),如果你从未声明一个变量,就去使用它,则会报错(这个大家都知道);此时,如果用
typeof
检查这个变量时,会返回undefined
。 - 函数无返回值时(在定义一个函数时,如果末尾没有 return 语句,那么,其实就是
return undefined
。) - 调用函数时,未传参
调用函数时,如果没有传实参,那么,对应形参的值就是 undefined。
function foo(name) {console.log(name);
}foo(); // 调用函数时,未传参。执行函数后的打印结果:undefined
实际开发中,如果调用函数时没有传参,我们可以根据需要给形参设置一个默认值:
function foo(name) {name = name || 'qianguyihao';
}foo();
等学习了 ES6 之后,上方代码也可以这样写:
function foo(name = 'qianguyihao') {}foo();
Null:空对象
如果你想定义一个变量用来保存引用类型(也就是对象),但是还不确定放什么内容,这个时候,可以在初始化时将其赋值为 null。
从语义上讲,null表示一个空对象,所以使用 typeof 检查一个 null 值时,会返回 object。举例:
var myObj = null;
cosole.log(typeof myObj); // 打印结果:object
undefined 实际上是由 null 衍生出来的,所以null == undefined
的结果为 true。null === undefined
的结果是 false。它们虽然相似,但还是有区别的,其中一个区别是,和数字运算时:
- 10 + null 结果为 10。
- 10 + undefined 结果为 NaN。
运算符
- 算数:
+ - * / % **
- 比较:
=== !== > < >= <=
(务必用全等===
) - 逻辑:
&& || !
,含短路行为 - 三元:
condition ? A : B
- 模板字符串:用反引号 ```, 可内插
${…}
。
Demo
const score = 75;
const ok = score > 60 ? 'pass' : 'fail';
const msg = `Result: ${ok}, score = ${score}`;
console.log(msg);
score > 60
为真,三元返回'pass'
。- 模板字符串直接插入变量,无需拼接。
输出
Result: pass, score = 75
流程控制
条件:if…else
, switch
循环:for
(索引)、for…in
(枚举键)、for…of
(遍历可迭代对象)
break
/ continue
控制跳出或跳过。
Demo
const arr = ['a', 'b', 'c'];
for (const val of arr) {if (val === 'b') continue;console.log(val); // 打印 'a' 和 'c'
}
for…of
直接拿值;当遇到'b'
时continue
跳过本轮。
输出
a
c
函数
- 声明式:有函数提升
- 表达式 / 箭头函数:在赋值后才能调用
- 默认参数与剩余参数
...args
- 箭头函数不绑定自己的
this
/arguments
。
Demo
const sum = (...ns) => ns.reduce((a, b) => a + b, 0);
console.log(sum(1, 2, 3, 4)); // 10
解释
...ns
收集任意个参数生成数组。reduce
累加得到总和。
输出
10
作用域与闭包
- JS 使用词法作用域(静态作用域)
- 闭包 = 函数 + 其引用的外部变量,即便外部函数已返回。
Demo
function makeCounter() {let count = 0;return () => ++count;
}const inc = makeCounter();
console.log(inc()); // 1
console.log(inc()); // 2
inc
始终持有对count
的引用形成闭包,累加状态得以保留。
输出
1
2
对象
- 字面量简写:
{x}
等价于{x: x}
- 计算属性:
['prop' + i]: value
- 方法简写:
say() {}
- 可选链:
obj?.child?.name
防止空值报错。
Demo
const x = 5;
const o = {x, // 简写['y' + 1]: 6, // 计算属性名 y1say() { return this.x }
};
console.log(o.y1, o.say()); // 6 5
输出
6 5
数组
- 高阶函数:
map
,filter
,reduce
- 解构、扩展
...
Array.isArray(obj)
判断。
Demo
const raw = [1, 2, 3, 4];
const squares = raw.map(n => n ** 2); // [1,4,9,16]
const evens = raw.filter(n => n % 2 === 0); // [2,4]
const total = raw.reduce((a, b) => a + b, 0); // 10
const [head, ...rest] = raw; // 解构
console.log(squares, evens, total, head, rest);
输出
[ 1, 4, 9, 16 ] [ 2, 4 ] 10 1 [ 2, 3, 4 ]
模块化
- ES Module(浏览器 / Node ≥ v14 原生支持)
export const foo = …;
/export default …;
import foo, { bar } from './lib.js';
- CommonJS(旧版 Node)
module.exports = …;
/const foo = require('./lib');
Demo (ESM)
// math.js
export const add = (a, b) => a + b;// main.mjs
import { add } from './math.js';
console.log(add(2, 3)); // 5
输出
5
异步基础
- 回调 →
Promise
→async/await
是演进路线。 - 静态方法:
Promise.all
,Promise.race
。 await
只能在async
函数或顶级module
中使用。
Demo
function delay(ms) {return new Promise(res => setTimeout(res, ms));
}(async () => {console.time('t');await delay(500);console.timeEnd('t'); // ≈ 500ms
})();
delay
返回在指定毫秒后解决的 Promise。await
暂停执行,确保时序易读。
输出
t: 500.XXXms
错误处理
try / catch / finally
捕获同步 &await
抛出的异常。- 自定义错误类继承
Error
。
Demo
class BadInputError extends Error {constructor(msg) { super(msg); this.name = 'BadInputError'; }
}function parseJson(str) {if (typeof str !== 'string') throw new BadInputError('Need string');return JSON.parse(str);
}try {parseJson(42);
} catch (e) {console.error(e.name, e.message); // BadInputError Need string
} finally {console.log('done');
}
输出
BadInputError Need string
done