数据类型
ECMAScript 规定变量值可以分为两种类型的数据:一个是基本类型值(原始值),一个是引用类型值(引用值)。基本类型值有六种:String、Number、 Boolean、Undefined、Null和 Symbol(ES6)。引用类型值只有一种,就是是由多个值构成的对象。
什么是对象
对象是无序的属性和方法的集合,对象它被认为是某个特定引用类型的实例,在 ECMAScript 中,引用类型是一种数据结构,它描述了某类对象所具有的属性和方法,(因此引用类型也可以理解为对象的类型/分类)。虽然引用类型有点像类,但跟类并不是一个概念。
常用的引用类型有Object、Array、Date、Function、RegExp、Error、Boolean、Number、String。我们可以通过new操作符+构造函数来创建某一类型的对象,构造函数就是用来创建对象的函数,比如 new + Date 就创建了一个日期对象,同理有数组对象,函数对象,正则对象,布尔对象,数字对象,字符串对象,错误对象等等。
因此在JS中,除了六个基本类型值外,其他全都是对象。
ECMAScript引用类型的区分(对象的类型的区分)
引用类型有Object、Array、Date、Function、RegExp、Error、Boolean、Number、String,他们大部分是构造函数,由于函数也是对象,因此这些引用类型也是对象了。
有两种引用类型比较特殊,分别是基本包装类型和单体内置对象。
基本包装类型(原始值包装类型)
为了便于操作基本类型值,ECMAScript 还提供了 3 个特殊的引用类型:Boolean、Number 和 String。每当用到某个基本类型值的方法或属性时,都会自动创建一个相应基本包装类型的实例,从而可以调用该实例的各种属性和方法。
单体内置对象
前面的引用类型多是构造函数,但是单体内置对象是已经实例化了,可直接使用。ECMAScript定义了几个单体内置对象,有Global 和 Math 和 JSON。
JS对象的分类
(前面是ECMAScript引用类型的分类,这个是JS对象的分类。)JS对象可以分为内置对象、宿主对象和自定义对象。
内置对象
内置对象的定义:任何由 ECMAScript实现提供、与宿主环境无关,并在代码开始执行时就存在的对象。这就意味着,开发者不用显式地实例化内置对象,因为它们已经实例化好了。比如Object、Array等虽然是构造函数,但也是Function的实例,所以也是函数对象,而且他们身上有很多方法可直接使用。
例如:Object、Array、Date、Function、RegExp、Error、EvalError 、RangeError 、ReferenceError 、SyntaxError 、TypeError、URIError 、Boolean、Number、String、Global、Math、JSON都属于内置对象。
宿主对象
由宿主提供的对象就是宿主对象,宿主就是JS
运行环境,JS可以在浏览器中运行,也可以在node服务器上运行,那么浏览器就是宿主。所以浏览器提供的对象就是宿主对象。例如:window、location、history、screen、navigator、document都是宿主对象,所有的DOM
和BOM
对象都属于宿主对象。
自定义对象
开发人员自己定义的对象。
属性名注意点
一个对象里面,他的属性名只有两种情况,一种是字符串,一种是符号symbol(了解),如果你给的属性名不是字符串,那他会转成字符串,如obj[10 ]实际上属性名是 '10',只是js并没有用引号包裹起来而已。
let obj = {};
obj[10] = 1;
obj['10'] = 11;
obj[{}] = '对象'
console.log(obj)
结果: {
10: 11,
[object Object]: "对象"
}
属性的特性
JS有一些内部特性用来描述属性的特征,分别是
- Enumerable:是否可枚举(是否可被Object.keys()或者for..in遍历出来)---共有
- Configurable:是否可被delete删除,是否可修改特性 ---共有
- Writable:属性值是否可被修改 ---数据属性独有
- Value:存放属性值,默认值为 undefined ---数据属性独有
- Get:获取函数,读取属性调用 ---访问器属性独有
- Set:设置函数,写入属性调用 ---访问器属性独有
属性的类型
根据内部特性,可以将属性分为数据属性和访问器属性。显式的给对象添加属性,并且几个特性都是true。使用Object.defineProperty添加属性,不指定特性则默认都是false或者undefined。
要修改属性的默认特性,就必须使用 Object.defineProperty() 方法。这个方法接收 3 个参数:
要给其添加属性的对象、属性名和一个描述符对象,描述符对象包括上面六个特性,数据属性和访问器属性各四个,Writable、value和Get、Set不能同时存在。有Writable和Value就为数据属性,有Get和Set为访问器属性。
let obj = { age: 26 }; //显式添加属性
Object.defineProperty(obj, 'name', { value: 'zjh', writable: true });
console.log(Object.getOwnPropertyDescriptor(obj, 'age'));
console.log(Object.getOwnPropertyDescriptor(obj, 'name'));
console.log(Object.keys(obj));
//显式添加
{
"value": 26,
"writable": true, //默认都是true
"enumerable": true,
"configurable": true
}
//Object.defineProperty
{
"value": "zjh",
"writable": true,
"enumerable": false, // 默认都是false
"configurable": false
}
//name不会被遍历出来
[
"age"
]
访问器属性的典型使用场景,就是设置属性值可以使其他属性发生变化。
(访问器属性仅了解,具体需要可看书)
let obj = { age: 26 };
let num = 100;
Object.defineProperty(obj, 'score', {
get() {
console.log('get');
return num; //直接拿外部的num当返回值,也就是修改num,score也变了
},
set(n) {
console.log('set');
num = n;
}
})
obj.score //调用get
obj.score = 90; //调用set
// 核心: Object.defineProperty创建的访问器属性让外面的变量num和对象产生了关系
// 调用getter,实际就是拿num返回给score,调用setter,实际上就是修改num
可枚举属性的遍历方法
for...in... 和 Object.keys() 都只能遍历可枚举属性,他们的区别在于 for..in..还能遍历原型链上的可枚举属性。
let person = {
name: '周嘉宏',
sex: '男',
}
Object.defineProperty(person, 'age', {
value: 18,
})
person.__proto__.type = '人类';
for (key in person) {
console.log(key); // 'name' 'sex' 'type'
}
console.log(Object.keys(person)); // ['name', 'sex']
for..in..和 object.keys 都不能遍历出 age 这个不可枚举属性.
同样,Object.values 也只能遍历可枚举属性的值
console.log(Object.values(person)); // ['周嘉宏', '男']
不可枚举属性的遍历方法
Object.getOwnPropertyNames可以遍历出不可枚举属性
let person = {
name: '周嘉宏',
sex: '男',
}
Object.defineProperty(person, 'age', {
value: 18,
})
person.__proto__.type = '人类';
console.log(Object.values(person)); ['name', 'sex']
console.log(Object.getOwnPropertyNames(person)); ['name', 'sex', 'age']
只有 for..in..能遍历原型链上的可枚举属性。只有Object.getOwnPropertyNames可以遍历自身的不可枚举属性。
属性简写
当属性值是一个变量的时候,并且和属性名相同的时候,就可以使用属性简写形式
let name = 'zjh';
let obj = {
name: name
}
let obj1 = {
name
}
console.log(obj);
console.log(obj1);
下面这中情况不能使用简写形式,属性值不是变量的时候
let obj = {
name1: 'name1' //虽然属性值是字符串name1,但他不是变量
}
let obj1 = {
name1 //报错,引用错误name1
}