Skip to content
On this page

关于 JS 的对象

对象属性的添加与删除

js
let obj = {}
obj.name = 'hello' // 添加 name 属性
delete obj.name // 删除 name 属性
console.log(obj.hasOwnProperty('name')) // false

对象和原型链属性检测

hasOwnProperty 方法只能检测实例对象是否有目标属性;in 操作符既能检测实例对象是否有目标属性,也能检测实例对象的原型上是否有目标属性。 setPrototypeOf(a, b) 将 b 赋值给 a 的隐式原型(有点认贼作父的意思),即 a.__proto__ = bJavaScript原型与继承的秘密 - 知乎 (zhihu.com)

计算属性与 assign 的使用

js
let obj = {}
let title = '语文'
obj[`${'category' + title}`] = { id: 10001, title: '语文'}
// 上面的属性赋值参与计算(计算属性)

// ******************** //
const courses = [
	{ title: '语文', id: 10001 },
	{ title: '数学', id: 10002 },
	{ title: '英语', id: 10003 },
]
// 需求:转成如下结构
let res = {
	'10001': { title: '语文', id: 10001 },
	'10002': { title: '数学', id: 10002 },
	'10003': { title: '英语', id: 10003 },
}
// anwser
let result = courses.reduce((obj, cur, index) => {
	obj[cur.id] = cur
	return cur
}, {})
// ******************** //

// assign 的使用,前者和后者如果有相同的属性名,后者覆盖前者
let cat = Object.assign({name: 'catty'}, {age: 2})
console.log(cat) // {name: 'catty', age: 2}

** for of 可以用来操作可迭代对象

js
const obj = { name: 'hello', age: 18 }
for(const [key, value] of Object.entries(obj)) {
	console.log(key value)
}
// name hello
// age 18

对象浅拷贝的几种方法

js
// 1
let english = { name: 'EN', id: 10001 }
let obj = Object.assign({}, english)
// assign 不能深层次拷贝

// 2
for(const key in english) {
 obj[key] = english[key]
}

// 3. separate operator
obj = {...english}

对象属性描述符 (ES8/ES2017)

使用 Object.getOwnPropertyDescriptor(obj, 'name')Object.getOwnPropertyDescriptors(obj) 查看是否可写、可遍历、可配置(writable/enumerable/configurable)。

js
const obj = {
	name: 'es',
}
const desc = Object.getOwnPropertyDescriptors(obj)
console.log(desc)
/*
{
    "name": {
        "value": "es",
        "writable": true,
        "enumerable": true,
        "configurable": true
    }
}
*/
const r = Object.getOwnPropertyDescriptor(obj, 'name')
console.log(r)
/*
{
    "value": "es",
    "writable": true,
    "enumerable": true,
    "configurable": true
}
*/

// 如何给对象设置属性
const obj1 = {}
Reflect.defineProperty(obj1, 'name', {
	value: 'ng',
	writable: false,
	enumerable: false,
    configurable: false
})
obj1.name = 'lisi' // 修改无效
Object.keys(obj1) // []
for(let key in obj1) {console.log(key)} // 空
delete obj1.name // false
const desc1 = Object.getOwnPropertyDescriptors(obj1)
console.log(desc1)
/*
{
    "name": {
        "value": "ng",
        "writable": false,
        "enumerable": true,
        "configurable": false
    }
}
*/

控制对象属性特征

js
const user = { age:18 }
Object.defineProperty(user, 'name', {
	value: 'hello',
	writable: false, // 对象属性是否可修改, flase 为不可修改,默认值为true
	enumerable: false, // 对象属性是否可通过 for-in 循环,flase 为不可循环,默认值为 true
	configurable: false, // 能否使用 delete、能否需改属性特性、或能否修改访问器属性,false 为不可重新定义,默认值为true
})
user.name = 'world' //其实是可以更改的,需要在严格模式下使用('use strict')
console.log(Object.keys(user)) // ["age"] ,即使使用 for in 也无法获取 name 
delete user.name
console.log(user.name) // hello

// 另一种写法
Object.defineProperties(user, {
	name: {
		value: 'hello',
		writable: false,
		enumerable: false,
		configurable: true,
	},
	age: {
		value: 21,
		writable: false,
		enumerable: false,
		configurable: true,
	}
})

不允许向对象添加属性

js
'use strict' // 此处如果删除,下面的代码不会报错,不影响功能运行
const obj = {age: 18}
Object.preventExtensions(obj)
obj.name = 'world' //TypeError: Cannot add property name

if(Object.isExtensible(obj)) {
	obj.name = 'hello'
}

封闭对象的 API 操作

js
"use strict" // 最好保留
const user = {name: 'hello'}
Object.seal(user) // 不可添加、修改、删除属性特征,注意是特征
console.log(Object.isSealed(user))
console.log(JSON.stringify(Object.getOwnPropertyDescriptors(user), null, 2))

// 打印结果如下
true
{
  "name": {
    "value": "hello",
    "writable": true,
    "enumerable": true,
    "configurable": false
  }
}

对属性控制更严格的 API

js
"use strict" // 最好保留
const user = {name: 'hello'}
Object.freeze(user) // 不可写,也不可重新定义属性特征
console.log(Object.isFrozen(user))
console.log(JSON.stringify(Object.getOwnPropertyDescriptors(user), null, 2))

