【前端教程】JavaScript 对象与数组操作实战:从基础到优化
在JavaScript编程中,对象和数组是构建复杂应用的基础。本文将通过多个实用案例,从原始实现出发,逐步优化代码,带你掌握对象创建、数组操作、数据遍历等核心技能。每个案例都包含原始代码解析、优化方案及关键知识点说明,帮助你写出更规范、高效的JavaScript代码。
案例1:创建人类对象及方法实现
需求说明
创建一个包含编号、名称、年龄、性别属性的人类对象,添加"吃饭"功能,并在页面展示该对象信息。
原始代码实现
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>人类对象示例</title>
<script>
var person={'bianhao':007,'name':'诗书画唱','age':20,'sex':'男','chifan' :function(){alert(this.name+"正在吃饭");}
};for(var i in person){document.write(person[i]+" ");
}
document.write("<hr>");
document.write(person.bianhao+" "+person.name + " "+person.age+" "+person.sex);
person.chifan();
</script>
</head>
<body>
</body>
</html>
原始代码解析
- 使用对象字面量创建了
person
对象,包含属性和方法 - 通过
for...in
循环遍历对象属性并输出 - 直接通过点语法访问属性并输出
- 调用
chifan
方法弹出提示框
存在的问题
- 使用
var
声明变量,缺乏块级作用域 - 编号
007
作为数字会被解析为7
,丢失前导零 - 使用
document.write()
输出内容,不利于页面布局控制 - 弹窗提示用户体验不佳
- 代码结构混乱,展示逻辑与数据混合
优化方案
<!DOCTYPE html>
<html>
<head><meta charset="utf-8" /><title>优化后的人类对象示例</title><style>.person-card {border: 1px solid #ccc;padding: 15px;max-width: 300px;margin: 20px;border-radius: 5px;box-shadow: 0 2px 3px rgba(0,0,0,0.1);}.person-info {margin: 8px 0;}.action-btn {margin-top: 10px;padding: 6px 12px;background-color: #4CAF50;color: white;border: none;border-radius: 4px;cursor: pointer;}.action-btn:hover {background-color: #45a049;}.status {margin-top: 10px;color: #666;font-style: italic;}</style>
</head>
<body><div class="person-card" id="personCard"></div><script>// 使用const声明常量对象,属性使用更规范的命名const person = {id: '007', // 改为字符串保留前导零name: '诗书画唱',age: 20,gender: '男', // 使用更规范的属性名// 吃饭方法,改为更新页面状态而非弹窗eat: function() {const statusElement = document.getElementById('status');statusElement.textContent = `${this.name}正在吃饭`;}};// 渲染人员信息到页面function renderPersonInfo(person) {const card = document.getElementById('personCard');// 创建信息展示区域card.innerHTML = `<h3>人员信息</h3><div class="person-info"><strong>编号:</strong>${person.id}</div><div class="person-info"><strong>姓名:</strong>${person.name}</div><div class="person-info"><strong>年龄:</strong>${person.age}</div><div class="person-info"><strong>性别:</strong>${person.gender}</div><button class="action-btn" onclick="person.eat()">执行吃饭动作</button><div class="status" id="status"></div>`;}// 页面加载完成后渲染信息window.onload = function() {renderPersonInfo(person);};</script>
</body>
</html>
优化点说明
- 变量声明:使用
const
替代var
,明确变量不可变 - 属性命名:使用更规范的英文命名(如
id
替代bianhao
,gender
替代sex
) - 数据类型:编号改为字符串类型,保留前导零
- 输出方式:使用DOM操作替代
document.write()
,通过模板字符串构建页面结构 - 用户体验:将弹窗提示改为页面内状态更新,添加交互按钮
- 代码组织:将渲染逻辑封装为函数,提高可维护性
- 样式美化:添加CSS样式,使展示更美观
案例2:人类对象数组创建与遍历
需求说明
创建包含3名人员信息的对象数组,并遍历展示所有人员信息。
原始代码实现
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>人员对象数组</title>
<script>
var persons=[{'bianhao':007,'name':'诗书画唱1','age':20},{'bianhao':666,'name':'诗书画唱2','age':21},{'bianhao':233,'name':'诗书画唱3','age':22},
];for(var i of persons){for(var j in i){document.write(i[j]+" ");}document.write("<br>");
}
</script>
</head>
<body>
</body>
</html>
原始代码解析
- 创建了包含3个人员对象的数组
- 使用
for...of
遍历数组中的每个对象 - 使用
for...in
遍历每个对象的属性 - 通过
document.write()
输出属性值,使用
分隔
存在的问题
- 缺乏表头信息,用户无法直观理解各数值含义
- 输出格式简陋,数据排列不整齐
- 同样存在编号前导零丢失问题
- 直接使用
document.write()
不利于样式控制
优化方案
<!DOCTYPE html>
<html>
<head><meta charset="utf-8" /><title>优化后的人员对象数组</title><style>.persons-table {border-collapse: collapse;width: 80%;max-width: 600px;margin: 20px;border: 1px solid #ddd;}.persons-table th, .persons-table td {padding: 12px;text-align: left;border-bottom: 1px solid #ddd;}.persons-table th {background-color: #f2f2f2;font-weight: bold;}.persons-table tr:hover {background-color: #f5f5f5;}</style>
</head>
<body><table class="persons-table" id="personsTable"><!-- 表格内容将通过JS动态生成 --></table><script>// 人员数据数组,使用规范命名和字符串编号const persons = [{ id: '007', name: '诗书画唱1', age: 20 },{ id: '666', name: '诗书画唱2', age: 21 },{ id: '233', name: '诗书画唱3', age: 22 }];// 渲染人员表格function renderPersonsTable(persons) {const table = document.getElementById('personsTable');// 创建表头const thead = document.createElement('thead');thead.innerHTML = `<tr><th>编号</th><th>姓名</th><th>年龄</th></tr>`;table.appendChild(thead);// 创建表体并添加数据行const tbody = document.createElement('tbody');// 使用forEach方法遍历数组,更现代的写法persons.forEach(person => {const row = document.createElement('tr');row.innerHTML = `<td>${person.id}</td><td>${person.name}</td><td>${person.age}</td>`;tbody.appendChild(row);});table.appendChild(tbody);}// 页面加载完成后渲染表格window.onload = function() {renderPersonsTable(persons);};</script>
</body>
</html>
优化点说明
- 数据结构:统一使用规范的属性命名,编号改为字符串
- 遍历方式:使用
forEach
方法遍历数组,代码更简洁清晰 - 展示方式:使用表格展示数据,添加表头,提高可读性
- 样式美化:添加CSS样式,包括hover效果,提升用户体验
- 代码组织:将渲染逻辑封装为函数,便于维护和复用
- DOM操作:通过创建DOM元素构建表格,而非直接写入文档
案例3:商品构造函数与总价计算
需求说明
创建商品构造函数,实例化3件商品并放入数组,计算并展示商品总价格。
原始代码实现
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>商品总价计算</title>
<script>
var shangPin = function(bianhao, name, price, num) {this.bianhao = bianhao;this.name = name;this.price = price;this.num = num;
}var shangPinShuZu = [new shangPin(1,'苹果', 2, 5),new shangPin(2,'香蕉', 5, 2),new shangPin(3, 'CD', 10, 1)
];alert("计算总价格为:"+jiSuanZongJiaGe(shangPinShuZu));function jiSuanZongJiaGe(shangPinShuZu) {var zongJiaGe = 0; for(var i of shangPinShuZu) {var meiGeShangPinJiaGe = (i.price * i.num);zongJiaGe += meiGeShangPinJiaGe;}return zongJiaGe;
}
</script>
</head>
<body>
</body>
</html>
原始代码解析
- 使用构造函数创建
shangPin
对象类型 - 实例化3个商品对象并存储在数组中
- 定义
jiSuanZongJiaGe
函数计算总价 - 通过弹窗展示计算结果
存在的问题
- 构造函数命名不规范(应使用 PascalCase 命名法)
- 仅通过弹窗展示结果,用户体验不佳
- 未展示商品列表信息,用户无法核对
- 变量命名使用拼音,不符合规范
- 缺乏必要的格式处理(如价格保留两位小数)
优化方案
<!DOCTYPE html>
<html>
<head><meta charset="utf-8" /><title>商品总价计算</title><style>.product-container {margin: 20px;max-width: 800px;}.product-table {border-collapse: collapse;width: 100%;margin-bottom: 20px;}.product-table th, .product-table td {border: 1px solid #ddd;padding: 12px;text-align: left;}.product-table th {background-color: #f2f2f2;}.total-price {font-size: 18px;font-weight: bold;color: #e74c3c;padding: 10px;border: 1px solid #eee;display: inline-block;}</style>
</head>
<body><div class="product-container"><h2>商品列表</h2><table class="product-table" id="productTable"></table><div id="totalPriceContainer"></div></div><script>// 使用ES6 class语法,更清晰的构造函数class Product {constructor(id, name, price, quantity) {this.id = id;this.name = name;this.price = price;this.quantity = quantity;}// 计算单个商品总价的方法getTotalPrice() {return this.price * this.quantity;}}// 创建商品数组const products = [new Product(1, '苹果', 2, 5),new Product(2, '香蕉', 5, 2),new Product(3, 'CD', 10, 1)];// 计算所有商品的总价function calculateTotalPrice(products) {// 使用reduce方法计算总价,更函数式的写法return products.reduce((total, product) => {return total + product.getTotalPrice();}, 0);}// 渲染商品列表和总价function renderProducts() {const table = document.getElementById('productTable');const totalContainer = document.getElementById('totalPriceContainer');// 创建表头table.innerHTML = `<tr><th>编号</th><th>商品名称</th><th>单价(元)</th><th>数量</th><th>小计(元)</th></tr>`;// 添加商品行products.forEach(product => {const row = document.createElement('tr');const subtotal = product.getTotalPrice();row.innerHTML = `<td>${product.id}</td><td>${product.name}</td><td>${product.price.toFixed(2)}</td><td>${product.quantity}</td><td>${subtotal.toFixed(2)}</td>`;table.appendChild(row);});// 展示总价const totalPrice = calculateTotalPrice(products);totalContainer.innerHTML = `<div class="total-price">商品总价:${totalPrice.toFixed(2)}元</div>`;}// 页面加载完成后渲染window.onload = function() {renderProducts();};</script>
</body>
</html>
优化点说明
- 构造函数:使用ES6的
class
语法,更清晰的面向对象结构 - 命名规范:使用英文命名且遵循规范(PascalCase 用于类,camelCase 用于方法和属性)
- 方法封装:在类中添加
getTotalPrice
方法计算单个商品总价 - 计算方式:使用
reduce
方法计算总价,更符合函数式编程风格 - 展示方式:通过表格展示完整商品信息,包括单价、数量和小计
- 格式处理:使用
toFixed(2)
确保价格保留两位小数 - 用户体验:在页面中展示结果而非弹窗,提供完整的视觉信息
扩展题:对象操作进阶技巧
扩展1:对象控制台输出与方法优化
原始代码实现
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>对象控制台输出</title>
<script>
// 第一种创建对象方法:直接创建
var duiXiang1={'bianhao':1,'name':'诗书画唱' , 'age':19,'sex':'男'};// 第二种创建对象方法:使用new创建
var duiXiang2=new Object();
duiXiang2.bianhao=2;
duiXiang2.name='诗书画唱SSHC';
duiXiang2.age=19;
duiXiang2.sex='男';duiXiang2.gongneng=function(){alert(this.name+"的功能是吃饭");document.write(this.name+"的功能是吃饭");
}console.log(duiXiang1);
console.log(duiXiang2);window.onload=function() {duiXiang2.gongneng();
}
</script>
</head>
<body>
</body>
</html>
优化方案
<!DOCTYPE html>
<html>
<head><meta charset="utf-8" /><title>对象控制台输出优化</title><style>.info-box {margin: 20px;padding: 15px;border: 1px solid #ccc;border-radius: 5px;}.hint {color: #666;font-style: italic;}</style>
</head>
<body><div class="info-box"><h3>对象调试演示</h3><p>请打开浏览器控制台(F12)查看对象信息</p><p class="hint">提示:在控制台中可以展开对象,查看其属性和方法</p><div id="actionResult"></div></div><script>// 使用对象字面量创建(推荐方式)const person1 = {id: 1,name: '诗书画唱',age: 19,gender: '男',// 重写toString方法,便于日志输出toString() {return `${this.name} (${this.id}): ${this.age}岁, ${this.gender}`;}};// 使用Object.create创建对象(适合需要原型继承的场景)const person2 = Object.create(null, {id: { value: 2, writable: true },name: { value: '诗书画唱SSHC', writable: true },age: { value: 19, writable: true },gender: { value: '男', writable: true },// 吃饭方法eat: { value: function() {const resultElement = document.getElementById('actionResult');resultElement.textContent = `${this.name}正在吃饭`;// 在控制台输出详细信息console.log(`${new Date().toLocaleTimeString()}: ${this.name}执行了吃饭动作`);}}});// 控制台输出优化console.group('人员对象信息');console.log('person1 对象:', person1);console.log('person1 字符串表示:', person1.toString());console.log('person2 对象:', person2);console.dir(person2); // 更详细的对象结构展示console.groupEnd();// 页面加载后执行方法window.onload = function() {// 添加按钮触发动作,而非自动执行const button = document.createElement('button');button.textContent = '执行吃饭动作';button.onclick = () => person2.eat();document.body.appendChild(button);};</script>
</body>
</html>
扩展1.1:对象属性的添加与删除
实现代码
<!DOCTYPE html>
<html>
<head><meta charset="utf-8" /><title>对象属性操作</title><style>.container {margin: 20px;}.object-display {padding: 15px;background-color: #f8f9fa;border-radius: 5px;margin: 10px 0;white-space: pre-wrap;}button {margin: 5px;padding: 6px 12px;cursor: pointer;}</style>
</head>
<body><div class="container"><h3>对象属性添加与删除演示</h3><div><button onclick="addProperty()">添加属性</button><button onclick="deleteProperty()">删除属性</button><button onclick="modifyProperty()">修改属性</button><button onclick="checkProperty()">检查属性是否存在</button></div><h4>当前对象:</h4><div class="object-display" id="objectDisplay"></div><div id="message"></div></div><script>// 初始对象let person = {id: 1,name: '诗书画唱',age: 19,gender: '男'};// 显示对象当前状态function displayObject() {const displayElement = document.getElementById('objectDisplay');// 格式化输出,便于阅读displayElement.textContent = JSON.stringify(person, null, 2);}// 添加属性function addProperty() {// 检查属性是否已存在if (!person.address) {person.address = '北京市';showMessage('已添加address属性');} else if (!person.hobby) {person.hobby = '阅读';showMessage('已添加hobby属性');} else {showMessage('所有可选属性已添加');}displayObject();}// 删除属性function deleteProperty() {// 删除属性的两种方式if (person.age) {delete person.age;showMessage('已删除age属性');} else if (person.gender) {delete person['gender'];showMessage('已删除gender属性');} else {showMessage('没有可删除的属性');}displayObject();}// 修改属性function modifyProperty() {person.age = person.age ? person.age + 1 : 20;showMessage(`已更新age属性为: ${person.age}`);displayObject();}// 检查属性是否存在function checkProperty() {const hasId = 'id' in person;const hasAddress = 'address' in person;showMessage(`id属性存在: ${hasId}, address属性存在: ${hasAddress}`);}// 显示操作消息function showMessage(text) {const messageElement = document.getElementById('message');messageElement.textContent = text;}// 初始化显示window.onload = function() {displayObject();};</script>
</body>
</html>
扩展1.2:JavaScript数组遍历方法详解
实现代码
<!DOCTYPE html>
<html>
<head><meta charset="utf-8" /><title>数组遍历方法详解</title><style>.container {margin: 20px;max-width: 800px;}.method-section {margin-bottom: 30px;padding: 15px;border: 1px solid #eee;border-radius: 5px;}.result {background-color: #f8f9fa;padding: 10px;margin-top: 10px;border-radius: 4px;white-space: pre-wrap;}button {padding: 6px 12px;cursor: pointer;}</style>
</head>
<body><div class="container"><h2>JavaScript数组遍历方法对比</h2><div class="method-section"><h3>1. for循环遍历</h3><button onclick="traverseWithFor()">执行for循环遍历</button><div class="result" id="forResult"></div></div><div class="method-section"><h3>2. for...of遍历</h3><button onclick="traverseWithForOf()">执行for...of遍历</button><div class="result" id="forOfResult"></div></div><div class="method-section"><h3>3. forEach遍历</h3><button onclick="traverseWithForEach()">执行forEach遍历</button><div class="result" id="forEachResult"></div></div><div class="method-section"><h3>4. map遍历(返回新数组)</h3><button onclick="traverseWithMap()">执行map遍历</button><div class="result" id="mapResult"></div></div><div class="method-section"><h3>5. filter遍历(筛选元素)</h3><button onclick="traverseWithFilter()">执行filter遍历</button><div class="result" id="filterResult"></div></div></div><script>// 人员数据数组const persons = [{ id: 1, name: '诗书画唱1', age: 19 },{ id: 2, name: '诗书画唱2', age: 22 },{ id: 3, name: '诗书画唱3', age: 25 }];// 1. for循环遍历function traverseWithFor() {const result = [];for (let i = 0; i < persons.length; i++) {result.push(`[${i}] ${persons[i].name}, ${persons[i].age}岁`);}document.getElementById('forResult').textContent = result.join('\n');}// 2. for...of遍历function traverseWithForOf() {const result = [];for (const person of persons) {result.push(`${person.name}, ${person.age}岁`);}document.getElementById('forOfResult').textContent = result.join('\n');}// 3. forEach遍历function traverseWithForEach() {const result = [];persons.forEach((person, index) => {result.push(`[${index}] ${person.name}, ${person.age}岁`);});document.getElementById('forEachResult').textContent = result.join('\n');}// 4. map遍历(返回新数组)function traverseWithMap() {// map返回一个新数组,包含转换后的值const mapped = persons.map(person => {return {fullInfo: `${person.name} (ID: ${person.id})`,ageGroup: person.age < 20 ? '青年' : '成年'};});document.getElementById('mapResult').textContent = JSON.stringify(mapped, null, 2);}// 5. filter遍历(筛选元素)function traverseWithFilter() {// filter返回符合条件的元素组成的新数组const adults = persons.filter(person => person.age >= 20);document.getElementById('filterResult').textContent = JSON.stringify(adults, null, 2);}</script>
</body>
</html>
总结与进阶建议
通过对以上案例的解析与优化,我们可以总结出JavaScript对象与数组操作的最佳实践:
- 命名规范:使用有意义的英文名称,遵循PascalCase(类)和camelCase(方法、属性)命名法
- 变量声明:优先使用
const
和let
,避免使用var
,明确变量的可变性 - 对象创建:
- 简单对象推荐使用对象字面量
{}
- 复杂对象或需要多次实例化时使用
class
- 避免使用
new Object()
创建对象
- 简单对象推荐使用对象字面量
- 数组遍历:
- 简单遍历推荐使用
forEach
- 需要返回新数组时使用
map
- 需要筛选元素时使用
filter
- 需要计算累加结果时使用
reduce
- 简单遍历推荐使用
- DOM操作:避免使用
document.write()
,通过createElement
和textContent
等方法操作DOM - 代码组织:将功能封装为函数,提高复用性和可维护性
进阶学习建议
- 学习ES6+更多特性:箭头函数、解构赋值、展开运算符等
- 掌握数组高级方法:
find()
,some()
,every()
,sort()
等 - 学习对象的原型与继承机制
- 了解Immutable数据处理理念,避免直接修改对象
- 学习TypeScript,为对象和数组添加类型定义,提高代码健壮性
通过不断实践这些技巧,你将能够写出更规范、高效、可维护的JavaScript代码,为构建复杂前端应用打下坚实基础。