语法
- 文字语法
var obj = {
key: value
}
- 构造语法
var obj = new Object() // new Object、Object
obj.key = value
类型
一般而言就六种语言类型:string、boolean、number、null、undefined、object
,还有ES6新增的symbol
(符号)
若是忽视typeof null === 'object'
(js多年的bug,null的二进制全为0,而二进制的前三位为0的话会被判为对象)的话,那么JavaScript万物皆对象
内置对象
JavaScript提供的一些函数:String、Number、Boolean、Function、Date、Error、RegExp、Array、Object
null、undefined
是没有构造语法的,Date、Error
则只有构造语法,俩者兼得的话其文字语法可以直接访问属性和方法(会自行处理成对应包装对象)
内容
对象的内容(value)一般而言不会存储在对象容器的内部。存储在对象内部的是(key),它们就像指针(其实是引用,js没有指针)一样指向内容真正的存储位置(就键访问和属性访问)
使用属性访问的话,属性名会被
toString
- 可计算属性名
var prefix = 'key'
var obj = {
[prefix + '1']: value
}
一般而言主要用于Symbol
- 属性与方法
在Java之类的一些语言中,属于对象的函数通常称之为方法,所以若是访问的是一个函数,那么属性访问也叫方法访问,该方法(函数)和普通函数没什么区别,除了this的绑定有点影响
函数不可能属于一个对象(都是引用),所以不能称之为方法
- 数组
其实数组就是个普通对象,只是内置了一下有了数组的特性,有点像当年C语言实现数组功能一样。
它正常的key也就是期望的key应该是数值(自然数),只要是符合这个的,length会自动++。key值类自然数的话也是会自行转成期望的下标。delete
操作的话,length不会变化 - 复制对象
这是个很复杂的问题。深拷贝、浅拷贝(和拷贝深度无关)。还有发生的死循环(循环引用)
- JSON安全(就是看起来像JSON的,说的全面点就是可以被序列化(看最下文)一个JSON字符串,然后根据这个字符串可以解析出一个结构和值一样的对象)的对象可以序列化,然后反序列化就完成了复制
- ES6的Object.assign可以完成浅拷贝
- 自行实现
function deepClone(source) {
if (typeof source === 'object') {
const targteObj = source.constructor === Array ? [] : {}
for (const key in source) {
if (source.hasOwnProperty(key)) {
if (source[key] && typeof source[key] === 'object') {
targteObj[key] = deepClone(source[key])
} else {
targteObj[key] = source[key]
}
}
}
return targteObj
} else {
return source
}
}
- 属性描述符(数据描述符)
Object.getOwnPropertyDescriptor
,获取属性描述
-
Writable
:是否可以修改属性值(value),若是false
的话,在严格模式下会报TypeError
。其实和setter为空一样,只是要抛出TypeError -
Configurable
:是否可以使用defineProperty修改属性描述符,若是false
的话会报TypeError
。不可逆。
有个例外,configurable: false时还是可以把writable从true改为false
configurable: false会禁止删除(delete)这个属性
delete不能直接释放内存,它只是在delete的属性没有引用时释放,内存管理通过断开引用来间接完成内存释放
-
enumerable
:属性是否可被枚举(for in循环),值得注意的是。for、for of、forEach等数组操作不影响
- 不变性
- 对象常量(不可修改、才得以或者删除):
writable: false、configurable: false
,可以创建一个常量属性
var obj = {}
Object.defineProperty(obj, 'key', {
value: 'value',
writable: false,
configurable: false
})
- 禁止扩展(不可添加新属性,保留现有属性):
Object.preventExtensions(obj)
- 密封(禁止扩展+
configurable: false
):Object.seal(obj)
- 冻结(密封+
writable: false
):Object.freeze(obj)
不可变仅作用在对象本身以及其直接属性,至于引用的其它对象是无效的
var obj = {
num: 1,
arr: [1]
}
Object.freeze(obj)
obj.num = 2
console.log(obj.num) // 1
obj.arr.push(2)
console.log(obj.arr) // [1, 2]
- [[Get]]
var obj = {
num: 1
}
obj.num // 2
obj.num是在obj上实现了[[Get]]操作(类似函数调用:[[Get]])。对象默认的[[Get]]操作会先在对象中查找是否具有这个属性,有的话就返回,没有的话proto继续找。实在找不到返回undefined。比较尴尬的是,若是这个属性就是undefined,就不能区分到底是哪种情况返回的undefined
- [[Put]]
并不仅仅触发[[Put]]来设置或者创建属性。实际上取决很多因素,最大的因素是对象之中是否存在这个属性
- 属性是否存在访问描述符?如果存在setter就调用setter
- 如果writable: false,在非严格模式下不生效,在严格模式下报
TypeError
- 如果不是以上俩情况,就设置值
如果不存在这个值的话就复杂了
- Getter和Setter
若是给一个属性定义了这俩个至少一个,这个属性就会定义为访问描述符。这时候就会忽略value和writable,而是关心set和get(以及configurable和enumerable)
一般而言俩者都定义才好
// 俩种定义方式
var obj = {
get a() {
return 2
}
}
Object.defineProperty(obj, 'b', {
get() {
return this.a * 2
}
})
obj.a // 2
obj.b // 4
- 存在性
判断属性是否存在,即obj.a === undefined
是哪种情况
var obj = {
a: 2
}
// 会检查整个原型链
'a' in obj // true
'b' in obj // false
// 只会检查对象本身
obj.hasOwnProperty('a') // true
obj.hasOwnProperty('b') // false
// Object.create(null),这种情况下的对象不能链接到Object.prototype,自然不能调用hasOwnProperty方法。可用call
- 枚举:值得注意的是,
enumerable: false
虽然不会出现在for in
循环内,不过还是可以用in来判定属性是否存在。
var obj = {
a: 2
}
// 以下均只作用于本对象。不作用于原型链
// 判断属性是否存在本对象之中,且enumerable: true
obj.propertyIsEnumerable('a') // true
// 返回一个包含所有可枚举属性的数组
Object.keys(obj) // ['a']
// 返回一个包含所有属性的数组
Object.getOwnPropertyNames(obj) // ['a']
遍历
-
for in
:遍历对象可枚举属性 -
for
:遍历数组下标(其实也是属性) - forEach、every(一直运行直到返回false)、some(一直运行直到返回true)
-
for of
:直接遍历值(必须本身定义了迭代器(@@iterator))
// 数组
var arr = [1, 2, 3]
var it = arr[Symbol.iterator]()
it.next() // {value: 1, done: false}
it.next() // {value: 2, done: false}
it.next() // {value: 3, done: false}
it.next() // {value: undefined, done: true}
// 对象
var obj = {
key0: 1,
key1: 2,
key2: 3
}
Object.defineProperty(obj, Symbol.iterator, {
writable: false,
enumerable: false,
configurable: false,
value: function() {
var o = this
var idx = 0
var ks = Object.keys(o)
return {
next: function() {
return {
value: o[ks[idx++]],
done: idx > ks.length
}
}
}
}
})
var it = obj[Symbol.iterator]()
it.next() // {value: 1, done: false}
it.next() // {value: 2, done: false}
it.next() // {value: 3, done: false}
it.next() // {value: undefined, done: true}
// for of循环
for (var v of obj) {
console.log(v)
}
序列化
对象其实就是字节,字节存储的是对象的数据信息、类型以及存储在其内的数据信息
var obj = {
key: value
}
就比如这个对象,它内容是存储在内存地址中,obj存储的只是这个内存地址的映射(引用),断电了自然就没了。那么就需要把它转成字符串,就可以存储在硬盘之上。服务器的JSON也是序列化之后传输到前端,然后反序列化就好了。

网友评论