JavaScript对象指南:从新手到轻松掌握

126 阅读4分钟

JavaScript对象指南:从新手到轻松掌握

前言

你好,我是刚入门的前端萌新,正在学习JavaScript。今天我想和大家分享我学习JavaScript对象的心得体会,希望能帮助到同样在学习路上的小伙伴们。


一、初识对象:万物皆对象的JavaScript世界

在JavaScript中,万物皆对象。这句话可能有点夸张,但确实说明了对象在JS中的重要地位。

1.1 两种主要类型
// 1. 原始类型(基本类型)
const str = 'hello'      // string
const num = 123          // number
const bool = true        // boolean
const und = undefined    // undefined
const nul = null         // null
const sym = Symbol('id') // Symbol
const big = 123n         // bigInt

// 2. 引用类型(本质上都是对象)
const func = function() {}   // function(特殊对象)
const arr = [1, 2, 3]        // array(特殊对象)
const obj = { name: '玉米' }  // object

二、创建对象的三种方式

想创建一个对象?JavaScript给你准备了三种方法,从简单到复杂,总有一款适合你。

2.1 对象字面量(最常用)
// 直接使用大括号创建
const person = {
  name: '玉米',
  age: 18,
  sayHello: function() {
    console.log(`大家好,我是${this.name}`)
  }
}
console.log(person.name) // 玉米
person.sayHello() // 大家好,我是玉米
2.2 new Object()构造函数
const obj = new Object()  // 创建一个空对象
obj.name = '玉米'
const abc = 'age'
obj[abc] = 18  // 使用变量作为属性名
obj['job'] = '前端开发'  // 使用字符串作为属性名

console.log(obj) // { name: '玉米', age: 18, job: '前端开发' }

// 动态操作属性
delete obj[abc]  // 删除age属性
console.log(obj) // { name: '玉米', job: '前端开发' }
2.3 自定义构造函数(批量创建)

看到这里你可能觉得:这两种方法都挺简单的嘛,一个个创建不就完了?别急,当你需要创建的对象数量从几个变成几十个、几百个时,问题就来了。

三、构造函数:批量生产对象的工厂

3.1 为什么需要构造函数?

让我给你讲个真实的故事。上个月我实习时,老板让我处理公司员工数据。一开始我觉得很简单:

// 我的第一版“解决方案”
function createEmployee(name, age, position) {
  const emp = {
    name: name,
    age: age,
    position: position,
    company: '掘金科技',
    joinDate: '2024-01-15'
  }
  return emp
}

// 创建几个员工
const emp1 = createEmployee('张三', 25, '前端开发')
const emp2 = createEmployee('李四', 28, '后端开发')
const emp3 = createEmployee('王五', 30, '产品经理')

// 然后一个个处理
console.log(emp1.name, emp1.position)
console.log(emp2.name, emp2.position)
console.log(emp3.name, emp3.position)
// ... 还有97个要写!

第一天我还干劲十足,到第三天我就崩溃了。想象一下:你要手动创建100个员工对象,每个都要单独处理,代码写到手软,debug调到眼花。

这时候我导师走过来看了一眼,说了句:“你这是在手工作坊里搞工业生产啊!”

3.2 “自动化工厂”拯救生产力

导师给我展示了真正的“工业级”解决方案——构造函数

// 这就是我们的“自动化工厂”
function Employee(name, age, position) {
  // new操作符在这里扮演“自动化流水线”的角色
  this.name = name
  this.age = age
  this.position = position
  this.company = '掘金科技'
  this.employeeId = 'JUEJIN' + Date.now() + Math.random().toString(36).substr(2, 9)
  
  this.introduce = function() {
    return `我是${this.name}${this.position},工号${this.employeeId}`
  }
  
  this.work = function(task) {
    return `${this.name}正在处理:${task}`
  }
}

// 现在创建100个员工?小菜一碟!
const employeeList = []
const positions = ['前端开发', '后端开发', 'UI设计', '产品经理', '测试工程师']

for (let i = 0; i < 100; i++) {
  const randomPosition = positions[Math.floor(Math.random() * positions.length)]
  const age = 22 + Math.floor(Math.random() * 15)
  
  employeeList.push(
    new Employee(`员工${i + 1}`, age, randomPosition)
  )
}

// 批量操作才是真正的快乐!
console.log(`=== 掘金科技员工系统 ===`)
console.log(`员工总数:${employeeList.length}人`)

