JavaScript 数据类型详解
概述
JavaScript 是一种动态类型语言,变量不需要提前声明类型。JavaScript 有 8 种数据类型,分为两大类:
原始类型 (Primitive Types): 7种
引用类型 (Reference Types): 1种
原始类型 (Primitive Types)
重点:原始类型的值是不可变的,存储在栈内存中,按值传递。
1. Number (数字)
JavaScript 的 number 类型包含了整数和浮点数,但实际上它们在底层都是用同一种格式存储的。
底层都是用64位浮点数(IEEE 754标准)存储的。这是JavaScript的一个重要特点:
JavaScript Number类型设计的优劣势分析
优势 👍
1. 简化性
开发者不需要考虑整数和浮点数的区别
减少类型转换的复杂性
API设计更简单统一
2. 动态性
支持JavaScript的动态类型特性
数字运算结果类型自动确定
减少类型声明的负担
3. 兼容性
与JSON格式天然兼容
跨平台数据交换更容易
劣势 👎
1. 精度问题
0.1 + 0.2 === 0.3 // false
0.1 + 0.2 // 0.30000000000000004
2. 大整数精度丢失
9007199254740992 === 9007199254740993 // true (应该是false!)
3. 性能开销
所有数字都用64位存储,浪费内存
整数运算也需要浮点数处理,速度较慢
4. 安全范围限制
安全整数范围: ±9,007,199,254,740,991
超出范围会有精度问题
5. 特殊值混乱
typeof NaN === 'number' // true (令人困惑)
NaN === NaN // false
现代JavaScript通过引入 BigInt
类型来部分解决大整数问题,但基础Number设计仍然是简单性优先于精确性的体现。
typeof 42 // "number" - 整数
typeof 3.14 // "number" - 浮点数
typeof -100 // "number" - 负整数
typeof NaN // "number" - 特殊值
typeof Infinity // "number" - 无穷大
所以JavaScript实际上没有严格意义上的整数类型,所有数字都是浮点数,只是有些数字在显示时看起来像整数。
let integer = 42; // 整数
let float = 3.14; // 浮点数
let negative = -100; // 负数
let scientific = 1.5e3; // 科学计数法: 1500
let infinity = Infinity; // 无穷大
let negInfinity = -Infinity; // 负无穷大
let notANumber = NaN; // Not a Number
// 特殊值检测
console.log(Number.isNaN(NaN)); // true
console.log(Number.isFinite(42)); // true
console.log(Number.isInteger(42)); // true
注意事项:
JavaScript 中所有数字都是 64 位浮点数
最大安全整数:
Number.MAX_SAFE_INTEGER
(2^53 - 1)浮点数计算可能不精确:
0.1 + 0.2 !== 0.3
2. String (字符串)
let single = 'Hello';
let double = "World";
let backtick = `Template ${single} ${double}`;
let multiline = `多行
字符串`;
// 字符串方法
let str = "JavaScript";
console.log(str.length); // 10
console.log(str.charAt(0)); // "J"
console.log(str.indexOf('Script')); // 4
console.log(str.slice(0, 4)); // "Java"
console.log(str.toUpperCase()); // "JAVASCRIPT"
字符串特性:
不可变 (immutable)
支持 Unicode
模板字符串支持表达式和多行
3. Boolean (布尔值)
let isTrue = true;
let isFalse = false;
// 布尔值转换 (Truthy/Falsy)
// Falsy 值 (转换为 false 的值):
console.log(Boolean(false)); // false
console.log(Boolean(0)); // false
console.log(Boolean(-0)); // false
console.log(Boolean(0n)); // false
console.log(Boolean("")); // false
console.log(Boolean(null)); // false
console.log(Boolean(undefined)); // false
console.log(Boolean(NaN)); // false
// 其他所有值都是 Truthy
console.log(Boolean("0")); // true
console.log(Boolean([])); // true
console.log(Boolean({})); // true
4. undefined
let undeclared;
console.log(undeclared); // undefined
function noReturn() {}
console.log(noReturn()); // undefined
let obj = {};
console.log(obj.nonExistent); // undefined
undefined 的产生:
声明但未赋值的变量
函数没有返回值
访问对象不存在的属性
函数参数未传递
5. null
let empty = null;
console.log(empty); // null
console.log(typeof null); // "object" (这是一个历史 bug)
// null vs undefined
console.log(null == undefined); // true (宽松比较)
console.log(null === undefined); // false (严格比较)
null 的含义:
表示"空"或"无"
需要显式赋值
通常用于表示对象的空值
6. Symbol (符号) - ES6
let sym1 = Symbol();
let sym2 = Symbol('description');
let sym3 = Symbol('description');
console.log(sym2 === sym3); // false (每个 Symbol 都是唯一的)
console.log(sym2.toString()); // "Symbol(description)"
// 全局 Symbol
let globalSym1 = Symbol.for('global');
let globalSym2 = Symbol.for('global');
console.log(globalSym1 === globalSym2); // true
// 用作对象属性
let obj = {};
obj[sym1] = 'value';
console.log(Object.keys(obj)); // [] (Symbol 属性不可枚举)
Symbol 特性:
每个 Symbol 都是唯一的
不能被隐式转换为字符串
常用作对象的私有属性键
7. BigInt - ES2020
let bigInt1 = 123n;
let bigInt2 = BigInt(123);
let bigInt3 = BigInt("123456789012345678901234567890");
console.log(typeof bigInt1); // "bigint"
console.log(bigInt1 + bigInt2); // 246n
// 注意:不能与 Number 直接运算
// console.log(bigInt1 + 123); // TypeError
console.log(bigInt1 + BigInt(123)); // 246n
BigInt 特性:
可以表示任意大的整数
不能与 Number 类型直接运算
不支持 Math 对象的方法
引用类型 (Reference Types)
Object (对象)
引用类型存储在堆内存中,变量存储的是引用地址,按引用传递。
// 普通对象
let person = {
name: "张三",
age: 30,
sayHello: function() {
return `Hello, I'm ${this.name}`;
}
};
// 数组 (Array)
let numbers = [1, 2, 3, 4, 5];
let mixed = [1, "hello", true, null, {key: "value"}];
// 函数 (Function)
function greet(name) {
return `Hello, ${name}!`;
}
// 日期 (Date)
let now = new Date();
let specificDate = new Date('2024-01-01');
// 正则表达式 (RegExp)
let regex = /\d+/g;
let regexConstructor = new RegExp('\\d+', 'g');
// 其他内置对象
let map = new Map();
let set = new Set();
let promise = new Promise((resolve) => resolve('done'));
类型检测
1. typeof 操作符
console.log(typeof 42); // "number"
console.log(typeof "hello"); // "string"
console.log(typeof true); // "boolean"
console.log(typeof undefined); // "undefined"
console.log(typeof null); // "object" (历史bug)
console.log(typeof Symbol()); // "symbol"
console.log(typeof 123n); // "bigint"
console.log(typeof {}); // "object"
console.log(typeof []); // "object"
console.log(typeof function(){}); // "function"
2. instanceof 操作符
let arr = [1, 2, 3];
let obj = {};
let date = new Date();
console.log(arr instanceof Array); // true
console.log(obj instanceof Object); // true
console.log(date instanceof Date); // true
console.log(arr instanceof Object); // true (数组也是对象)
// instanceof 判断obj在其原型链上是否有Array原型
alert(obj instanceof Array);
3. Object.prototype.toString.call() - 最可靠的类型检测
// 标准的类型检测函数
function getType(value) {
return Object.prototype.toString.call(value).slice(8, -1);
}
console.log(getType(42)); // "Number"
console.log(getType("hello")); // "String"
console.log(getType(true)); // "Boolean"
console.log(getType(null)); // "Null"
console.log(getType(undefined)); // "Undefined"
console.log(getType([])); // "Array"
console.log(getType({})); // "Object"
console.log(getType(new Date())); // "Date"
console.log(getType(/regex/)); // "RegExp"
// 具体用法示例
Object.prototype.toString.call([]) === "[object Array]"; // 判断是否是Array对象
Object.prototype.toString.call(function(){}) === "[object Function]"; // 判断是否为函数对象
// 另一种实现方式
var toString = Object.prototype.toString;
function getType(o) {
return toString.call(o).slice(8, -1);
}
console.log(getType(null)); // Null
console.log(getType(undefined)); // Undefined
console.log(getType(1)); // Number
console.log(getType('aaa')); // String
console.log(getType(new Boolean(true))); // Boolean
function A() { this.a = 11; }
console.log(getType(new A())); // Object
类型转换
隐式类型转换
// 字符串转换
console.log("5" + 3); // "53" (数字转字符串)
console.log("5" - 3); // 2 (字符串转数字)
console.log("5" * 3); // 15
console.log("5" / 3); // 1.6666666666666667
// 布尔值转换
console.log(true + 1); // 2
console.log(false + 1); // 1
console.log("" == false); // true
console.log(0 == false); // true
// 对象转原始值
console.log({} + ""); // "[object Object]"
console.log([] + ""); // ""
console.log([1,2] + ""); // "1,2"
显式类型转换
// 转数字
console.log(Number("123")); // 123
console.log(Number("123.45")); // 123.45
console.log(Number("123abc")); // NaN
console.log(parseInt("123")); // 123
console.log(parseInt("123.45")); // 123
console.log(parseFloat("123.45")); // 123.45
console.log(+"123"); // 123 (一元加号)
// 转字符串
console.log(String(123)); // "123"
console.log(String(true)); // "true"
console.log((123).toString()); // "123"
// 转布尔值
console.log(Boolean(1)); // true
console.log(Boolean(0)); // false
console.log(!!1); // true (双重否定)
console.log(!!"hello"); // true
JavaScript的历史包袱与陷阱
JavaScript诞生的"不靠谱"历史
JavaScript诞生于1995年,Brendan Eich在短短10天内设计完成。时间紧迫导致了很多设计决策比较仓促,为了向后兼容性,这些"历史bug"一直保留至今。
经典的"不靠谱"行为
// 1. 经典的 typeof null bug
console.log(typeof null); // "object" (明明是null,却说是object!)
console.log(null instanceof Object); // false (但instanceof又正确)
// 2. 令人困惑的相等比较(隐式类型转换)
console.log([] == false); // true (空数组等于false???)
console.log("" == 0); // true (空字符串等于0)
console.log(" " == 0); // true (空格字符串也等于0)
console.log("0" == false); // true (字符串"0"等于false)
console.log(null == undefined); // true (null等于undefined)
// 但严格相等就正常了
console.log([] === false); // false ✓
console.log(null === undefined); // false ✓
// 3. 加法运算的混乱(+ 操作符重载)
console.log(1 + "2"); // "12" (数字+字符串=字符串拼接)
console.log("2" + 1); // "21"
console.log(1 + 2 + "3"); // "33" (先算1+2=3,再拼接"3")
console.log("3" + 1 + 2); // "312" (从左到右全部拼接)
// 减法、乘法、除法就正常(自动转数字)
console.log("3" - 1); // 2 ✓
console.log("3" * 2); // 6 ✓
console.log("6" / 2); // 3 ✓
// 4. NaN 的奇怪行为
console.log(NaN === NaN); // false (NaN不等于自己!)
console.log(NaN == NaN); // false (宽松比较也不行)
console.log(Number.isNaN(NaN)); // true (只能用这个方法判断)
// 5. 浮点数精度问题(所有语言都有,但JS特别明显)
console.log(0.1 + 0.2); // 0.30000000000000004
console.log(0.1 + 0.2 === 0.3); // false
// 6. 自动类型转换的更多奇怪例子
console.log(true + true); // 2 (true转成1)
console.log(true + false); // 1
console.log("5" - "4"); // 1 (字符串减法转数字)
console.log("5" + - "4"); // "5-4" (复杂的操作优先级)
console.log([] + []); // "" (两个空数组相加得空字符串)
console.log({} + []); // "[object Object]" (对象转字符串)
console.log([] + {}); // "[object Object]"
// 7. 函数参数的诡异行为
function test(a) {
arguments[0] = 99;
console.log(a); // 99 (参数和arguments关联!)
}
test(1);
为什么会这样?
设计时间紧迫:10天设计一门语言,很多细节考虑不周
多种语言影响:同时借鉴了Java、Scheme、Self等语言特性
向后兼容:一旦发布就不能破坏现有代码,bug也要保留
隐式类型转换:为了方便使用,但规则复杂且不直观
实践建议
1. 类型安全编程(避开历史陷阱)
// ✅ 总是使用严格相等
if (value === null) { /* 明确检查null */ }
if (typeof value === 'string') { /* 明确类型检查 */ }
// ✅ 避免隐式转换陷阱,但可以利用有用的特性
if (value == null) { /* 同时检查 null 和 undefined,这个是有用的 */ }
// ✅ 显式类型转换
const num = Number(str); // 而不是 +str 或 str * 1
const str = String(num); // 而不是 num + ""
const bool = Boolean(value); // 而不是 !!value
// ✅ 安全的数组和对象检查
if (Array.isArray(value)) { /* 而不是 typeof value === 'object' */ }
if (value && typeof value === 'object' && value !== null) { /* 检查对象 */ }
// ✅ 安全的NaN检查
if (Number.isNaN(value)) { /* 而不是 value === NaN */ }
// ✅ 浮点数比较
function isEqual(a, b, epsilon = 0.0001) {
return Math.abs(a - b) < epsilon;
}
console.log(isEqual(0.1 + 0.2, 0.3)); // true
// ✅ 使用适当的类型检查
function processArray(arr) {
if (!Array.isArray(arr)) {
throw new TypeError('Expected an array');
}
// 处理数组
}
2. 现代 JavaScript 中的类型处理
// ✅ 使用可选链操作符 (ES2020) - 避免属性访问错误
const user = { profile: { name: "张三" } };
console.log(user.profile?.name); // "张三"
console.log(user.profile?.age); // undefined (不会报错)
// ✅ 使用空值合并操作符 (ES2020) - 避免falsy判断陷阱
const defaultName = user.name ?? "匿名用户"; // 只有null/undefined才用默认值
const defaultAge = user.age ?? 0; // 不会因为age=0而用默认值
// ✅ 使用模板字符串 - 避免复杂的字符串拼接
const message = `用户 ${user.name ?? '匿名'} 的年龄是 ${user.age ?? '未知'}`;
// ✅ 使用现代工具
// TypeScript: 静态类型检查,编译时发现类型错误
// ESLint: 检查常见的JavaScript陷阱
// Prettier: 统一代码格式,避免歧义
3. 为什么JavaScript仍然流行?
尽管有这些历史包袱,JavaScript依然是最流行的编程语言:
生态系统庞大:npm包数量最多,轮子最全
无处不在:浏览器、Node.js服务端、React Native移动端、Electron桌面应用
现代工具链完善:TypeScript、Babel、Webpack等解决了很多历史问题
语言在持续进步:ES6+增加了很多现代特性,弥补了早期设计不足
开发效率高:动态类型在原型开发和小项目中确实提高开发效率
学习成本低:语法简单,容易上手
4. 现代JavaScript开发建议
// ✅ 在大型项目中使用TypeScript
interface User {
name: string;
age: number;
email?: string;
}
// ✅ 使用ESLint配置捕获常见错误
// .eslintrc.js 中配置规则避免常见陷阱
// ✅ 使用严格模式
'use strict'; // 或在模块中自动启用
// ✅ 优先使用const/let,避免var
const API_URL = 'https://api.example.com';
let counter = 0;
// ✅ 使用现代语法特性
const users = await fetch('/api/users').then(res => res.json());
const activeUsers = users.filter(user => user.active);
总结
JavaScript 的数据类型系统虽然相对简单,但理解其细节对于编写健壮的代码至关重要:
7种原始类型 + 1种引用类型
原始类型:不可变,按值传递,存储在栈中
引用类型:可变,按引用传递,存储在堆中
掌握类型检测方法和转换规则
使用现代 JavaScript 特性提高代码安全性
理解这些概念是掌握 JavaScript 的基础,也是进阶学习的重要前提。
深入对象操作
对象属性操作
// 枚举对象属性
for(var i in obj) {
console.log(obj[i]);
}
// 定义函数对象和原型
function foo() {}
foo.prototype.z = 3; // 给原型加属性
var obj = new foo(); // 实例化对象
obj.a = 10; // 添加对象自有属性
console.log(obj.a); // 访问对象自有属性
console.log(obj.z); // 访问从原型链继承的属性
// 判断属性来源
obj.hasOwnProperty('z'); // false - 判断是否为对象自有属性
obj.hasOwnProperty('a'); // true
// 动态添加和删除属性
obj.z = 5; // 动态添加属性z
console.log(foo.prototype.z); // 3 - 原型链里的z并没有改变
delete obj.z; // 删除obj的动态属性z,但无法删除原型上的属性
原型链操作
// 创建对象指定原型链
var obj = Object.create({x: 1}); // obj ---> {x:1} ---> Object.prototype ---> null
obj.toString(); // 可以访问Object.prototype上的方法
var obj = Object.create(null); // obj ---> null (没有原型链)
obj.toString; // undefined
属性描述符
// 查看属性描述符
console.log(Object.getOwnPropertyDescriptor({pro: 'aaa'}, 'pro'));
// {value: 'aaa', writable: true, enumerable: true, configurable: true}
// writable - 决定属性是否可写
// enumerable - 决定属性是否可枚举
// configurable- 决定属性是否可删除
定义属性
// 定义单个属性
var cat = {};
Object.defineProperty(cat, 'price', {
enumerable: false,
value: 1000,
writable: false,
configurable: false
});
console.log(cat.propertyIsEnumerable('price')); // false - 是否可枚举
console.log(cat.hasOwnProperty('price')); // true - 是否自有属性
cat.price = 20; // 尝试改变属性值(失败,因为writable:false)
console.log(cat.price); // 1000
console.log(delete cat.price); // false - 尝试删除属性(失败)
for(var i in cat) { console.log(cat[i]); } // 不会枚举price
// 定义多个属性
var person = {};
Object.defineProperties(person, {
title: {value: 'fe', enumerable: true},
corp: {value: "BABA", enumerable: true},
salary: {value: 10000, enumerable: true, writable: true},
luck: {
get: function() {
return Math.random() > 0.5 ? 'good' : 'bad';
}
},
promote: {
set: function(level) {
this.salary *= 1 + level * 0.1;
},
get: function() {
return this.salary;
}
}
});
console.log(person.salary); // 10000
console.log(person.luck); // 'good' 或 'bad' (随机)
person.promote = 2; // 调用set方法
console.log(person.promote); // 调用get方法,返回调整后的salary
属性访问和查找
// 安全的属性读取
var yz = obj && obj.y && obj.y.z;
var defaultValue = obj[b] || 'default';
// 删除属性
delete obj.a; // 删除对象属性
delete Object.prototype; // false - 不能删除prototype属性
var descriptor = Object.getOwnPropertyDescriptor(Object, 'prototype');
descriptor.configurable; // false - 该属性决定属性是否可删除
// 注意:delete只能删除对象的属性,不能删除全局变量和局部变量
// 查找属性
console.log(cat.propertyIsEnumerable('legs')); // true - 可枚举
console.log(cat.propertyIsEnumerable('toString')); // false - 不可枚举
'legs' in cat; // true - 会查找原型链
cat.hasOwnProperty('legs'); // 只查找对象自有属性
Object.keys(cat); // 返回所有可枚举属性的key
对象状态控制
var obj = {x: 1, y: 2};
// 扩展性控制
console.log(Object.isExtensible(obj)); // true
Object.preventExtensions(obj); // 设置对象不可再增加属性
console.log(Object.isExtensible(obj)); // false
obj.z = 1;
console.log(obj.z); // undefined
// 密封性控制
console.log(Object.isSealed(obj)); // false
Object.seal(obj); // 设置对象属性不可删除
console.log(Object.isSealed(obj)); // true
// 冻结控制
console.log(Object.isFrozen(obj)); // false
Object.freeze(obj); // 设置对象属性不可修改值
console.log(Object.isFrozen(obj)); // true
对象序列化
var obj = {
x: 1,
y: true,
z: [1, 2, 3],
nullVal: null,
undef: undefined // undefined不会出现在序列化字符串里
};
console.log(JSON.stringify(obj)); // {"x":1,"y":true,"z":[1,2,3],"nullVal":null}
var obj2 = JSON.parse('{"x":1}');
console.log(obj2.x); // 1
// 自定义序列化 - toJSON方法
var obj = {
x: 1,
y: 2,
o: {
o1: 1,
o2: 2,
toJSON: function() {
return this.o1 + this.o2;
}
}
};
console.log(JSON.stringify(obj)); // {"x":1,"y":2,"o":3}
toString 和 valueOf
// 对象被当做字符串使用时,会先调用valueOf,若不能返回原始类型,再调用toString
var obj = {x: 1, y: 2};
console.log(obj.toString()); // [object Object]
obj.toString = function() {
return this.x + this.y;
};
console.log("Result: " + obj); // Result: 3
obj.valueOf = function() {
return this.x + this.y + 100;
};
console.log("Result: " + obj); // Result: 103 (valueOf优先级更高)
面向对象编程实践
基本概念
面向对象编程的核心思想是:不需要定义全局函数去操作不同的数据类型,而是数据类型本身具有方法去操作自身的值。使用 a.sort()
而不是 sort(a)
。
动态对象创建
// 利用JS动态加载特性,通过匿名函数创建对象
(function() {
window.cky = {name: "caokaiyan"};
cky.print = function() {
console.log("i am " + this.name + " and i am " + this.age + " years old!");
}
})();
cky.age = 20; // 动态添加属性
console.log(cky);
cky.print();
对象字面量
var book = {
topic: 'javascript',
fat: 10,
line: [32, 43, 53, 64],
total: function() {
console.log(this.fat);
}
};
console.log(book);
book.author = 'caokaiyan'; // 动态添加对象属性
console.log(book);
数组对象扩展
var point = [{x: 1, y: 3}, {x: 1, y: 4}];
point.dist = function() {
// 使用局部变量 vs 成员变量的区别:
// this.p1 = this[0]; // 成员变量,对象销毁时才释放
// var p1 = this[0]; // 局部变量,函数调用完就销毁
var p1 = this[0];
var p2 = this[1];
var res = p1.x * p2.x + p1.y * p2.y;
console.log(res);
return res;
};
console.log(point);
point.dist();
构造函数和原型
// 简单的构造函数示例
var Point = function(x, y) {
this.x = x;
this.y = y;
};
console.log(Point); // Point 函数
var point = new Point(2, 3);
console.log(point); // Point 对象
// 动态添加原型方法
Point.prototype.r = function() {
return this.x * this.y;
};
console.log(point.r()); // 6
构造函数中的作用域
function A() {
// 成员方法 - 可以被实例访问
this.b = function() {
console.log("i am b");
c(); // 可以调用内部函数
};
// 内部函数 - 不能被实例直接访问
function c() {
console.log("i am c");
}
// 内部函数变量形式 - 不能被实例访问
var d = function() {
console.log("i am d");
};
// 成员属性 - 可以被实例访问
this.e = "i am e";
// 局部变量 - 不能被实例访问
var f = "i am f";
}
// 原型方法 - 所有实例共享
A.prototype.g = function() {
console.log("i am g");
};
var cky = new A();
cky.b(); // "i am b" 然后 "i am c"
// cky.c(); // TypeError: cky.c is not a function
// cky.d(); // TypeError: cky.d is not a function
console.log(cky.e); // "i am e"
console.log(cky.f); // undefined
cky.g(); // "i am g"
重要概念总结
原型链很重要:它决定了对象继承哪些属性和方法
this关键字:在构造函数中使用this定义的是成员变量,用var定义的是局部变量
属性访问:区分自有属性和继承属性,使用hasOwnProperty()判断
对象状态:理解可扩展性、密封性、冻结性的区别
类型检测:Object.prototype.toString.call()是最可靠的类型检测方法
JavaScript数据类型进阶 - 实用指南
学习优先级说明
基于你的实用主义学习风格,本文档按重要性分级:
🔥 必须掌握 - 日常开发必用
⚡ 重要了解 - 常见场景会用到
📚 了解即可 - 特殊场景才用,现在可跳过
🔥 必须掌握:字符串操作
基本字符串方法
let str = "JavaScript";
// 长度和访问
console.log(str.length); // 10
console.log(str[0]); // "J"
console.log(str.charAt(0)); // "J" (更安全)
// 查找
console.log(str.indexOf("Script")); // 4
console.log(str.includes("Script")); // true (推荐)
console.log(str.startsWith("Java")); // true
console.log(str.endsWith("Script")); // true
// 截取
console.log(str.slice(0, 4)); // "Java"
console.log(str.slice(-6)); // "Script" (从后往前)
console.log(str.substring(0, 4)); // "Java"
// 替换
console.log(str.replace("Java", "Type")); // "TypeScript"
console.log(str.replaceAll("a", "A")); // "JAvAScript"
// 大小写
console.log(str.toLowerCase()); // "javascript"
console.log(str.toUpperCase()); // "JAVASCRIPT"
// 去空格
let text = " hello world ";
console.log(text.trim()); // "hello world"
// 分割
console.log("a,b,c".split(",")); // ["a", "b", "c"]
模板字符串(重要!)
let name = "张三";
let age = 30;
// 传统拼接(不推荐)
let oldWay = "你好,我是" + name + ",今年" + age + "岁";
// 模板字符串(推荐)
let newWay = `你好,我是${name},今年${age}岁`;
// 多行字符串
let html = `
<div>
<h1>${name}</h1>
<p>年龄:${age}</p>
</div>
`;
// 表达式
let message = `明年我就${age + 1}岁了`;
🔥 必须掌握:数组基础
数组创建和访问
// 创建数组
let fruits = ["苹果", "香蕉", "橙子"];
let numbers = [1, 2, 3, 4, 5];
let mixed = ["文本", 123, true, null];
// 访问元素
console.log(fruits[0]); // "苹果"
console.log(fruits.length); // 3
// 修改元素
fruits[1] = "葡萄";
console.log(fruits); // ["苹果", "葡萄", "橙子"]
核心数组方法
let arr = [1, 2, 3];
// 添加/删除元素
arr.push(4); // 末尾添加: [1, 2, 3, 4]
arr.pop(); // 末尾删除: [1, 2, 3]
arr.unshift(0); // 开头添加: [0, 1, 2, 3]
arr.shift(); // 开头删除: [1, 2, 3]
// 查找元素
console.log(arr.indexOf(2)); // 1
console.log(arr.includes(2)); // true
// 转换为字符串
console.log(arr.join(",")); // "1,2,3"
console.log(arr.join(" - ")); // "1 - 2 - 3"
🔥 必须掌握:重要数组方法
数组遍历和转换
let numbers = [1, 2, 3, 4, 5];
// forEach - 遍历每个元素
numbers.forEach(num => {
console.log(num * 2);
});
// map - 转换数组(返回新数组)
let doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
// filter - 过滤数组
let evenNumbers = numbers.filter(num => num % 2 === 0);
console.log(evenNumbers); // [2, 4]
// find - 找到第一个符合条件的元素
let found = numbers.find(num => num > 3);
console.log(found); // 4
// some/every - 检测数组
console.log(numbers.some(num => num > 3)); // true (有元素>3)
console.log(numbers.every(num => num > 0)); // true (所有元素>0)
实际应用示例
// 用户数据处理(常见场景)
let users = [
{name: "张三", age: 25, active: true},
{name: "李四", age: 30, active: false},
{name: "王五", age: 35, active: true}
];
// 获取所有活跃用户的姓名
let activeUserNames = users
.filter(user => user.active)
.map(user => user.name);
console.log(activeUserNames); // ["张三", "王五"]
// 检查是否有成年用户
let hasAdult = users.some(user => user.age >= 18);
console.log(hasAdult); // true
🔥 必须掌握:解构赋值
数组解构
// 基本用法
let arr = [1, 2, 3];
let [a, b, c] = arr;
console.log(a); // 1
console.log(b); // 2
// 跳过元素
let [first, , third] = arr;
console.log(first); // 1
console.log(third); // 3
// 默认值
let [x, y, z = 0] = [1, 2];
console.log(z); // 0
// 交换变量
let num1 = 10, num2 = 20;
[num1, num2] = [num2, num1];
console.log(num1); // 20
对象解构
let user = {
name: "张三",
age: 30,
email: "zhangsan@example.com"
};
// 基本用法
let {name, age} = user;
console.log(name); // "张三"
console.log(age); // 30
// 重命名变量
let {name: userName, age: userAge} = user;
console.log(userName); // "张三"
// 默认值
let {name, age, city = "北京"} = user;
console.log(city); // "北京"
// 函数参数解构(很实用!)
function greetUser({name, age}) {
return `你好${name},你今年${age}岁`;
}
console.log(greetUser(user)); // "你好张三,你今年30岁"
⚡ 重要了解:数字类型
数字操作基础
// 数字方法
let num = 123.456;
console.log(num.toFixed(2)); // "123.46" (保留2位小数)
console.log(num.toString()); // "123.456"
console.log(parseInt("123px")); // 123
console.log(parseFloat("123.45")); // 123.45
// 检查数字
console.log(Number.isNaN(NaN)); // true
console.log(Number.isFinite(123)); // true
console.log(Number.isInteger(123)); // true
// 数学运算
console.log(Math.round(4.7)); // 5
console.log(Math.floor(4.7)); // 4
console.log(Math.ceil(4.3)); // 5
console.log(Math.random()); // 0-1随机数
console.log(Math.max(1, 3, 2)); // 3
console.log(Math.min(1, 3, 2)); // 1
⚡ 重要了解:Object.keys/values/entries
对象遍历
let user = {
name: "张三",
age: 30,
city: "北京"
};
// 获取所有键
console.log(Object.keys(user)); // ["name", "age", "city"]
// 获取所有值
console.log(Object.values(user)); // ["张三", 30, "北京"]
// 获取键值对
console.log(Object.entries(user)); // [["name", "张三"], ["age", 30], ["city", "北京"]]
// 实际应用:遍历对象
Object.entries(user).forEach(([key, value]) => {
console.log(`${key}: ${value}`);
});
// 将对象转为数组处理,再转回对象
let doubledAges = Object.fromEntries(
Object.entries(user)
.filter(([key, value]) => typeof value === 'number')
.map(([key, value]) => [key, value * 2])
);
⚡ 重要了解:JSON操作
JSON基础
// 对象转JSON
let user = {
name: "张三",
age: 30,
hobbies: ["读书", "游泳"]
};
let jsonString = JSON.stringify(user);
console.log(jsonString); // '{"name":"张三","age":30,"hobbies":["读书","游泳"]}'
// JSON转对象
let userData = JSON.parse(jsonString);
console.log(userData.name); // "张三"
// 实际应用:本地存储
localStorage.setItem('user', JSON.stringify(user));
let savedUser = JSON.parse(localStorage.getItem('user'));
📚 了解即可(现在可跳过)
原始类型的方法
// 了解即可:原始类型会临时包装成对象
let str = "hello";
console.log(str.toUpperCase()); // 临时创建String对象
Map和Set
// 了解即可:特殊数据结构,普通对象和数组够用了
let map = new Map();
let set = new Set();
WeakMap和WeakSet
// 了解即可:高级特性,新手不需要
let weakMap = new WeakMap();
Iterable对象
// 了解即可:for...of背后的机制
for (let item of [1, 2, 3]) {
console.log(item);
}
日期对象
// 了解即可:现代项目通常用date-fns或dayjs库
let now = new Date();
console.log(now.getFullYear()); // 获取年份
学习建议(类比驾驶)
现在重点练习(基本驾驶技能)
字符串操作 = 方向盘(最常用的控制)
数组方法 = 刹车和油门(核心操作)
解构赋值 = 倒车镜(提高效率的工具)
JSON操作 = 导航系统(数据传输必备)
了解概念即可(高级驾驶技巧)
Map/Set = 定速巡航(特殊场景才用)
WeakMap/WeakSet = 自适应巡航(高级功能)
Iterable = 发动机工作原理(知道即可)
实际开发模式
// 典型的数据处理流程
const processUsers = (users) => {
return users
.filter(user => user.active) // 过滤活跃用户
.map(({name, age, email}) => ({ // 解构重组数据
displayName: name.toUpperCase(),
info: `${age}岁`,
contact: email
}))
.sort((a, b) => a.displayName.localeCompare(b.displayName));
};
// API数据处理
const saveUserData = (userData) => {
const jsonData = JSON.stringify(userData);
localStorage.setItem('users', jsonData);
};
const loadUserData = () => {
const jsonData = localStorage.getItem('users');
return jsonData ? JSON.parse(jsonData) : [];
};
总结
掌握了这些核心内容,你就能处理90%的日常开发需求。就像学会了基本驾驶技能,你就能安全上路了。那些高级特性,等你熟练了再学也不迟!
下一步建议:拿这些方法去实际项目中练习,比如处理用户列表、购物车数据等,实战是最好的学习方式。