共计 11566 个字符,预计需要花费 29 分钟才能阅读完成。
关注前端小讴,浏览更多原创技术文章
了解对象
- ECMAScript 定义对象:无序属性的汇合(一组没有特定程序的值),其属性能够蕴含 根本值 、 对象 、 函数 ,整个对象能够设想成一个 散列表
相干代码 →
-
创立自定义对象 的 2 种办法:Object 构造函数 和对象字面量:
- 用构造函数创立一个 Object 实例,而后为它增加属性和办法
var person = new Object() person.name = 'Nicholas' person.age = 29 person.job = 'Software Engineer' person.sayName = function () {console.log(this.name) }
- 对象字面量创建对象
var person = { | |
name: 'Nicholas', | |
age: 29, | |
job: 'Software Engineer', | |
sayName: function () {console.log(this.name) | |
}, | |
} |
属性的类型
- 为 JavaScript 实现引擎的标准定义,开发者 不能间接拜访 ,用 两个中括号 把个性名称括起来
数据属性
数据属性 | 含意 | 默认值 |
---|---|---|
[[Configurable]] |
是否配置(delete 删除、批改个性、改为拜访器属性) | 在对象上定义:true / Object.defineProperty()定义:false |
[[Enumerable]] |
是否通过 for-in 循环返回属性 | 在对象上定义:true / Object.defineProperty()定义:false |
[[Writable]] |
是否批改属性的值 | 在对象上定义:true / Object.defineProperty()定义:false |
[[Value]] |
属性的数据值 | undefined |
-
数据属性蕴含一个 保留数据值 的地位,用
Object.defineProperty()
批改属性的默认个性,办法接管 3 个参数:- 属性所在对象、属性名、描述符对象(描述符对象的属性:
configurable
、enumerable
、writable
、value
的一个或多个) - 严格模式下,批改
[[Writable]]
为 false 的值会报错 - 严格模式下,用
delete
删除[[Configurable]]
为 false 的属性会报错
- 属性所在对象、属性名、描述符对象(描述符对象的属性:
var person = {} | |
Object.defineProperty(person, 'name', { | |
writable: false, // 不可批改 | |
configurable: false, // 不可配置 | |
value: 'Nicholas', | |
}) | |
console.log(person.name) // 'Nicholas' | |
person.name = 'Greg' // 试图重写(严格模式会报错)console.log(person.name) // 'Nicholas',无奈重写 | |
delete person.name // 试图删除(严格模式会报错)console.log(person.name) // 'Nicholas',无奈删除 |
-
对同一个属性屡次调用
Object.defineProperty()
会有限度:- 若
configurable
为false
,configurable
、writable
和enumerable
属性不可再被批改 - 若
writable
为false
,描述符对象的value
属性不可再被批改
- 若
Object.defineProperty(person, 'name', { | |
// configurable: true, // 报错,configurable 为 true 方可批改 | |
// value: 'Simon', // 报错,writable 为 true 方可批改 | |
// writable: true, // 报错,configurable 为 true 方可批改 | |
// enumerable: true, // 报错,configurable 为 true 方可批改 | |
}) |
拜访器属性
拜访器属性 | 含意 | 默认值 |
---|---|---|
[[Configurable]] |
是否配置(delete 删除、批改个性、改为拜访器属性) | 在对象上定义:true / Object.defineProperty()定义:false |
[[Enumerable]] |
是否通过 for-in 循环返回属性 | 在对象上定义:true / Object.defineProperty()定义:false |
[[Get]] |
读取属性时调用的函数 | undefined |
[[Set]] |
写入属性时调用的函数 | undefined |
-
拜访器属性 不蕴含数据值,用
Object.defineProperty()
定义属性:办法接管 3 个参数- 属性所在对象、属性名、描述符对象(和数据属性用法一样)
- 只指定
getter()
-> 属性只读不写;只指定setter()
-> 属性只写不读 - 严格模式下,只指定 getter 或 setter 均会报错
var book = { | |
_year: 2017, // 默认属性 | |
edition: 1, // 默认属性 | |
} | |
Object.defineProperty(book, 'year', { | |
// year 是拜访器属性 | |
get() {return this._year}, | |
set(newValue) {if (newValue > 2017) { | |
this._year = newValue // this._year = 2018 | |
this.edition += newValue - 2017 // this.edition = 1 + 2018 - 2017 | |
} | |
}, | |
}) | |
book.year = 2018 // 写入拜访器属性。调用 set()办法 | |
console.log(book) // {_year: 2018, edition: 2} |
- IE8 或更早,定义拜访器属性的办法(遗留的办法,可在浏览器测试,vscode 会报错)
book.__defineGetter__('year', function () {return this._year}) | |
book.__defineSetter__('year', function (newValue) {if (newValue > 2017) { | |
this._year = newValue | |
this.edition += newValue - 2017 | |
} | |
}) | |
book.year = 2018 | |
console.log(book) |
定义多个属性
-
Object.defineProperties()
办法通过 多个描述符 一次性定义多个属性,办法接管 2 个参数:- 要增加 / 批改属性的对象、描述符对象
Object.defineProperties()
办法定义的所有属性都是在 同一时间定义 的
var book2 = {} | |
Object.defineProperties(book2, { | |
_year: { | |
writable: true, | |
value: 2017, | |
}, | |
edition: { | |
writable: true, | |
value: 1, | |
}, | |
year: {get() {return this._year}, | |
set(newValue) {if (newValue > 2017) { | |
this._year = newValue | |
this.edition += newValue - 2017 | |
} | |
}, | |
}, | |
}) | |
book2.year = 2018 | |
console.log(book2.edition) // 2 |
读取属性的个性
-
Object.getOwnPropertyDescriptor()
办法获得指定属性的 属性描述符,接管 2 个参数:- 属性所在对象、要获得其描述符的属性名
var book3 = {} | |
Object.defineProperties(book3, { | |
_year: {value: 2017,}, | |
edition: {value: 1,}, | |
year: {get: function () {return this._year}, | |
set: function (newValue) {if (newValue > 2017) { | |
this._year = newValue | |
this.edition += newValue - 2017 | |
} | |
}, | |
}, | |
}) | |
var descriptor = Object.getOwnPropertyDescriptor(book3, '_year') | |
console.log(descriptor) // {value: 2017, writable: false, enumerable: false, configurable: false} | |
console.log(descriptor.value) // 2017 | |
console.log(descriptor.configurable) // false,Object.getOwnPropertyDescriptor()定义属性的个性,默认值为 false | |
console.log(typeof descriptor.get) // undefined,数据属性不含 get 函数 | |
var descriptor2 = Object.getOwnPropertyDescriptor(book3, 'year') | |
console.log(descriptor2) // {get: [Function: get], set: [Function: set], enumerable: false, configurable: false } | |
console.log(descriptor2.value) // undefined,拜访器属性不含 value | |
console.log(descriptor2.configurable) // false,Object.getOwnPropertyDescriptor()定义属性的个性,默认值为 false | |
console.log(typeof descriptor2.get) // 'function' |
Object.getOwnPropertyDescriptors()
办法获取 参数对象 每个属性的 属性描述符 (在每个属性上调用Object.getOwnPropertyDescriptor()
办法并在一个新对象中返回)
console.log(Object.getOwnPropertyDescriptors(book3)) | |
/* | |
{ | |
_year: { | |
value: 2017, | |
writable: false, | |
enumerable: false, | |
configurable: false | |
}, | |
edition: {value: 1, writable: false, enumerable: false, configurable: false}, | |
year: {get: [Function: get], | |
set: [Function: set], | |
enumerable: false, | |
configurable: false | |
} | |
} | |
*/ |
- 调用
Object.defineProperty()
或Object.defineProperties()
办法批改或定义属性时,configurable
、enumerable
、writable
的默认值 均为 false(用对象字面量则默认值为 true)
var book4 = {year: 2017,} | |
var descriptorBook4 = Object.getOwnPropertyDescriptor(book4, 'year') | |
console.log( | |
'book4', // book4 | |
descriptorBook4.configurable, // true | |
descriptorBook4.enumerable, // true | |
descriptorBook4.writable, // true | |
typeof descriptorBook4.set, // undefined | |
typeof descriptorBook4.get // undefined | |
) | |
var book5 = {} | |
Object.defineProperty(book5, 'year', {value: 2017,}) | |
var descriptorBook5 = Object.getOwnPropertyDescriptor(book5, 'year') | |
console.log( | |
'book5', // book5 | |
descriptorBook5.configurable, // false | |
descriptorBook5.enumerable, // false | |
descriptorBook5.writable, // false | |
typeof descriptorBook4.set, // undefined | |
typeof descriptorBook4.get // undefined | |
) |
合并对象
-
ES6 新增
Object.assign()
办法,办法接管 一个指标对象 和若干源对象 作为参数,将每个源对象中 可枚举 和自有 属性复制到指标对象:- 可枚举:
Object.propertyIsEnmerable()
返回 true - 自有:
Object.hasOwePropety()
返回 true - 应用源对象上的
[[Get]]
获得属性值,应用指标对象上的[[Set]]
设置属性值 - 指标对象会被批改,办法 返回批改后的指标对象
- 可枚举:
let dest, src, result | |
// 单个源对象 | |
dest = {} | |
src = {id: 'src'} | |
result = Object.assign(dest, src) // 返回批改后的指标对象 | |
console.log(result) // {id: 'src'} | |
console.log(dest === result) // true,批改后的指标对象 | |
console.log(dest === src) // false,指标对象和源对象 | |
// 多个源对象 | |
dest = {} | |
result = Object.assign(dest, { a: 'foo'}, {b: 'bar'}) | |
console.log(result) // {a: 'foo', b: 'bar'} | |
// 获取函数与设置函数 | |
dest = {set a(val) {console.log(`Invoked dest setter with param ${val}`) | |
}, | |
} | |
src = {get a() {console.log(`Invoked src better`) | |
return 'foo' | |
}, | |
} | |
Object.assign(dest, src) | |
/* | |
'Invoked src better',调用源对象上的 get 办法取得返回值 | |
'Invoked dest setter with param foo',再调用指标对象的 set()办法传入值 | |
*/ |
- 若多个源对象有雷同的属性,则应用最初一个复制的值
dest = {id: 'dest'} | |
result = Object.assign(dest, { id: 'src1', a: 'foo'}, {id: 'src2', b: 'bar'}) | |
console.log(result) // {id: 'src2', a: 'foo', b: 'bar'} |
- 从源对象拜访器属性获得的值,会作为动态值赋给指标对象,不能在两个对象间转移获取函数和设置函数
dest = {set id(x) {console.log(x) | |
}, | |
} | |
Object.assign(dest, { id: 'first'}, {id: 'second'}, {id: 'third'}) // first second third,顺次赋值给指标对象 | |
console.log(dest) // set id: ƒ id(x),设置函数是不变的 |
Object.assign()
实际上是对每个源对象执行 浅复制
dest = {} | |
src = {a: {} } | |
Object.assign(dest, src) | |
console.log(dest) // {a: {} } | |
console.log(dest === src) // false | |
console.log(dest.a === src.a) // true,对源对象浅复制,复制对象的援用 |
Object.assign()
也能够只有一个参数,参数为 源对象 ,调用办法会间接 返回源对象本人- 只有一个参数的
Object.assign()
更能体现“浅复制”
console.log(Object.assign(src) === src) // true | |
src = {a: 1} | |
dest = Object.assign(src) | |
console.log(dest) // {a: 1} | |
dest.a = 2 | |
console.log(src) // {a: 2} |
- 如果 赋值期间出错 ,操作中之并退出报错,
Object.assign()
不会回滚,已实现的批改将保留
dest = {} | |
src = { | |
a: 'foo', // 没遇到谬误,执行复制 | |
get b() {throw new Error() // 注入谬误,操作终止 | |
}, | |
c: 'bar', // 已遇到谬误,不会执行 | |
} | |
try {Object.assign(dest, src) | |
} catch (e) {} | |
console.log(dest) // {a: 'foo'},遇到谬误前曾经实现的批改被保留 |
对象标识及相等断定
-
ES6 新增
Object.is()
,办法接管 2 个参数,用于判断 两个值是否是雷同的值,如果下列任何一项成立,则两个值雷同:- 两个值都是 undefined
- 两个值都是 null
- 两个值都是 true 或者都是 false
- 两个值是由雷同个数的字符依照雷同的程序组成的字符串
- 两个值指向同一个对象
-
两个值都是数字并且:
- 都是正零 +0
- 都是负零 -0
- 都是 NaN
- 都是除零和 NaN 外的其它同一个数字
console.log(undefined === undefined) // true | |
console.log(null === null) // true | |
console.log(+0 === 0) // true | |
console.log(+0 === -0) // true // true | |
console.log(-0 === 0) // true | |
console.log(NaN === NaN) // false | |
// Object.is() | |
console.log(Object.is(undefined, undefined)) // true | |
console.log(Object.is(null, null)) // true | |
console.log(Object.is(+0, 0)) // true | |
console.log(Object.is(+0, -0)) // false | |
console.log(Object.is(-0, 0)) // false | |
console.log(Object.is(NaN, NaN)) // true |
加强的对象语法
属性值简写
- 应用变量名(不再写冒号)会主动被解释为同名属性键
var name = 'Matt' | |
var person = {name: name,} | |
var person = {name} |
可计算属性
- 能够在对象字面量中实现 动静属性赋值
var nameKey = 'name' | |
var ageKey = 'age' | |
var jobKey = 'job' | |
var person = {[nameKey]: 'Matt', | |
[ageKey]: 27, | |
[jobKey]: 'Software engineer', | |
} | |
console.log(person) // {name: 'Matt', age: 27, job: 'Software engineer'} |
- 可计算属性自身能够是 简单的表达式,实例化时再求值
var uniqueToken = 0 | |
function getUniqueKey(key) {return `${key}_${uniqueToken++}` | |
} | |
var person = {[getUniqueKey(nameKey)]: 'Matt', | |
[getUniqueKey(ageKey)]: 27, | |
[getUniqueKey(jobKey)]: 'Software engineer', | |
} | |
console.log(person) // {name_0: 'Matt', age_1: 27, job_2: 'Software engineer'} |
简写办法名
- 放弃给函数表达式明名,显著缩短办法申明
var person = {sayName: function (name) {console.log(`My name is ${name}`) | |
}, | |
} | |
var person = {sayName(name) {console.log(`My name is ${name}`) | |
}, | |
} |
- 对获取函数和设置函数也实用
var person = { | |
name_: '', | |
get name() {return this.name_}, | |
set name(name) {this.name_ = name}, | |
sayName() {console.log(`My name is ${this.name_}`) | |
}, | |
} | |
person.name = 'Matt' | |
person.sayName() // 'My name is Matt' |
- 与 可计算属性键 互相 兼容
var methodKey = 'sayName' | |
var person = {[methodKey](name) {console.log(`My name is ${name}`) | |
}, | |
} | |
person.sayName('Matt') // 'My name is Matt' |
对象解构
- 在 一条语句 中应用 嵌套数据 实现 一个或多个赋值 操作(应用 与对象相匹配的构造 实现 对象属性赋值)
var person = { | |
name: 'Matt', | |
age: 27, | |
} | |
var {name: personName, age: personAge} = person | |
console.log(personName, personAge) // 'Matt' 27 |
- 变量间接应用属性名称,可进一步简化语法
var {name, age} = person // 变量间接应用属性名称 | |
console.log(name, age) // 'Matt' 27 |
- 援用的属性不存在,则该变量的值为 undefined
var {name, job} = person // job 不存在 | |
console.log(name, job) // 'Matt' undefined |
- 可在解构赋值时 定义默认值
var {name, job = 'Sofrware engineer'} = person // 定义 job 的默认值 | |
console.log(name, job) // 'Matt' 'Sofrware engineer' |
- 解构外部应用函数
ToObject()
(不能间接在运行环境拜访)把元数据 转换为对象,因而原始值会被当做对象,null 和 undefined 不能被解构(会报错)
var {length} = 'foobar' // 'foobar' 转换为 String 包装对象 | |
console.log(length) // 6,字符串长度 | |
var {constructor: c} = 4 // 4 转换为 Number 包装对象 | |
console.log(c === Number) // true,constructor 指向构造函数 | |
var {_} = null // TypeError | |
var {_} = undefined // TypeError |
- 给 当时申明的变量 赋值时,赋值表达式 必须蕴含在一对括号中
var person = { | |
name: 'Matt', | |
age: 27, | |
} | |
let personName2, | |
personAge2 // 当时申明的变量 | |
;({name: personName2, age: personAge2} = person) // 给实现申明的变量赋值,赋值表达式必须蕴含在一对括号中 | |
console.log(personName, personAge) // 'Matt' 27 |
嵌套解构
- 能够通过解构来 复制对象属性
var person = { | |
name: 'Matt', | |
age: 27, | |
job: {title: 'Software engineer',}, | |
} | |
var personCopy = {} | |
;({name: personCopy.name, age: personCopy.age, job: personCopy.job} = person) // 解构赋值,复制对象属性 | |
person.job.title = 'Hacker' // 批改属性,源对象和赋值对象都受影响 | |
console.log(person) // {name: 'Matt', age: 27, job: { title: 'Hacker'} } | |
console.log(personCopy) // {name: 'Matt', age: 27, job: { title: 'Hacker'} } |
- 能够应用 嵌套构造,以匹配嵌套的属性
var {job: { title}, | |
} = person | |
console.log(title) // 'Hacker',嵌套解构,title = person.job.title |
- 无论源对象还是指标对象,外层属性未定义 时,不能 应用嵌套构造
var person = { | |
job: {title: 'Software engineer',}, | |
} | |
var personCopy = {} | |
;({foo: { bar: personCopy.bar}, | |
// personCopy.bar = person.foo.bar,foo 在源对象 person 上是 undefined | |
} = person) // TypeError: Cannot read property 'bar' of undefined | |
;({job: { title: personCopy.job.title}, | |
// personCopy.job.title = person.job.title,job 在指标对象 persoCopy 上是 undefined | |
} = person) // TypeError: Cannot set property 'title' of undefined |
局部解构
- 如果一个解构表达式 波及多个赋值 ,开始的赋值胜利而前面的赋值出错,解构赋值只会实现一部分, 出错前面的不再赋值
var person = { | |
name: 'Matt', | |
age: 27, | |
} | |
var personName, personBar, personAge | |
try { | |
;({ | |
name: personName, // personName = person.name,赋值胜利,foo: {bar: personBar}, // personBar = person.foo.bar,foo 未在 person 定义,赋值失败操作中断 | |
age: personAge, // 操作已中断,赋值失败 | |
} = person) | |
} catch (e) {} | |
console.log(personName, personBar, personAge) // 'Matt' undefined undefined |
参数上下文匹配
- 函数 参数列表 也能够进行解构赋值,且 不影响 arguments 对象,能够在函数签名中申明在函数体内应用局部变量
var person = { | |
name: 'Matt', | |
age: 27, | |
} | |
function printPerson(foo, { name, age}, bar) {console.log(arguments) | |
console.log(name, age) | |
} | |
printPerson('1st', person, '2nd') | |
// ["1st", {name: "Matt", age: 27}, "2nd"] | |
// 'Matt' 27 | |
function printPerson2(foo, { name: personName, age: personAge}, bar) {console.log(arguments) | |
console.log(name, age) | |
} | |
printPerson2('1st', person, '2nd') | |
// ["1st", {name: "Matt", age: 27}, "2nd"] | |
// 'Matt' 27 |
总结 & 问点
API | 含意 | 参数 | 返回值 |
---|---|---|---|
Object.defineProperty() |
批改属性的默认个性 / 定义属性 | ① 属性所在对象 ② 属性名 ③ 描述符对象 | |
Object.defineProperties() |
一次性(同时)定义多个属性 | ① 属性所在对象 ② 描述符对象 | |
Object.getOwnPropertyDescriptor() |
获得指定属性的属性描述符 | ① 属性所在对象 ② 要获得描述符的属性名 | 属性描述符对象 |
Object.getOwnPropertyDescriptors() |
获取参数对象每个属性的属性描述符 | ① 属性所在对象 ② 要获得描述符的属性名 | 每个属性描述符对象组成的新对象 |
Object.assign() |
源对象可枚举且自有属性复制到指标对象 | ① 指标对象(选)② 源对象 | 批改后的指标对象 |
Object.is() |
判断两个值是否是雷同的值 | ① 值 1 ② 值 2 | true / false |
- JS 的对象是什么?其属性能够蕴含什么?创立自定义对象有哪些办法?
- 对象的数据属性有哪些个性?其含意和默认值别离是什么?
- 如何批改对象的数据属性?对同一个属性屡次批改有哪些限度?
- 对象的拜访器属性有哪些个性?其含意和默认值别离是什么?
- 如何定义对象的拜访器属性?同时定义多个属性呢?
- 如何获取指定属性的属性描述符?全副属性的属性描述符呢?
- Object.assign()的用法和本质含意是什么?其是否转移 get()函数和 set()函数?
- 请用代码证实 Object.assign()是“浅复制”以及“不回滚”
- ES6 有哪些加强对象的语法?其用法别离是什么?
- 什么是解构赋值?原始值能够当作解构源对象么?为什么?
- 请用代码顺次举例嵌套解构、局部解构和参数上下文匹配
正文完
发表至: javascript
2021-04-23