// 统计各部门人数
const deptStats = {}
employeeList.forEach(emp => {
  deptStats[emp.position] = (deptStats[emp.position] || 0) + 1
})

console.log('各部门人数统计:')
for (const [dept, count] of Object.entries(deptStats)) {
  console.log(`${dept}: ${count}人`)
}

// 找出所有前端开发人员
const frontendDevs = employeeList.filter(emp => emp.position === '前端开发')
console.log(`\n前端开发团队(${frontendDevs.length}人):`)
frontendDevs.slice(0, 5).forEach(emp => {
  console.log(`  ${emp.introduce()}`)
})

从“手工小作坊”到“自动化工厂”,我的生产力直接提升了10倍!原来要写几天的代码,现在几分钟就搞定了。

3.3 构造函数和普通函数的区别

当一个函数被 new 调用时,它就成为了构造函数:

function Car(color) {
  this.name = 'su7'
  this.height = '1400'
  this.color = color
}

// 使用new:构造函数模式
const car1 = new Car('purple')
console.log(car1) // Car { name: 'su7', height: '1400', color: 'purple' }

// 不使用new:普通函数调用
const car2 = Car('purple')
console.log(car2) // undefined(因为没有返回值)
console.log(window.name) // 'su7'(this指向了window)

四、new关键字的神秘面纱

4.1 new到底做了什么?
// new关键字背后的秘密
function Person(name) {
  // 1. new会创建一个空对象,并让this指向它
  // const this = {}
  
  // 2. 执行构造函数代码
  this.name = name
  
  // 3. 返回this对象
  // return this
}

// 手动模拟new的过程
function person(name) {
  const _this = {}      // 第1步:创建空对象
  _this.name = name     // 第2步:添加属性
  return _this          // 第3步:返回对象
}

const obj = person('玉米')
console.log(obj) // { name: '玉米' }
4.2 更复杂的模拟
function myNew(constructor, ...args) {
  // 1. 创建一个空对象,并继承构造函数的原型
  const obj = Object.create(constructor.prototype)
  
  // 2. 执行构造函数,绑定this到新对象
  const result = constructor.apply(obj, args)
  
  // 3. 如果构造函数返回对象,则返回该对象,否则返回新对象
  return typeof result === 'object' && result !== null ? result : obj
}

// 测试
function Student(name, grade) {
  this.name = name
  this.grade = grade
}

const stu = myNew(Student, '小明', '一年级')
console.log(stu) // Student { name: '小明', grade: '一年级' }

五、包装类:原始类型的"临时对象外套"

5.1 什么是包装类?

学习过程中我还遇到一些有趣的细节,比如包装类这个“魔术师”:

// 看起来是普通的字符串
const greeting = 'hello world'

// 但它居然有“方法”!
console.log(greeting.toUpperCase())  // HELLO WORLD
console.log(greeting.length)         // 11

// 其实JavaScript在背后偷偷做了这些:
// 1. 临时创建一个String对象:new String('hello world')
// 2. 调用这个对象的方法
// 3. 用完就扔掉这个临时对象

// 所以下面这种情况就会出现
greeting.customProp = 'test'  // 临时对象被添加属性
console.log(greeting.customProp)  // undefined - 临时对象已经消失了

这种“临时工”机制,虽然有时让人困惑,但也体现了JavaScript的灵活性。

5.2 包装类的陷阱
// 错误示范:忘记写new
function Person(name) {
  this.name = name
}

const p1 = Person('小明')  // 没有new,this指向全局!
console.log(p1)  // undefined
console.log(window.name)  // 小明(浏览器环境)

// 正确做法:要么记得new,要么用严格模式
function Person(name) {
  'use strict'
  this.name = name
}

// 现在如果忘记new,就会直接报错
5.3 我的最佳实践
// 1. 构造函数命名用大驼峰
function Employee(config) {
  // 2. 支持传入配置对象
  this.name = config.name
  this.age = config.age
  // 3. 设置默认值
  this.status = config.status || '在职'
}

// 4. 批量创建时使用数组方法
const employees = Array.from({length: 50}, (_, i) => {
  return new Employee({
    name: `员工${i + 1}`,
    age: 22 + (i % 10),
    status: i % 20 === 0 ? '离职' : '在职'
  })
})

最后

学习JavaScript对象时,我最大的体会是:多写代码,多尝试。刚开始我也被构造函数、包装类这些概念搞得头晕,但通过实际写代码、调试、看结果,慢慢就理解了。

希望这篇文章对你有帮助!如果有任何问题或建议,欢迎在评论区交流。我们一起进步!