JS 中的数据类型共有七种:number、string、bool、symbol、undefined、null、object
其中,对象 object 是唯一的复杂类型,而其他六种类型都是简单类型。
对象的语法
对象是一组 “键值对”(key-value)的集合,是一种无序的复合数据集合。其写法如下:
let obj = {'name': 'jack', 'age': 18} //简单写法
let obj = new Object({'name': 'jack'}) //规范写法
console.log({'name': 'jack', 'age': 18}) //匿名对象
属性名:每个 key(键)都是对象的属性名(property)
属性值:每个 value 都是对象的属性值。
关于键名(key)的几个细节:
-
键名是字符串,不是标识符,可以包含任意字符(包括
''、' '、数字、中文、表情符号等)。 -
引号可以省略,省略之后就只能写标识符,如果键名不符合标识名的条件且也不是数字,则必须加上引号,否则会报错。
-
就算引号省略了,键名也还是字符串。(很重要)
let obj1 = {
'': 1,
' ': 2,
'中文': 3,
'😀': 4,
'77name': 'jack'
}
let obj2 = { 1: 111 } //没有数字下标,键名只有字符串
Object.keys(obj2) //得到 obj2 的所有 key:["1"]
一个值得注意的例子:
{ foo: 'a' } // "a",在 Firefox 中可以验证
上面代码表示:代码块中有一个名为 foo 的标签(label),其内容为"a",它不是一个对象,而是一个代码块(block)。
奇怪的属性名
在 JavaScript 中,所有的属性名会自动转为字符串
let obj = {
1: 'a',
3.2: 'b',
1e2: true, //先把 1e2 变成数字100,再变成'100'
1e-2: true,
.234: true,
0xFF: true //十六进制,自动换算成数字255,然后变成"255"
};
obj;
// Object {
// 1: "a",
// 3.2: "b",
// 100: true,
// 0.01: true,
// 0.234: true,
// 255: true
// }
Object.keys(obj); //["1", "3.2", "100", "0.01", "0.234", "255"]
变量作属性名
用变量做属性名的写法:[变量名]: 值
不加[]的属性名会自动变成字符串,加了[]则会当做变量求值,求出的值如果不是字符串,则会自动变成字符串。
let p1 = 'name'
let obj = { p1: 'jack' } //这样写,属性名为 'p1'
let obj = { [p1]: 'jack' } //这样写,属性名为 'name'
对象的增删改查
删除对象的属性
delete 操作符用于删除对象的某个属性。使用 delete obj.xxx 或 delete obj['xxx'] 删除 obj 的 xxx 属性,删除成功后返回 true。
let obj = { name: 'jack', age: 18 }
obj.name = undefined
obj // { name: undefined, age: 18 },删除的是属性值 'jack'
delete obj.name // true
obj // { age: 18 },删除的是属性 name
delete obj['name'] // true,删除一个不存在的属性,delete 不报错,而且返回 true
注意区分 属性值为 undefined 和 不含属性名。
没有就是没有,undefined 就是 undefined,不能含糊。
属性是否存在:in 运算符
in 运算符用于检查对象是否包含某个属性(检查的是键名,不是键值),如果包含就返回 true,否则返回 false。
let obj = { age: 18 }
'name' in obj //false,name 不是 obj 的属性名
'age' in obj //true,age 是 obj 的属性名
含有属性名,但是值为 undefined 的表示方法:
'xxx' in obj && obj.xxx === undefined
注意 obj.xxx === undefined 不能断定 'xxx' 是否为 obj 的属性,而是用 in 判断。
let obj1 = {}
let obj2 = { x: undefined }
obj1.x === undefined //true
obj2.x === undefined //true
'x' in obj //false
'x' in obj2 //true
查看对象的属性(读属性)
1. 查看自身所有属性 Object.keys(obj)
2. 查看自身 + 共有属性 console.dir(obj)
3. 判断一个属性是自身的还是共有的 obj.hasOwnProperty('toString')
let obj = { name: 'jack', age: 18 }
Object.keys(obj) //["name", "age"],查看属性名 key
Object.values(obj) //["jack", 18],查看属性值 value
Object.entries(obj) //查看 key 和 value
console.dir(obj) //以目录的形式打印出所有自身和共有属性
'toString' in obj //true,使用 in 不能区分一个属性是自身的还是共有的
obj.hasOwnProperty('toString') //false,说明 toString 是共有属性
obj.hasOwnProperty('name') //true,说明 name 是自身属性
4. 查看某一属性
- 中括号语法
obj['key'](优先使用) - 点语法
obj.key - 坑新人语法
obj[key](key 是一个变量,值一般不为 'key')
let obj = {name: 'jack', age: 18 }
obj['name'] //"jack"
obj.name //"jack"
obj['na' + 'me'] //"jack"
obj[name] //undefined,此时 name 为变量,要先求 name 的值
name //"",name 的值为 ''
window.name = 'age' //假如将 name 的值设为 'age'
obj[name] //18
obj[console.log('name')] //undefined
上面代码中,console.log('name') 表达式的值为 undefined。
obj[console.log('name')] 等价于 obj['undefined'],而 obj 没有 undefined 这个属性。
5. 区分 obj.name 与 obj[name]
关于 obj.name
-
obj.name等价于obj['name'] -
obj.name不等价于obj[name] -
这里的 name 是字符串,而不是变量。
关于 obj[name]
-
let name='jack' -
obj[name]等价于obj['jack'] -
而不是
obj['name']和obj.name -
这里的 name 是变量
必须要搞清楚的思考题:
let list = ['name', 'age', 'gender']
let person = {
name: 'jack',
age: 18,
gender: 'man'
}
for (let i = 0; i < list.length; i++) {
let name = list[i]
console.log(person__???__)
}
上面代码中的 console 语句应如何填写,使得 person 的所有属性被打印出来?
选项1:console.log(person.name) 选项2:console.log(person[name])
修改或增加对象的属性(写属性)
修改或增加自身属性
1. 直接赋值
let obj = { name: 'jack' }
obj.age = 18
obj.name = 'tom' //name 是字符串
obj['name'] = 'tom'
obj['na' + 'me'] = 'tom'
//若写成 obj[name] = 'tom' 是错误的,这里的 name 是变量,值不确定,可能是 'name' 或 'age'
let key = 'name'
obj[key] = 'tom'
//若写成 obj.key = 'tom' 是错误的,obj.key 等价于 obj['key']
2. 批量赋值 Object.assign(ES 6 新增 API)
Object.assign(obj, { p1: 1, p2: 2, p3:3, p4: 4, p5: 5})
obj //{name: "tom", age: 18, p1: 1, p2: 2, p3: 3, p4: 4, p5: 5}
修改或增加共有属性
- 无法通过自身修改或增加共有属性
let obj1 = {}, obj2 = {} //共有 toString
obj1.toString = 'xxx' //只是在改 obj1 自身属性
obj2.toString //还是在原型上
- 如果偏要修改或增加原型上的属性
obj.__proto__.toString = 'xxx' //不推荐使用__proto__,会影响性能
Object.prototype.toString = 'xxx'
一般来说,不要修改原型,会引起很多问题。
修改隐藏属性
- 不推荐使用
__proto__
let obj = { name: 'jack' }
let obj2 = { name: 'tom' }
let common = { kind: 'human'}
obj.__proto__ = common
obj2.__proto__ = common
- 推荐使用
Object.create
let common = { kind: 'human' }
let obj = Object.create(common) //以 common 为原型创建 obj
obj.name = 'jack'
let obj2 = Object.create(common) //以 common 为原型创建 obj2
obj2.name = 'tom'
一般来说,不要修改原型,会引起很多问题。
'xxx' in obj 和 obj.hasOwnProperty('xxx') 的区别
'xxx' in obj
in 运算符用于判断对象 obj 是否包含 xxx 属性,但不能区分属性 xxx 是自身的还是从原型链上继承的。
obj.hasOwnProperty('xxx')
obj.hasOwnProperty('xxx') 用于指定对象 obj 的自身属性中是否具有 xxx 属性。
- 返回
true,说明xxx是obj的自身属性 - 返回
false,说明xxx是obj的继承属性(共有属性)。
let obj = { name: 'jack', age: 18 }
'name' in obj //true
'toString' in obj //true
obj.hasOwnProperty('name') //true,name 是自身属性
obj.hasOwnProperty('toString') //false,toString 是继承属性