第三节:对象探秘——从创建到转换的完整指南 1. 对象创建的三板斧在JavaScript中创建一个对象就像搭积木一样有几种不同的玩法。先说说最接地气的字面量创建法这就像直接手写购物清单一样简单const person { name: 小明, age: 18, sayHi() { console.log(大家好我是${this.name}) } }这种写法简单直观但有个小缺点——每次创建相似对象都要重复写属性。这时候可以用构造函数模式就像用模具批量生产function Person(name, age) { this.name name this.age age this.sayHi function() { console.log(大家好我是${this.name}) } } const p1 new Person(小明, 18) const p2 new Person(小红, 17)不过这种写法每个实例都会创建新方法有点浪费内存。于是就有了原型链模式把公共方法挂在原型上function Person(name, age) { this.name name this.age age } Person.prototype.sayHi function() { console.log(大家好我是${this.name}) }ES6之后又有了更优雅的class语法糖底层其实还是原型链class Person { constructor(name, age) { this.name name this.age age } sayHi() { console.log(大家好我是${this.name}) } }1.1 冷门但强大的Object.create有个容易被忽略的创建方式——Object.create它能精确控制原型链const proto { greet() { console.log(Hello!) } } const obj Object.create(proto, { name: { value: 小明, writable: true } })这种方式特别适合需要精细控制对象属性的场景比如实现继承function createAnimal(name) { const animal Object.create(Animal.prototype) animal.name name return animal }2. 类型转换的暗箱操作当对象遇到需要原始值的场景JavaScript会偷偷调用两个方法valueOf和toString。这个转换顺序很多人会搞混记住这个口诀要数字先valueOf要字符串先toString。const obj { value: 42, valueOf() { return this.value }, toString() { return [Object ${this.value}] } } console.log(obj 1) // 43调用valueOf console.log(${obj}) // [Object 42]调用toString2.1 那些年踩过的坑有个经典面试题Boolean(new Boolean(false))为什么是true因为new Boolean(false)创建的是对象不是原始值对象转布尔值永远为true不管对象本身代表什么console.log(typeof new Boolean(false)) // object console.log(Boolean({})) // true另一个坑是数组转字符串console.log([1,2,3] [4,5]) // 1,2,34,5 // 相当于 // [1,2,3].toString() [4,5].toString() // 1,2,3 4,53. 实战中的类型转换技巧3.1 数据格式化处理API响应时经常需要格式化数据function formatUser(user) { // 安全转换避免undefined return { id: user.id || 0, name: String(user.name || ), isVIP: Boolean(user.vip) } }3.2 宽松比较的陷阱比较会触发隐式转换容易踩坑console.log([] ![]) // true // 解析 // ![] → false // [] false → [] → → 0 // false → 0 // 所以 0 0建议用严格比较除非你很清楚转换规则。4. 特殊对象的处理4.1 Date对象的转换Date对象转换很特殊直接toString和valueOf返回不同结果const now new Date() console.log(now.valueOf()) // 时间戳数字 console.log(now.toString()) // 可读时间字符串 console.log(now 1) // 先调用toString()4.2 自定义转换行为用Symbol.toPrimitive可以完全控制转换逻辑const exam { score: 90, [Symbol.toPrimitive](hint) { if (hint number) return this.score if (hint string) return 分数:${this.score} return this.score 60 ? 及格 : 不及格 } } console.log(exam) // 90 console.log(${exam}) // 分数:90 console.log(exam !) // 及格!5. 原型链的转换影响原型方法也会参与类型转换比如Number.prototype.toString function() { return 数字${this} } console.log((123).toString()) // 数字123甚至可以玩点花的Object.prototype.valueOf function() { return 42 } console.log({} 1) // 43不过在实际项目中修改内置原型是危险操作容易引发难以追踪的bug。