关注[前端小讴] ,阅读更多原创技术文章
理解对象
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)
person. name = 'Greg'
console. log ( person. name)
delete person. name
console. log ( person. name)
对同一个属性多次调用 Object.defineProperty()
会有限制:
若 configurable
为 false
,configurable
、writable
和enumerable
属性不可再被修改 若 writable
为 false
,描述符对象的value
属性不可再被修改
Object. defineProperty ( person, 'name' , {
} )
访问器属性
访问器属性 含义 默认值 [[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' , {
get ( ) {
return this . _year
} ,
set ( newValue) {
if ( newValue > 2017 ) {
this . _year = newValue
this . edition += newValue - 2017
}
} ,
} )
book. year = 2018
console. log ( book)
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)
读取属性的特性
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)
console. log ( descriptor. value)
console. log ( descriptor. configurable)
console. log ( typeof descriptor. get )
var descriptor2 = Object. getOwnPropertyDescriptor ( book3, 'year' )
console. log ( descriptor2)
console. log ( descriptor2. value)
console. log ( descriptor2. configurable)
console. log ( typeof descriptor2. get )
Object.getOwnPropertyDescriptors()
方法获取参数对象 每个属性的属性描述符 (在每个属性上调用Object.getOwnPropertyDescriptor()
方法并在一个新对象中返回)
console. log ( Object. getOwnPropertyDescriptors ( book3) )
调用 Object.defineProperty()
或Object.defineProperties()
方法修改或定义属性时,configurable
、enumerable
、writable
的默认值 均为 false(用对象字面量则默认值为 true)
var book4 = {
year: 2017 ,
}
var descriptorBook4 = Object. getOwnPropertyDescriptor ( book4, 'year' )
console. log (
'book4' ,
descriptorBook4. configurable,
descriptorBook4. enumerable,
descriptorBook4. writable,
typeof descriptorBook4. set ,
typeof descriptorBook4. get
)
var book5 = { }
Object. defineProperty ( book5, 'year' , {
value: 2017 ,
} )
var descriptorBook5 = Object. getOwnPropertyDescriptor ( book5, 'year' )
console. log (
'book5' ,
descriptorBook5. configurable,
descriptorBook5. enumerable,
descriptorBook5. writable,
typeof descriptorBook4. set ,
typeof descriptorBook4. get
)
合并对象
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)
console. log ( dest === result)
console. log ( dest === src)
dest = { }
result = Object. assign ( dest, { a: 'foo' } , { b: 'bar' } )
console. log ( result)
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)
dest = { id: 'dest' }
result = Object. assign ( dest, { id: 'src1' , a: 'foo' } , { id: 'src2' , b: 'bar' } )
console. log ( result)
从源对象访问器属性取得的值,会作为静态值赋给目标对象,不能在两个对象间转移获取函数和设置函数
dest = {
set id ( x) {
console. log ( x)
} ,
}
Object. assign ( dest, { id: 'first' } , { id: 'second' } , { id: 'third' } )
console. log ( dest)
Object.assign()
实际上是对每个源对象执行浅复制
dest = { }
src = { a: { } }
Object. assign ( dest, src)
console. log ( dest)
console. log ( dest === src)
console. log ( dest. a === src. a)
Object.assign()
也可以只有一个参数,参数为源对象 ,调用方法会直接返回源对象自己 只有一个参数的Object.assign()
更能体现“浅复制”
console. log ( Object. assign ( src) === src)
src = { a: 1 }
dest = Object. assign ( src)
console. log ( dest)
dest. a = 2
console. log ( src)
如果赋值期间出错 ,操作中之并退出报错,Object.assign()
不会回滚 ,已完成的修改将保留
dest = { }
src = {
a: 'foo' ,
get b ( ) {
throw new Error ( )
} ,
c: 'bar' ,
}
try {
Object. assign ( dest, src)
} catch ( e ) { }
console. log ( dest)
对象标识及相等判定
ES6 新增Object.is()
,方法接收 2 个参数,用于判断两个值是否是相同的值 ,如果下列任何一项成立,则两个值相同:
两个值都是 undefined 两个值都是 null 两个值都是 true 或者都是 false 两个值是由相同个数的字符按照相同的顺序组成的字符串 两个值指向同一个对象 两个值都是数字并且:
都是正零 +0 都是负零 -0 都是 NaN 都是除零和 NaN 外的其它同一个数字
console. log ( undefined === undefined)
console. log ( null === null )
console. log ( + 0 === 0 )
console. log ( + 0 === - 0 )
console. log ( - 0 === 0 )
console. log ( NaN === NaN )
console. log ( Object. is ( undefined, undefined) )
console. log ( Object. is ( null , null ) )
console. log ( Object. is ( + 0 , 0 ) )
console. log ( Object. is ( + 0 , - 0 ) )
console. log ( Object. is ( - 0 , 0 ) )
console. log ( Object. is ( NaN , NaN ) )
增强的对象语法
属性值简写
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)
var uniqueToken = 0
function getUniqueKey ( key) {
return ` ${ key} _ ${ uniqueToken++ } `
}
var person = {
[ getUniqueKey ( nameKey) ] : 'Matt' ,
[ getUniqueKey ( ageKey) ] : 27 ,
[ getUniqueKey ( jobKey) ] : 'Software engineer' ,
}
console. log ( person)
简写方法名
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 ( )
var methodKey = 'sayName'
var person = {
[ methodKey] ( name) {
console. log ( `My name is ${ name} ` )
} ,
}
person. sayName ( 'Matt' )
对象解构
在一条语句 中使用嵌套数据 实现一个或多个赋值 操作(使用与对象相匹配的结构 实现对象属性赋值 )
var person = {
name: 'Matt' ,
age: 27 ,
}
var { name: personName, age: personAge } = person
console. log ( personName, personAge)
var { name, age } = person
console. log ( name, age)
引用的属性不存在 ,则该变量的值为 undefined
var { name, job } = person
console. log ( name, job)
var { name, job = 'Sofrware engineer' } = person
console. log ( name, job)
解构内部使用函数ToObject()
(不能直接在运行环境访问)把元数据转换为对象 ,因此原始值会被当做对象,null 和 undefined 不能被解构(会报错)
var { length } = 'foobar'
console. log ( length)
var { constructor: c } = 4
console. log ( c === Number)
var { _ } = null
var { _ } = undefined
给事先声明的变量 赋值时,赋值表达式必须包含在一对括号中
var person = {
name: 'Matt' ,
age: 27 ,
}
let personName2,
personAge2
; ( { name: personName2, age: personAge2 } = person)
console. log ( personName, personAge)
嵌套解构
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)
console. log ( personCopy)
var {
job: { title } ,
} = person
console. log ( title)
无论源对象还是目标对象,外层属性未定义 时,不能 使用嵌套结构
var person = {
job: {
title: 'Software engineer' ,
} ,
}
var personCopy = { }
; ( {
foo: { bar: personCopy. bar } ,
} = person)
; ( {
job: { title: personCopy. job. title } ,
} = person)
部分解构
如果一个解构表达式涉及多个赋值 ,开始的赋值成功而后面的赋值出错,解构赋值只会完成一部分,出错后面的不再赋值
var person = {
name: 'Matt' ,
age: 27 ,
}
var personName, personBar, personAge
try {
; ( {
name: personName,
foo: { bar: personBar } ,
age: personAge,
} = person)
} catch ( e ) { }
console. log ( personName, personBar, personAge)
参数上下文匹配
函数参数列表 也可以进行解构赋值,且不影响 arguments 对象 ,可以在函数签名中声明在函数体内使用局部变量
var person = {
name: 'Matt' ,
age: 27 ,
}
function printPerson ( foo, { name, age } , bar) {
console. log ( arguments)
console. log ( name, age)
}
printPerson ( '1st' , person, '2nd' )
function printPerson2 ( foo, { name: personName, age: personAge } , bar) {
console. log ( arguments)
console. log ( name, age)
}
printPerson2 ( '1st' , person, '2nd' )
总结 & 问点
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 有哪些增强对象的语法?其用法分别是什么? 什么是解构赋值?原始值可以当作解构源对象么?为什么? 请用代码依次举例嵌套解构、部分解构和参数上下文匹配