【JS基础-Day5】对象与内置对象

2 阅读4分钟

【JS基础-Day5】对象与内置对象

📺 对应视频:P64-P77 | 🎯 核心目标:掌握对象的CRUD操作、遍历对象、Math内置对象及引用类型原理


一、对象基础

1.1 什么是对象?

对象是键值对的集合,用来描述一个有多个属性的事物。

// 现实类比:
// 一个人 → 对象
// 姓名、年龄、性别 → 属性
// 走路、说话、吃饭 → 方法(函数作为属性值)

let person = {
  name: '张三',      // 属性(键: 值)
  age: 18,
  gender: '男',
  greet: function() {  // 方法
    console.log(`我叫${this.name}`)
  }
}

1.2 创建对象

// 方式一:对象字面量(最常用)
let obj = {
  key1: value1,
  key2: value2
}

// 方式二:new Object()
let obj2 = new Object()
obj2.name = '张三'

// 方式三:构造函数(进阶,Day12详解)
function Person(name, age) {
  this.name = name
  this.age = age
}
let p = new Person('张三', 18)

二、对象的 CRUD

2.1 读取属性(查)

let person = { name: '张三', age: 18, 'my-job': '工程师' }

// 点语法(常用)
console.log(person.name)   // '张三'
console.log(person.age)    // 18

// 方括号语法(属性名含特殊字符、或属性名是变量时必须用)
console.log(person['name'])       // '张三'
console.log(person['my-job'])     // '工程师'(含连字符的属性名)

let key = 'age'
console.log(person[key])    // 18(动态属性名)
console.log(person.key)     // undefined(把 key 当属性名了)

2.2 修改属性(改)

let person = { name: '张三', age: 18 }

person.name = '李四'   // 修改已有属性
person['age'] = 20     // 方括号语法修改

2.3 增加属性(增)

let person = { name: '张三' }

person.gender = '男'    // 直接赋值新属性
person['hobby'] = '打篮球'

// 结果:{ name: '张三', gender: '男', hobby: '打篮球' }

2.4 删除属性(删)

let person = { name: '张三', age: 18, gender: '男' }

delete person.gender    // 删除属性
console.log(person)    // { name: '张三', age: 18 }

三、遍历对象

3.1 for...in 遍历

let person = { name: '张三', age: 18, gender: '男' }

for (let key in person) {
  console.log(key)           // 属性名:name, age, gender
  console.log(person[key])   // 属性值:张三, 18, 男
}

⚠️ for...in 会遍历原型链上的可枚举属性,通常配合 hasOwnProperty 过滤

3.2 Object 方法遍历(推荐)

let person = { name: '张三', age: 18, gender: '男' }

// 获取所有键
Object.keys(person)    // ['name', 'age', 'gender']

// 获取所有值
Object.values(person)  // ['张三', 18, '男']

// 获取所有键值对
Object.entries(person) // [['name','张三'], ['age',18], ['gender','男']]

// 遍历示例
Object.keys(person).forEach(key => {
  console.log(`${key}: ${person[key]}`)
})

四、对象方法

4.1 对象中的方法

let calculator = {
  result: 0,
  add(num) {           // ES6 简写方法语法
    this.result += num
    return this        // 返回 this 支持链式调用
  },
  subtract(num) {
    this.result -= num
    return this
  },
  getResult() {
    return this.result
  }
}

// 链式调用
calculator.add(10).add(5).subtract(3).getResult()  // 12

4.2 this 关键字(初步理解)

let person = {
  name: '张三',
  greet() {
    console.log(this.name)  // this 指向调用该方法的对象
  }
}
person.greet()  // '张三'(this = person)

// 注意:this 指向在运行时确定,不是在定义时
const fn = person.greet
fn()  // undefined(严格模式)或全局对象(this 丢失!)

五、内置对象 Math

5.1 Math 常用属性

Math.PI      // 3.141592653589793(圆周率)
Math.E       // 2.718281828459045(自然常数)
Math.LN2     // 0.6931471805599453
Math.SQRT2   // 1.4142135623730951

5.2 Math 常用方法

// 取整
Math.floor(4.9)   // 4  向下取整(地板)
Math.ceil(4.1)    // 5  向上取整(天花板)
Math.round(4.5)   // 5  四舍五入
Math.trunc(4.9)   // 4  去掉小数部分(ES6)
Math.trunc(-4.9)  // -4(注意与 floor 的区别,floor(-4.9) = -5)

// 绝对值
Math.abs(-10)    // 10
Math.abs(10)     // 10

// 最大最小值
Math.max(1, 2, 3)         // 3
Math.min(1, 2, 3)         // 1
Math.max(...[1, 5, 3])    // 5(展开数组)

// 幂运算和开方
Math.pow(2, 10)   // 1024(2的10次方)
Math.sqrt(16)     // 4(平方根)
Math.cbrt(27)     // 3(立方根)

// 对数
Math.log(Math.E)  // 1(自然对数)
Math.log2(8)      // 3
Math.log10(1000)  // 3

5.3 Math.random() 随机数

