对象
对象的内容是一组键值对,值可以是数据或者函数。
let persion = new Object();
persion.name = 'Eden';
persion.sayName = function () {
console.log(this.name)
}
}
let persion = {
name: 'Eden',
sayName() {
console.log(this.name)
}
<!--用这两种方式创建的对象是等价的,他们的属性和方法都一样 -->
属性的特征(内部特征不可用js直接访问)
数据属性
数据属性包含一个保存值的位置,值会从这个位置读取,也会写入这个位置。 数据属性有四个特性描述他们的行为。
- [[Confingurable]]:表示是否可以通过delete删除并重新定义,是否可以把它修改为浏览器属性,默认情况下,所有直接定义在对象上的属性该值都为true,如前面例子。
- [[Enumerable]]:属性是否可以通过for-in循环返回,默认情况下,所有直接定义在对象上的属性该值都为true,如前面例子。
- [[Writable]]:属性的值是否可被修改,默认情况下,所有直接定义在对象上的属性该值都为true,如前面例子。
- [[Value]]:包含属性实际的值,就是前面提到读取和写入属性值的位置,默认为undefined,前面的例子中,name的[[Value]]特性会被设置为'Eden'。
如果要改变属性的默认特性,就要使用Object.definePropety(),该方法接收三个参数,对象,属性名称,和一个描述符对象。如下所示:
let persion = {};
Object.defineProperty(persion, 'name', {
writable: false,
value: 'Eden'
})
console.log(persion.name) //Eden
persion.name = 'Rax'
console.log(persion.name) //Eden
此外,当一个属性被定义为不可配置之后(Confingurable:false),就不能改为可以配置的了,再次调用Object.defineProperty()并修改任何非wirtable的属性都会跑出错误。 在调用Object.defineProperty()时,Confingurable,Enumerable,Writable值如果不指定,则都默认为false.
访问器属性
访问器属性不包含数据值,他们包含一个getter函数和一个setter函数,在读取访问器属性时,或调用getter函数,这个函数会返回一个有效的值。在写入访问器属性时,会调用setter函数并传入新的值。访问器属性有四个特性描述他们的行为。
- [[Confingurable]]:表示是否可以通过delete删除并重新定义,是否可以把它修改为数据属性,默认情况下,所有直接定义在对象上的属性该值都为true。
- [[Enumerable]]:属性是否可以通过for-in循环返回,默认情况下,所有直接定义在对象上的属性该值都为true,如前面例子。
- [[Get]]:获取函数,在读取属性时调用,默认值为undefined。
- [[Set]]:设置函数,在写入属性时调用,默认值为undefined。
访问器属性不能直接定义,必须使用Object.defineProperty(),如下:
let 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.edition) //2
定义多个属性值
Object.defineProperties(book, {
year_: {
value:2017
},
edition: {
value:1
},
year: {
get() {
return this.year_;
},
set(newValue) {
if (newValue > 2017) {
this.year_ = newValue;
this.edition += newValue - 2017
}
}
}
})
这段代码直接定义了两个数据属性year_和edition,还有一个访问器属性year,与上面例子的区别在于数据属性Confingurable,Enumerable,Writable的特性值都为false。
读取属性的特征。
Obiect.getOwnPropertyDescroptor(),可获取指定属性的属性描述符。
Obiect.getOwnPropertyDescroptor(book,'year_')
Obiect.getOwnPropertyDescroptors()会在每个自有属性上调用Obiect.getOwnPropertyDescroptor(),并在一个新对象中返回他们。
Obiect.getOwnPropertyDescroptors(book)
合并对象(把源对象的所有本地属性一起复制到目标对象上)
Object.assign(),该方法接收一个目标对象和多个源对象作为参数,每个源对象中可枚举(Object.proprtyIsEnumerable()return true)和自有(Object.hasOwnPropety()return true)属性复制到目标对象,对每个符合条件的属性,这个方法会使用源对象[[Get]]取得属性的值,然后使用目标对象上的[[Set]]设置属性的值。
// 单个源对象
let result, dest, a;
dest = {};
a = { id: 'aid' }
result = Object.assign(dest, a)
// Object.assign修改目标对象,也会返回修改后的目标对象。
console.log(result === dest) //true
console.log(a === dest) //false
// 多个源对象
dest = {};
result = Object.assign(dest, { a: 'aid' }, { b: 'bid' })
// 设置函数与获取函数 (对象上的方法,不是浏览器属性上的方法)
dest = {
set id(value) {
console.log('id:', value);
}
}
a = {
get id() {
return 'aid'
}
}
Object.assign(dest, a)
console.log(dest); //{}因为设置函数没有赋值操作
Object.assign对每个源对象执行的是浅复制,如一多个源对象有相同的属性,则使用最后一个复制的值,不能在两个对象之间转移获取函数和设置函数,源对象访问器上的获取函数会作为一个静态值复制给目标对象。
对象引用
浅复制意味着只会复制对象的引用
let dest, src;
dest = {}
src = {
a:{}
}
Object.assign(dest, src)
console.log(dest.a === src.a);//true
如果赋值出错,则操作会终止并退出,但是在报出错误之前目标对象上已经完成的修改会继续存在。
对象标志及相等判定
边界值情况,===操作符判断出错
console.log(+0 === -0);//true
console.log(+0 === 0);//true
console.log(-0 === 0);//true
console.log(NaN === NaN);//false
// // 可以使用Object.is()
console.log(Object.is(+0, -0));//false
console.log(Object.is(+0, 0));//true
console.log(Object.is(-0, 0));//false
console.log(Object.is(NaN, NaN));//true
// 检查超过两个值的可以用递归属性
const checkRqual = (x, ...rest) => {
console.log(rest[0]);
return Object.is(x, rest[0]) &&
(rest.length < 2 || checkRqual(...rest))
}
console.log(checkRqual(0, +0, 0, 0)); //true
console.log(checkRqual(0, +0, -0, 0)); //false
增强对象的语法(ES6语法糖)
属性值简写
// 当属性名和变量一致时
let name = 'Eden'
let persion = {
name: name
}
}
// 可适用以下方式简写,如果没有找到同名变量,则会抛出ReferenceError
let persion = {
name
}
可计算属性
// 使用变量的值做为属性
const nameKey = 'name';
const ageKey = 'age';
let persion = {};
persion[nameKey] = 'Eden';
persion[ageKey] = 24;
console.log(persion); //{name:'Eden',age:24}
// 有了可计算属性后,可在对象字面量中完成动态赋值,中括号包围的对象键做为js表达式而不是字符串
const nameKey = 'name';
const ageKey = 'age';
let persion = {
[nameKey]: 'Eden',
[ageKey]:24
}
console.log(persion); //{name:'Eden',age:24}
可计算属性可以是复杂的表达式,可以时一个方法,在实例化时在求值
简写方法名
// 给对象定义方法
let persion = {
sayName: function (name) {
console.log(`My name is ${name}`);
}
}
// 简写方法
let persion = {
sayName(name) {
console.log(`My name is ${name}`);
}
简写方法名与可计算属性可以相互兼容
对象解构
ES6新增语法,使用与对象匹配的结构来实现对象一个或多个的属性赋值。
- 不使用解构时
let persion = {
name: 'Eden',
age: 24
}
let persionName = persion.name,
persionAge = persion.age;
- 使用解构语法
let persion = {
name: 'Eden',
age: 24
}
let { name: persionName, age: persionAge } = persion;
- 如果变量直接使用属性名称,可以简写
let persion = {
name: 'Eden',
age: 24
}
let { name, age } = persion;
- 当引用属性不存在,则该变量的值为undefined
let persion = {
name: 'Eden',
age: 24
}
let { name, job } = persion
console.log(jib);//undefined
// 解构时可定义默认值
let { name, job = 'programmer' } = persion;
console.log(jib);//programmer
- 解构变量可以事先声明,此时赋值表达式必须包含在一对括号里
let persion = {
name: 'Eden',
age: 24
}
let persionName, persionAge;
({ name: persionName, age: persionAge }) = persion;
- 解构在内部使用函数ToObject(),这意味着在对象解构上下文中,原始值会被当作对象,null和undefined不能被解构
let { length } = 'Eden'
console.log(length); //4
let { constructor } = 4
console.log(constructor === Number);//true
嵌套解构
- 通过解构来赋值对象属性
let persion = {
name: 'Eden',
age: 24,
job: {
content:'programmer'
}
}
let otherPersion = {};
({
name: otherPersion.name,
age: otherPersion.age,
job: otherPersion.job
} = persion)
// 因为只是对象的引用被赋值,所以改变persion.job会影响otherPersion
persion.job.content = 'teacher'
console.log(otherPersion.job.content);//teacher
- 赋值解构可以使用嵌套解构
let persion = {
name: 'Eden',
age: 24,
job: {
content:'programmer'
}
}
let { job: { content } } = persion
console.log(content);//programmer
- 无论时源对象还是目标对象,在外层属性没有定义的情况下不能使用嵌套解构
let persion = {
name: 'Eden',
age: 24,
job: {
content: 'programmer'
}
}
let otherPersion = {};
// foo在源对象上是undefined
({
foo: {
bar: otherPersion.bar
}
} = persion) //TypeError
//foo在目标对象上是undefined
({
job: {
content: otherPersion.job.content
}
} = persion)//TypeError
部分解构
如果一个表达式涉及多个赋值,开始赋值成功后面出错,则解构只会完成一部分,后面失败的属性值为undefined
参数上下文匹配
函数参数列表中也可以使用解构赋值
let persion = {
name: 'Eden',
age: 24,
job: {
content: 'programmer'
}
}
function printPersion({ name: persionName, age: persionAge }) {
console.log(persionName, persionAge);
}
printPersion(persion) //Eden 24