目录
JavaScript 数组详解
一、数组的创建与基本特性
二、数组核心操作
1. 增删元素
2. 截取与合并
3. 查找与判断
三、数组迭代与转换
1. 迭代方法(不修改原数组)
2. 排序与反转
四、map与join
1. map 方法
2. join 方法
3. map 与 join 联合使用
四、ES6+ 新增方法
五、性能优化与陷阱
六、常见问题与解决方案
JavaScript 对象详解
一、对象的创建与基本特性
1. 创建对象
2. 键的特性
3. 属性的特性(Property Attributes)
二、对象操作
1. 属性访问
2. 属性增删改查
3. 遍历属性
三、对象高级特性
1. 原型链(Prototype Chain)
2. this 的指向
3. 对象拷贝
四、ES6+ 新增特性
1. 计算属性名
2. 方法简写
3. Object 静态方法
4. Symbol 作为键
五、常见问题与解决方案
1. 对象引用传递
2. 原型污染
3. 枚举与不可枚举属性
JavaScript 数组详解
数组(Array)是 JavaScript 中最重要且灵活的数据结构之一,用于存储有序的元素集合。
一、数组的创建与基本特性
- 数组做为数据的集合,它的单元值可以是任意数据类型。
-
创建数组
// 字面量(推荐) let arr1 = [1, "a", true]; // 构造函数(慎用空参数) let arr2 = new Array(3); // [空 ×3](稀疏数组) let arr3 = new Array(1, 2, 3); // [1, 2, 3]// ES6: Array.of 避免参数歧义 let arr4 = Array.of(3); // [3],而不是 [空 ×3]
可以使用arr.length获取数组长度。
-
稀疏数组与密集数组
-
稀疏数组:存在空位(empty slots),如
new Array(3)
。 -
密集数组:所有位置均有值,如
[1, 2, 3]
。
let sparse = [1, , 3]; // 稀疏数组(索引1为空) console.log(1 in sparse); // false(空位不存在属性)
-
-
数组本质
-
数组是特殊类型的对象,索引为属性名,
length
属性自动更新。 -
可以动态扩展,但非连续内存结构(与类型化数组不同)。
-
二、数组核心操作
1. 增删元素
方法 | 行为 | 示例 |
---|---|---|
push() | 末尾添加元素,返回新长度 | arr.push(4) → [1,2,3,4] |
pop() | 移除并返回末尾元素 | arr.pop() → 3,arr变为[1,2] |
unshift() | 开头添加元素,返回新长度 | arr.unshift(0) → [0,1,2,3] |
shift() | 移除并返回开头元素 | arr.shift() → 0,arr变为[1,2,3] |
splice() | 删除/插入元素(直接修改原数组) | arr.splice(1,1,"x") → [1,"x",3] |
2. 截取与合并
方法 | 行为 | 示例 |
---|---|---|
slice() | 返回子数组(不修改原数组) | arr.slice(1,3) → [2,3] |
concat() | 合并数组(不修改原数组) | arr.concat([4,5]) → [1,2,3,4,5] |
3. 查找与判断
方法 | 行为 | 示例 |
---|---|---|
indexOf() | 返回元素首次出现的索引 | arr.indexOf(2) → 1 |
includes() | 检查是否包含元素(ES6+) | arr.includes(2) → true |
find() | 返回第一个满足条件的元素(ES6+) | arr.find(x => x > 1) → 2 |
some() | 检查是否有元素满足条件 | arr.some(x => x > 2) → true |
every() | 检查是否所有元素满足条件 | arr.every(x => x < 5) → true |
三、数组迭代与转换
1. 迭代方法(不修改原数组)
方法 | 用途 | 示例 |
---|---|---|
forEach() | 遍历数组,无返回值 | arr.forEach(x => console.log(x)) |
map() | 映射新数组 | arr.map(x => x * 2) → [2,4,6] |
filter() | 过滤符合条件的元素 | arr.filter(x => x > 1) → [2,3] |
reduce() | 累积计算结果(左到右) | arr.reduce((sum, x) => sum + x, 0) → 6 |
reduceRight() | 累积计算结果(右到左) | arr.reduceRight((sum, x) => sum + x, 0) → 6 |
2. 排序与反转
方法 | 行为 | 示例 |
---|---|---|
sort() | 原地排序(默认按字符串Unicode排序) | [3,1,2].sort() → [1,2,3] |
reverse() | 反转数组 | arr.reverse() → [3,2,1] |
自定义排序示例:
let numbers = [10, 2, 5];
numbers.sort((a, b) => a - b); // 升序 → [2, 5, 10]
四、map与join
1. map
方法
功能
遍历数组的每个元素,执行回调函数,返回一个由回调结果组成的 新数组。
不修改原数组,适用于数据转换或提取特定信息。
语法
const newArray = arr.map(callback(currentValue, index, array), thisArg);
参数
-
callback
:处理每个元素的函数,参数依次为:-
currentValue
:当前元素值 -
index
(可选):当前元素索引 -
array
(可选):原数组
-
-
thisArg
(可选):回调函数中的this
指向。
示例
const numbers = [1, 2, 3];
const doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6]
map重点在于有返回值,forEach没有返回值(undefined) 。
2. join
方法
功能
将数组所有元素按指定分隔符连接成一个 字符串。
不修改原数组,常用于生成 CSV、URL 参数或格式化文本。
语法
const str = arr.join(separator);
参数
-
separator
(可选):分隔符,默认是,
。若为""
,元素间无分隔符。
示例
const fruits = ['Apple', 'Banana', 'Orange'];
const list = fruits.join(', ');
console.log(list); // "Apple, Banana, Orange"
3. map
与 join
联合使用
结合 map
和 join
可以 先转换数据,再拼接字符串,适合生成结构化文本(如 HTML、日志等)。
const arr = ['red', 'blue', 'pink']// 1. 数组 map方法 处理数据并且 返回一个数组
const newArr = arr.map(function (ele, index) {// console.log(ele) // 数组元素// console.log(index) // 索引号return ele + '颜色'
})
console.log(newArr)// 2. 数组join方法 把数组转换为字符串
// 小括号为空则逗号分割
console.log(newArr.join()) // red颜色,blue颜色,pink颜色
// 小括号是空字符串,则元素之间没有分隔符
console.log(newArr.join('')) //red颜色blue颜色pink颜色
console.log(newArr.join('|')) //red颜色|blue颜色|pink颜色
四、ES6+ 新增方法
方法 | 用途 | 示例 |
---|---|---|
Array.from() | 类数组转数组(如 arguments 、NodeList ) | Array.from("123") → ["1","2","3"] |
Array.of() | 创建数组(避免 new Array 的歧义) | Array.of(3) → [3] |
findIndex() | 返回第一个满足条件的元素索引 | arr.findIndex(x => x > 2) → 2 |
fill() | 填充数组元素 | new Array(3).fill(0) → [0,0,0] |
flat() | 扁平化嵌套数组 | [1, [2,3]].flat() → [1,2,3] |
flatMap() | 映射后扁平化 | [1,2].flatMap(x => [x, x*2]) → [1,2,2,4] |
五、性能优化与陷阱
-
避免稀疏数组
-
空位可能导致
map()
、forEach()
等方法的意外行为。 -
使用
fill()
初始化密集数组:let dense = new Array(3).fill(0); // [0, 0, 0]
-
-
循环性能对比
-
for
循环:最快,适合大数据量。 -
forEach
:代码简洁,但无法break
。 -
for...of
:可迭代任意可迭代对象。
-
-
浅拷贝与深拷贝
-
浅拷贝:
slice()
、concat()
、[...arr]
。 -
深拷贝:
JSON.parse(JSON.stringify(arr))
(无法处理函数、循环引用)。
-
-
类型化数组(Typed Arrays)
处理二进制数据时更高效:let buffer = new ArrayBuffer(16); // 分配16字节内存 let int32View = new Int32Array(buffer);// 32位整数视图(4个元素) int32View[0] = 42;
六、常见问题与解决方案
-
typeof arr
返回"object"
使用Array.isArray()
检测数组:console.log(Array.isArray([1,2])); // true
-
修改原数组的方法
push()
、pop()
、splice()
等会直接修改原数组,注意副作用。 -
伪数组转换
将类数组(如arguments
)转为数组:function example() {let args = Array.from(arguments); // 或 [...arguments] }
JavaScript 对象详解
JavaScript 对象(Object)是键值对的集合,是 JavaScript 的核心数据结构之一,用于表示复杂实体。
一、对象的创建与基本特性
1. 创建对象
JavaScript 对象可以通过多种方式创建:
// 1. 字面量(推荐)
const person = { name: "Alice", age: 25,sayHi() { console.log("Hi!") },sayHello: function () {console.log('Hello!')}
};// 2. 构造函数
const car = new Object();
car.brand = "Toyota";// 3. Object.create()(指定原型)
const protoObj = { x: 1 };
const obj = Object.create(protoObj); // obj.__proto__ = protoObj// 4. ES6 类语法(语法糖)
class Animal {constructor(name) { this.name = name; }
}
const dog = new Animal("Buddy");
2. 键的特性
-
键的类型:字符串或 Symbol(自动转换为字符串)。
-
键的唯一性:同一对象中键唯一,后定义的覆盖先前的。
-
命名:可以使用
""
或''
,一般情况下省略,除非名称遇到特殊符号如空格、中横线等
const obj = { 1: "number key", "1": "string key"
};
console.log(obj[1]); // "string key"(字符串键覆盖数值键)
3. 属性的特性(Property Attributes)
每个属性包含三个隐藏特性(通过 Object.getOwnPropertyDescriptor
查看):
-
value
:属性值。 -
writable
:是否可修改。 -
enumerable
:是否可枚举(出现在for...in
中)。 -
configurable
:是否可删除或修改特性。
Object.defineProperty(obj, "secret", {value: "hidden",writable: false,enumerable: false
});
二、对象操作
1. 属性访问
-
点语法:
obj.key
(键需符合标识符规则)。 -
方括号语法:
obj["key"]
(支持动态键名)。
const key = "age";
console.log(person[key]); // 25
2. 属性增删改查
-
添加/修改属性:
person.job = "Engineer"; // 添加属性 person.age = 26; // 修改属性person.say = function (str) {console.log(str) };
-
删除属性:
delete person.age; // 删除属性(需 configurable 为 true)delete person.say;
-
检查存在:
console.log("name" in person); // true(包括原型链) console.log(person.hasOwnProperty("name")); // true(仅自身属性)
3. 遍历属性
-
for...in
:遍历自身及原型链的可枚举属性。for (const key in person) { console.log(key, person[key]); } //key为字符串
-
Object.keys()
:返回自身可枚举属性的键数组。Object.keys(person); // ["name", "sayHi"]
-
Object.values()
/Object.entries()
(ES6+):Object.values(person); // ["Alice", function...] Object.entries(person); // [["name", "Alice"], ...]
三、对象高级特性
1. 原型链(Prototype Chain)
-
原型继承:对象通过
__proto__
(或Object.getPrototypeOf()
)访问原型。 -
原型链查找:访问属性时,若对象自身不存在,则向上查找原型链。
const parent = { x: 1 };
const child = Object.create(parent);
console.log(child.x); // 1(来自原型)
2. this
的指向
-
方法中的
this
:指向调用方法的对象。 -
箭头函数:无自己的
this
,继承外层作用域。
const obj = {value: 10,getValue() { return this.value; }, // this 指向 objgetValueArrow: () => this.value // this 指向全局(或外层)
};
3. 对象拷贝
-
浅拷贝:复制对象的第一层属性(引用类型属性共享)。
const copy = Object.assign({}, obj); // 或 {...obj}
-
深拷贝:递归复制所有层级(需处理循环引用)。
const deepCopy = JSON.parse(JSON.stringify(obj)); // 无法复制函数、Symbol等
四、ES6+ 新增特性
1. 计算属性名
允许在字面量中使用表达式作为键:
const key = "id";
const obj = { [key + "_1"]: 123, // 键为 "id_1"[Symbol("secret")]: "hidden"
};
2. 方法简写
简化对象方法的定义:
const obj = {method() { /* ... */ } // 等价于 method: function() { ... }
};
3. Object
静态方法
-
Object.assign(target, ...sources)
:合并对象(浅拷贝)。 -
Object.freeze(obj)
:冻结对象(禁止增删改属性)。 -
Object.seal(obj)
:密封对象(禁止增删属性,允许修改值)。
4. Symbol 作为键
Symbol 值唯一,适合定义对象的私有或特殊属性:
const PRIVATE_KEY = Symbol();
const obj = { [PRIVATE_KEY]: "secret" };
console.log(obj[PRIVATE_KEY]); // "secret"
五、常见问题与解决方案
1. 对象引用传递
对象赋值传递引用,修改会相互影响:
const a = { x: 1 };
const b = a;
b.x = 2;
console.log(a.x); // 2
解决:使用拷贝(浅拷贝或深拷贝)。
2. 原型污染
修改 Object.prototype
会影响所有对象:
Object.prototype.customMethod = () => {}; // 所有对象都会继承此方法
解决:避免修改内置原型。
3. 枚举与不可枚举属性
默认添加的属性为可枚举,但某些内置属性(如 toString
)不可枚举:
console.log(Object.keys({})); // [](不包含继承属性)