对象——《你不知道的js(上卷)》读书笔记(六)

写在前面

在上一篇笔记中,我们介绍了不同的调用位置this会绑定不同的对象,那么,什么是对象呢?
在这里我们介绍一下JS的对象是个什么

创建一个对象

// 字面量创建方式
var obj1 = {
	key : value ,
	...
}
//构造函数创建
var obj2 = new Object();
obj2.key = value;

这两种方式创建的对象基本一样,他的不同点是字面量声明可以一次声明多个健值对,但是构造方式每次质嫩个设置一个。

value可以是任意js支持的类型,关于类型的介绍在这篇文章里边哦。

存取对象的值

obj1[key]
obj1.key 

我们可以通过以上两种方式获取对象的值,他们基本上一样,但是在细节上还是有一定的区别。

采用obj.key的方式获取属性值时,必须时合法的标识符,比如obj.a-123!! ,由于a-123!!不是一个合法的标识符,因此需要采用key[‘a-123!!’]来获取属性的值。

在方括号获取值时,这里的key永远会转成一个字符串来使用。此外采用括号的形式还可以用表达式的方式来动态计算属性名。例如:obj[name+‘str’]

对象属性的Get和Put

在获取对象的属性值时,其实是调用了底层的get方法,获取对象的属性,如果找不到,就会沿着prototype继续查找,如果找到了顶层依然没有找到,那么get的值会返回undefined。

这种规则与获取一个变量有细微的区别,如果变量找到顶层依然没有找到,会会抛出一个reference异常。

如果属性值本身被赋值为了undefined,怎么区分呢?参考本文最后一节,属性的存在性~

再来说说put。也就是我们给对象的属性设置一个值时,不仅仅是一个setter这么简单。他的底层还会判断是否是访问描述符、他的writable属性是否是可写。如果是否,要判断是否是严格模式再进一步判断,最后才考虑修改属性的值。

数组存取的小细节

数组作为对象的一种,但是他的存取与普通对象的存取还是有一些差别。

差别一:尽管我们可以把数组当成普通的对象去使用,但是如果数组的键不是数字,他的length不会增加。

var arr = ['a','b','c'];
arr.d = 'd'
arr.length; // 3
arr.d // d

差别二:如果我们设置值时的键没有使用数字,数组底层会优先转成数字进行存储,这个时候length就会发生变化~

var arr = ['a','b','c'];
arr['3'] = 'd'
arr.length; // 4
arr[3] // d

复制对象

由于对象的底层是指针,他是存在栈中的地址指向了真正的对象。

根据对象的复制的程度,也就是你究竟想要的是他的值还是他的地址,可以将对象的复制分为深复制和浅复制,也称为深拷贝和浅拷贝。

当然这样表述是不完全准确的。

属性描述符

属性描述符包括:值、可写、可枚举、可配置~

  • value:值
  • writable:决定是否可以修改value,如果为false,则表示不可以修改属性的值,强行修改的话,普通模式修改失败,严格模式,会报错TypeError
  • configurable:可配置的,即是否可以修改属性描述符。defineProperty(…)可以修改属性的这几种配置项。当然你要注意,configurable一旦配置成false,就无法修改属性描述,也不能删除当前属性了~当你要修改时,无论什么模式都会报错TypeError。有一个小小的例外,即便configurable配置成false,也可以将writable的true改成false。
  • enumerable:控制属性是否会出现在对象的属性枚举中。

对象的不变性

有时候我们需要一个对象的属性值是不可变的,就算是属性值为对象的引用,也要控制他的不变性。

这个时候我们可以参考以下的方法:

对象常量控制不变性

结合writable和configurable就可以创建一个真正的常量属性,这意味着它不可以被更改、重定义、删除。

var myobj = {};

Object.defineProperty(myobj,'MAX_COUNT',{
	value:10,
	writable:false,
	configurable:false
	
})

通过禁止扩展创建常量属性

这种方式可以禁止一个对象添加新属性,保留之前的属性。

var myobj = {a:2};
Object.preventExtensions(myobj)
myobj.b = 3
myobj.b //undefined

非严格模式:静默失败;严格模式:TypeError

通过密封创建常量属性

var myobj = {a:2};

Object.seal(myobj)//密封对象
//等价于下面的代码 先禁止扩展后设置可配置属性为false
Object.preventExtensions(myobj)
Object.defineProperty(myobj,'a',{
	configurable:false
})

这种方式不能添加新属性,不能重新配置或者删除现有的属性。
这种方式可以修改属性的值~

冻结属性值

var myobj = {a:2};

Object.freeze(myobj)//冻结对象
//等价于下面的代码 先冻结后设置可写属性为false
Object.seal(myobj)
Object.defineProperty(myobj,'a',{
	writable:false
})
//也等价于下面的代码,先禁止扩展后设置可配置和可写属性为false
Object.preventExtensions(myobj)
Object.defineProperty(myobj,'a',{
	configurable:false,
	writable:false
})

如果你想深度冻结,就需要遍历咯~

属性的存在性

考虑一个这样的问题。如果一个对象的属性值被我们手动赋值成undefined,当通过属性访问时,就是undefined,但是如果一个属性值从未被定义和赋值。访问时依然是undefined,这两个要怎么区分呢?

这个时候我们往往用key in 来判断,这个时候如果自有属性没有的话就要去原型链上找。

如果我们不希望去原型链上找,可以采用obj.hasOwnProperty(key)来判断。

这两种方式都可以。

// 这里是一些注意点
4 in [2,3,4]  //false

var obj = Object.create(null);
obj.hasOwnProperty(key) //报错
Object.prototype.hasOwnProperty.call(obj,key) //正确

这里的in操作符要与for in 属性便利区分开来,in 操作符是判断存在性, 而for in 是判断枚举性。

枚举

关于枚举的几个方法:

obj.propertyIsEnumerable(key) //判断一个属性是否可枚举且直接存在对象中,而不是原型链
Object.keys(obj) //返回一个可枚举的属性数组
Object.getOwnPropertyNames() //返回所有的属性值,不论是否可以枚举
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值