// Math.random() 返回 [0, 1) 之间的浮点数
Math.random()    // 0.7234...(每次不同)

// 生成 [min, max) 的整数
function getRandomInt(min, max) {
  return Math.floor(Math.random() * (max - min)) + min
}
getRandomInt(1, 10)   // 1-9 之间的整数

// 生成 [min, max] 的整数(包含 maxfunction getRandomIntInclusive(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min
}
getRandomIntInclusive(1, 6)  // 模拟骰子:1-6

// 数组随机取一个元素
function randomItem(arr) {
  return arr[Math.floor(Math.random() * arr.length)]
}
randomItem(['石头', '剪刀', '布'])  // 随机一个

六、引用类型 vs 值类型

6.1 根本区别

// 值类型(基本类型):存储值本身,赋值是复制
let a = 10
let b = a   // b 是 a 的副本
b = 20
console.log(a)  // 10(a 不受影响)

// 引用类型:存储的是内存地址,赋值是复制地址
let obj1 = { name: '张三' }
let obj2 = obj1   // obj2 指向同一个对象
obj2.name = '李四'
console.log(obj1.name)  // '李四'(obj1 也变了!)

内存示意图:

值类型:
  a[10]
  b[20](独立的副本)

引用类型:
  obj1 → 地址0x001 → { name: '李四' }
  obj2 → 地址0x001 ↗(指向同一个对象)

6.2 函数传参的影响

// 值类型传参:传的是副本,不影响原始变量
function increment(n) {
  n++
}
let num = 10
increment(num)
console.log(num)  // 10(未被修改)

// 引用类型传参:传的是地址,会修改原始对象
function addProp(obj) {
  obj.job = '工程师'
}
let person = { name: '张三' }
addProp(person)
console.log(person)  // { name: '张三', job: '工程师' }(被修改了!)

七、其他常用内置对象预览

// Date 对象(后面Web APIs详讲)
let now = new Date()
now.getFullYear()  // 年
now.getMonth()     // 月(0-11,注意+1)
now.getDate()      // 日
now.getDay()       // 星期(0-6,0=周日)
now.getHours()     // 时
now.getMinutes()   // 分

// JSON(数据交换格式)
JSON.stringify({ name: '张三', age: 18 })  // '{"name":"张三","age":18}'
JSON.parse('{"name":"张三","age":18}')     // { name: '张三', age: 18 }

八、综合练习

练习1:学生成绩统计

let students = [
  { name: '张三', score: 85 },
  { name: '李四', score: 92 },
  { name: '王五', score: 78 }
]

// 计算平均分
let avg = students.reduce((sum, s) => sum + s.score, 0) / students.length

// 找最高分学生
let best = students.reduce((a, b) => a.score > b.score ? a : b)

// 所有人成绩+5分
students.forEach(s => s.score = Math.min(s.score + 5, 100))

练习2:随机生成验证码

function generateCode(length = 6) {
  const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
  let code = ''
  for (let i = 0; i < length; i++) {
    code += chars[Math.floor(Math.random() * chars.length)]
  }
  return code
}
console.log(generateCode())  // 如:'aB3kLm'

九、知识图谱

对象与内置对象
├── 对象
│   ├── 创建:字面量 {} / new Object()
│   ├── 访问:点语法 / 方括号语法
│   ├── 增删改查(CRUD)
│   ├── 遍历:for...in / Object.keys/values/entries
│   ├── 方法(函数作为属性)
│   └── this(指向调用对象,运行时确定)
├── Math 内置对象
│   ├── 取整:floor / ceil / round / trunc
│   ├── 其他:abs / max / min / pow / sqrt
│   └── 随机数:random(),配合 floor 取整范围
└── 引用类型
    ├── 赋值复制的是地址(不是值)
    ├── 多个变量可能指向同一对象
    └── 函数传参也是传地址,会修改原对象

十、高频面试题

Q1:如何判断对象上是否有某个属性?

let obj = { name: '张三', age: 18 }

'name' in obj              // true(包含原型链)
obj.hasOwnProperty('name') // true(只检查自身属性)
obj.name !== undefined      // true(但如果值本身是 undefined 就失效)
Object.hasOwn(obj, 'name') // true(ES2022,推荐)

Q2:浅拷贝和深拷贝?

// 浅拷贝:只复制一层,嵌套对象仍然共享引用
let a = { x: 1, y: { z: 2 } }
let b = { ...a }             // 展开运算符(浅拷贝)
let c = Object.assign({}, a) // Object.assign(浅拷贝)
b.x = 10   // 不影响 a.x
b.y.z = 99 // 影响 a.y.z!(共享引用)

// 深拷贝:完全独立的副本
let d = JSON.parse(JSON.stringify(a))   // 简单方法(有限制)
let e = structuredClone(a)              // 现代方法(ES2022)

Q3: null undefined 的区别?

null 是一个表示"空对象"的特殊值,通常是程序员主动赋予的;undefined 是变量未赋值时的默认状态。typeof null'object'(历史 bug),typeof undefined'undefined'


⬅️ 上一篇Day4 - 函数与作用域 ➡️ 下一篇Web APIs Day1 - DOM操作基础