JS-对象详细解析
对象详细解析
对象
创建一个对象
大概有两种方式
-
文字语法
var myObj = { key:value ... }
-
构造形式
var myObj = new Object(); myObj.key = value;
两种方式唯一的区别:在文字声明中可以添加多个key/value,而在构造形式中只能添加一个
因此,在JS社区中,大多数人都认为能使用文字声明就不要使用构造形式
对象的内容
对象的内容是由一些存储在特定命名位置的值组成的,我们称之为属性
在引擎内部,这些值的存储方式是多种多样的,一般不会在对象容器的内部。存储在对象容器内部的是这些属性的名称,他们就像指针一样,指向真正存储的位置
访问属性的值,我们有两种方式:.
和[""]
var obj = {
a:2,
}
obj.a;
obj['a']
值得注意的一点是,使用[""]
时,一定是以字符串的形式访问,并且可以接受任意UTF-8字符串作为属性名
在对象中,属性名永远都是字符串
数组
数组也支持使用[]
访问,不过数组期望的是下标,也就是值存储的位置->整数
但是,数组也是对象,你仍然可以给数组添加属性
var myArr = ['foo',42,'bar'];
myArr.baz = "baz";
myArr.length; // 3
myArr.baz; // "baz"
可以看到,给数组添加属性后length并不会改变,但是我们可以访问到该属性
但是,这种语法并不提倡,数组和普通的对象都根据其对应的行为和用途进行了优化,所以最好还是只用对象存储key/value
,只用数组存储index/value
值得注意的一点是:当你试图向数组添加一个属性,但是属性名看起来下一个数字,那么他就会变成一个数值下标,因此会修改数组的内容,而不是添加一个属性
复制对象
function anotherFunction(){}
var anotherObject = {
c:true,
}
var anotherArray = [];
var myObject = {
a:2,
b:anotherObject,
c:anotherArray,
d:anotherFunction
}
两种复制:
-
浅拷贝,复制出的新对象的a的值会复制旧对象中a的值,也就是2,但是对于
b,c,d
四个属性来说,他们只是引用,他们与旧对象中的b,c,d
引用的是同一个对象ES6中定义了
Object.assign()
实现浅拷贝var newObj = Object.assign({},myObject)
-
深拷贝,与浅拷贝不同的是,除了复制myObject之外,还会复制anotherObject,anotherArray
var newObj = JSON.parse(JSON.stringify(someObj))
属性描述符
从ES5开始,所有属性都具备了属性描述符
var myObject = {
a:2
}
Object.getOwnPropertyDescriptor(myObject,"a");
// { value: 2, writable: true, enumerable: true, configurable: true }
可以看到属性a不止有value,还多了另外三个特性:writable(可写),enumerable(可枚举),configurable(可配置)
在创建普通属性时,属性描述符会使用默认值(全都是true)。当然,我们可以使用Object.defineProperty(..)
,来添加一个新属性,或者修改一个已有属性
var myObject = {}
Object.defineProperty(myObject,"a",{
value:2,
writable:true,
configurable:true,
enumerable:true,
})
console.log(myObject.a) // 2
-
writable
决定是否可以修改属性的值
var myObject = {} Object.defineProperty(myObject,"a",{ value:2, writable:false, // 此处为false configurable:true, enumerable:true, }) console.log(myObject.a) // 2 myObject.a = 3; console.log(myObject.a) // 2 修改失败!!!
-
enumerable
这个描述符控制的是属性是否会出现在属性枚举中,比如说
for..in
循环中,虽然仍能正常访问他,但是已经无法在枚举中出现var myObject = { a:2, b:3, c:4 } Object.defineProperty(myObject,"a",{ value:2, writable:true, configurable:true, enumerable:false, }) for (key in myObject){ console.log(key,myObject[key]) } // b 3 // c 4
-
configurable
只要属性是可配置的,就可以使用
defineProperty(..)
修改属性描述符也就是说,configurable修改成false是单向操作,无法撤销
除了无法修改之外,还会禁止删除这个属性
不可变性
-
结合
writable:false
和configurable:false
就可以创建一个真正的常量属性 -
可以使用
Object.preventExtensions(..)
禁止一个对象添加新属性并且保留已有属性 -
可以使用
Object.seal(..)
创建一个密封对象,会在一个现有对象上调用Object.preventExtensions
,并且把所有属性标记为configurable:false
,即不能添加新属性也不能重新配置或者修改现有属性(但是可以修改属性的值) -
可以使用
Object.freeze(..)
冻结一个对象,堆在seal的基础上再把所有属性的writable标记为false,这样就无法修改属性的值了但是,这个只是禁止对于对象本身及其任意直接属性的修改(你仍然可以修改对象引用的其他对象)
Getter&Setter
在ES5中,可以使用getter和setter部分改写默认操作,但是只能应用在某个属性上,无法应用在整个对象上
对于访问描述符来说,JS会忽略他们的value和writable,取而代之的是关心get和set
var myObject = {
get a(){
return this._a_;
},
set a(val){
this._a_ = val*2;
},
get b(){
return this._a_*2;
}
}
myObject.a = 3
console.log(myObject.a); // 6
console.log(myObject.b); // 12
可以看到上面的代码中运行结果与我们平时的不太一样,我们设置了a=3,却获取到了a=6,这是因为我们在set中,定义了this._a_ = val*2
转载自:https://juejin.cn/post/7010724727320739847