// 打印结果如下
true
{
  "name": {
    "value": "hello",
    "writable": false, // 不可写
    "enumerable": true,
    "configurable": false
  }
}

利用访问器保护数据

js
"use strict" // 最好保留
const user = {
	data: {age: 18},
	set age(v) {
		if(typeof v != 'number' || v < 1 || v > 120) {
			throw new Error("invalid age")
		}
		this.data.age = v
	},
	get age() {
		return this.data.age
	}
}

token 读取的小技巧

js
const Request = {
	set token(content){
		localStorage.setItem('token', content)
	},
	get token() {
		let token = localStorage.getItem('token')
		if(!token) {
			//
		}
		return token
	}
}
Request.token = 'asdfghjklrfvqa'

访问器的优先级

同名属性里,设置访问器的属性优先级最高。这句话是错的,写在后面的属性优先级高。

自定义 toJSON

js
const user = {
	name: 'he',
	toJSON: function() {
		return {
			title: '~~' + this.name
		}
	}
}
console.log(JSON.stringify(user, null, 2))

JSON 转为 JS 可操作对象

js
let obj = JSON.parse(json, (key, value) => {
	if(key === 'name') {
		value = 'world'
	}
	return value
})

对象的扩展

js
console.log(Object.is(2, '2')) // false
console.log(Object.is(NaN, NaN)) // true
console.log(Object.is(+0, -0)) // false
console.log(+0 === -0) // true

let obj1 = {}
let obj2 = obj1
console.log(Object.is(obj1, obj2)) // true
  • 扩展运算符和 Object.assign()
js
let obj = { name: 'he' }
let y = {...obj} // { name: 'he' }
let z = { age: 18, name: 'llo' }
Object.assign(z, obj) // z: { name: 'he', age: 18 }

Object.assign() 拷贝(浅拷贝)对象时也有问题:

js
let target = {
	parent: 'fa',
	child: {
		name: 'world',
		child: {
			name: 'he'
		},
		weight: 120,
		eye: 2
	}
}
let source = {
	child: {
		name: 'he',
		child: {
			name: 'llo'
		},
		weight: 120,
	}
}
console.log(Object.assign(target, source)) // 少了 eye 属性
  • in 操作符
js
let obj = { name: 'he' }
console.log('name' in obj) // true
let arr = [1,2,3]
console.log(3 in arr) // false 判断当前索引的位置是否有值
console.log(2 in arr) // true
  • 对 对象的遍历
js
let obj = {name: 'he'}
for(let key in obj) {
	console.log(obj[key])
}
Object.keys(obj).forEach(key => {
	console.log(key)
})
Object.getOwnPropertyNames(obj).forEach(key => {
	console.log(key)
})
Reflect.ownKeys(obj).forEach(key => {
	console.log(key)
})
  • Object.fromEntries() (ES 10 / ES 2019)
js
const obj = {
	name: 'jelly',
	age: 28
}
const entries = Object.entries(obj)
console.log(entries) // [['name', 'jelly'], ['age', 28]]

let fromEntries = Object.fromEntries(entries)
console.log(fromEntries) // {name: 'jelly', age: 28}

// 可以把 Map 结构转成 普通对象
const m = new Map()
m.set('name', 'heilen')
m.set('age', 21)
fromEntries = Object.fromEntries(m)
console.log(fromEntries) // {name: 'heilen', age: 21}

// 场景: 从对象提取 value 大于目标值的结果对象
const course = {
	math: 80,
	english: 88,
	chinese: 90
}
const res = Object.entries(course).filter(([key, val]) => {
	return val > 85
})
console.log(res) // [['english', 88], ['chinese', 90]]
const res1 = Object.fromEntries(res)
console.log(res1) // {english: 88, chinese: 90}

Object.values() (ES8/ES2017)

js
const obj = {
	name: 'llo',
	age: 18
}
const res = Object.keys(obj)map(key => obj[key])
console.log(res)
const res1 = Object.values(obj)
console.log(res1)

Object.entries() (ES8/ES2017)

js
const obj = {
	name: 'llo',
	age: 18
}
console.log(Object.entries(obj))
for(let [key, val] of Object.entries(obj)) {
	console.log(key, val)
}
console.log(Object.entries([1, 2])) // [['0', 1], ['1', 2]]

Rest & Spread

js
// 数组的展开扩展运算符
const arr1 = [1]
const arr2 = [2, 3]
console.log([...arr1, ...arr2]) // [1, 2, 3]
// 对象的展开扩展运算符
let obj1 = {
	name: 'llo',
	child: {
		name: 'lisi'
	}
}
let obj2 = {
	age: 18
}
let obj3 = {...obj1, ...obj2} // 合并对象
console.log(obj3)
// 对象的展开扩展运算符 是浅拷贝
obj1.child.name = 'zhangsan'
console.log(obj3)

// 对象 Rest
const course = {
	className: 'english',
	studentsCount: 28,
	school: 'shell'
}
const { className, ...rest } = course
console.log(className, rest) // english {studentsCount: 28, school: 'shell'}

Released under the MIT License.