理解JS对象
1.理解对象
创建自定义对象的通常方式是创建一个Object的一个实例,然后给它添加属性和方法
eg:
let person = new Object();
person.name = "Nocholas";
person.age = 29;
person.job = "Sofaware Engineer";
person.sayName = function () {
console.log(this.name);
}
console.log(person);
这个例子创建了一个名为person的对象,而且有三个属性值 name age job 和一个方法sayName。这个创建对象的方法再以前是很流行的。然而在现在,大多采用对象字面量的方法
eg:
let person = {
name: "Nocholas",
age:29,
jog:"Software Engineer",
sayName(){
console.log(this.name);
}
}
console.log(person);
这种创建对象的方法更加简洁明了,在写代码的时候,我更喜欢这种写法。。
2.属性的类型
对象里的属性分为两种:数据属性 和 访问器属性。首先我们看看数据属性。
2.1数据属性
数据属性包含一个保存数据值的位置。值会从这个位置读取,也会写入到这个位置。数据属性有 4 个特性描述它们的行为。
-
[[Configurable]]:翻译:adj. 可配置的;结构的 表示属性是否可以通过 delete 删除并重新定义,是否可以修改它的特 性,以及是否可以把它改为访问器属性。默认情况下,所有直接定义在对象上的属性的这个特 性都是 true,如前面的例子所示。
-
[[Enumerable]]:翻译:adj. 可列举的;可点数的 表示属性是否可以通过 for-in 循环返回。默认情况下,所有直接定义在对 象上的属性的这个特性都是 true,如前面的例子所示。
-
[[Writable]]:翻译:adj. 可写的,能写成文的 表示属性的值是否可以被修改。默认情况下,所有直接定义在对象上的属性的 这个特性都是 true,如前面的例子所示。
-
[[Value]]:翻译:值 包含属性实际的值。这就是前面提到的那个读取和写入属性值的位置。这个特性 的默认值为 undefined
将属性添加到对象之后 Configurable Enumerable Writable 都会被默认设置为true。而value会被设置为指定值。要修改属性的默认特性,我们必须使用
Object.defineProperty()
翻译:定义属性
let person = {}; Object.defineProperty(person,"name",{ writable: false, // 设置为false后无法被修改 value: "Nicholas" }); console.log(person.name); person.name = "Greg"; console.log(person.name);这个例子创建了一个名为 name的属性,并把它赋值 Nicholas,并且它的writeable属性被设置为 false 。那么这个值就不能修改了,在非严格模式下重新给name赋值会被忽略,而在严格模式下 则会抛出错误。
类似的规则,也使用与其它数据属性,下面我们来试试 configurable这个属性,这个属性设置为false后,那么相应的属性就不能删除了。
let person = {}; Object.defineProperty(person,"name",{ configurable: false, // 设置false后无法被删除了 value: "Nicholas" }); console.log(person.name); delete person.name; console.log(person.name);还是老样子,非严格模式不报错,严格模式下抛出错误。
此外:此外,一个属性被定义为不可配置之后,就 不能再变回可配置的了。
也就是说这玩意是单程票,改了就改不回去了。这些数据属性,可能我们在开发中用的不多,单通过它们可以更好的理解JS中的对象。
2.2访问器属性
访问器属性不包括数据值。它们包含一个获取 getter函数 和一个设置 setter函数,不过这两个函数不是必须的。在设置访问属性时,会调用获取还能输返回一个有效值。在写入访问器属性时,会调用设置函数并传入新值,访问器属性有四个特性描述它们的行为
- [[Configurable]]:表示属性是否可以通过 delete 删除并重新定义,是否可以修改它的特 性,以及是否可以把它改为数据属性。默认情况下,所有直接定义在对象上的属性的这个特性 都是 true。
- [[Enumerable]]:表示属性是否可以通过 for-in 循环返回。默认情况下,所有直接定义在对 象上的属性的这个特性都是 true。
- [[Get]]:获取函数,在读取属性时调用。默认值为 undefined。
- [[Set]]:设置函数,在写入属性时调用。默认值为 undefined。
访问器属性不能直接定义,必须使用Object.defineProperty()
eg:
// 访问器属性
// 定义一个对象,包含私有成员year_和公共成员edition
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
这个例子里面 book 对象 有两个默认属性 year_ 和 edition 。这是访问器属性的典型使用场景,即设置一个值会导致其它值发送一些变化。 获取函数和设置函数不一定都需要定义,只定义获取函数意味着属性时只读的,只有设置函数的属性意味着不能读取,非严格模式会返回 undefined 严格模式下会抛出错误。
3.定义多个属性
在一个对象上同时定义多个属性 我们可以采用 **Object.defineProperties()**方法。它接收两个参数
eg:
let book = {};
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
}
}
}
});
定义一个属性和多个属性的唯一区别就是,所有属性是同时定义的,并且数据属性的configurable enumerable writable 特性值都是false。这也意味着。。无法删除,无法遍历,无法修改。。。黑人问号.jpg??????好吧我也不知道为啥这样搞
4.读取属性的特性
使用 Object.getOwnPropertyDescriptor()方法可以取得指定属性的描述接收两个参数 属性所在的对象和属性名,返回一个对象。
eg:
let book = {};
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
}
}
}
});
let descriptor = Object.getOwnPropertyDescriptor(book, "year_");
console.log(descriptor);
descriptor = Object.getOwnPropertyDescriptor(book, "year");
console.log(descriptor);
输出:
{
value: 2017,
writable: false,
enumerable: false,
configurable: false
}
{
get: [Function: get],
set: [Function: set],
enumerable: false,
configurable: false
}
ECMAScript 2017 新增了 Object.getOwnPropertyDescriptors()(其实也就是多了个s)静态方法。这个方法实际上 会在每个自有属性上调Object.getOwnPropertyDescriptor()并在一个新对象中返回它们。
用这个方法我们就可以用这个方法来直接输出了
let book = {};
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
}
}
}
});
console.log(Object.getOwnPropertyDescriptors(book));
输出:
{
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
}
}
5.合并对象
-
合并两个对象,有时候也称为“混入”,因为目标对象通过混入元对象的属性得到了增强。ECMAScript6专门为合并对象提供了Object.assign()方法这个方法接收一个目标对象和一个或多个源对象作为参数,然后将每个源对象中可枚举属性复制到目标对象。
eg:
// 合并对象 let dest,src,result; /** * 简单复制 */ dest = {}; src = {id:'src'}; result = Object.assign(dest,src); // Object.assign修改目标对象 // 也会返回修改后的目标对象 console.log(dest === result); console.log(dest !== src); console.log(result); console.log(dest); true true { id: 'src' } { id: 'src' }多个源对象
eg:
/** * 多个源对象 */ let dest,src,result; dest = {}; result = Object.assign(dest,{a:'foo'},{b:'bar'}); console.log(result);
获取函数与设置函数
eg:
/**
* 获取函数与设置函数
*/
let dest,src,result;
dest = {
set a(val){
console.log(`You input ${val}`);
}
};
src = {
get a(){
console.log("get it");
return 'foo';
}
};
Object.assign(dest,src);
// 调用 src 的获取方法
// 调用 dest 的设置方法并传入参数"foo"
// 因为这里的设置函数不执行赋值操作
// 所以实际上并没有把值转移过来
console.log(dest);
Object.assign()实际上对每个源对象执行的是浅复制。如果多个源对象都有相同的属性,则使 用最后一个复制的值。此外,从源对象访问器属性取得的值,比如获取函数,会作为一个静态值赋给目 标对象。换句话说,不能在两个对象间转移获取函数和设置函数。
覆盖属性:
/**
* 覆盖属性
*/
let dest,src,result;
dest = {id:'dest'};
result = Object.assign(dest,{id:'src1',a:'foo'},{id:'src2',b:'bar'});
console.log(result);
对象引用
let dest,src,result;
dest = {};
src ={a:{}};
Object.assign(dest,src);
// 浅复制意味着只会复制对象的引用
console.log(dest);
console.log(dest.a === src.a);
//{ a: {} }
//true