一. 引用数据类型中的对象
1.1 概述
对象就是一组“键值对”(key-value)的集合,是一种无序的复合数据集合。
var obj = {
name:"zevin",
"age":21,
7:{lost:shanxi,city:yuncheng},
get:funciton(a){
return true;
}
};
上述代码中,大括号就定义了一个对象(对象是以{ }
作为边界),它被赋值给变量obj
,所以变量obj
中保存的引用地址就指向了一个对象。该对象中包含了四个键值对:
-
name,“age”,7,get被称为键名:
js规定对象的所有键名都是字符串(ES6 又引入了 Symbol 值也可以作为键名),所以加不加引号都可以。数字也可以是键名,会被自动转换成字符串。 -
“zevin”,21,{lost:shanxi,city:yuncheng},匿名函数被称为键值:
键值几乎可以是任何类型:字符串,数字,对象,匿名函数等等。
键与值通过“:”隔开,键值对之间通过“,”隔开,最后一个键值对(get)后面的逗号可有可无。
1.2 初始化
1.2.1 字面量
var obj = { };
1.2.2 构造函数模式
使用Object系统内置的对象构造函数,也可以使用自定义构造函数。obj是对象,也被称为实例。Object是类,也被称为构造函数,构造函数是创建对象的模板。
var obj = new Object();
1.3 属性访问
var obj = {
name:"zevin",
1107:21,
gender:"male"
};
1.3.1 点访问
(对象 . 属性名)。数字键名不能使用点访问,必须采用中括号访问。
obj.name // "zevin"
obj.1107 // 报错
1.3.2 中括号访问
如果中括号里直接放键名,那么键名必须放在引号里面,否则会被当作变量处理。当然也可以用变量代理。
数字键名只能使用中括号访问,而且可以不加引号。
obj["name"] // "zevin"
var man = "gender";
obj[man] // male 等价于: obj["gender"] 等价于: obj.gender
obj[1107] // 21
1.4 属性的赋值
点运算符和方括号运算符,不仅可以用来读取值,还可以用来赋值。
var obj = {};
obj.name = "zevin";
obj[1107] = 21;
JS允许随时新增属性,不一定要在定义对象的时候,就定义好属性。(在java对象中,定义对象时就确定了属性不可更改)。
这是正常的默认赋值,后续还可以通过Object.defineProperty()方法添加自定义属性的方式来满足我们更复杂的需求(比如属性不可写)。传送门👇:
【JavaScript笔记(五)】为对象添加自定义属性 —— Object.defineProperty()方法详细解读
1.5 属性的查看
1.5.1 Object.keys
查看一个对象本身的所有属性,可以使用Object.keys
方法。会返回一个数组包含该对象所有可遍历的属性键名。
var obj = {
name:"zevin",
1107:21,
gender:"male"
};
Object.keys(obj); // ['name','1107','gender']
1.5.2 in 运算符
in 运算符用来检查对象是否包含某个属性,无论是自身定义的,还是继承的,只要包含都会返回true;否则返回false。
var obj = {
name:"zevin",
1107:21,
gender:"male"
};
"name" in obj; // true
"age" in obj; // false
"toString" in obj; // true
1.6 属性的遍历(for-in)
依次从obj
中获取属性名赋值给key
,通过obj[key]
访问属性值。
for(var key in obj){
console.log(key,obj[key]);
};
for-in循环还有两点需要注意:
- for-in循环只会遍历输出可遍历(
enumerable = true
)的属性; - 它不仅遍历对象自身的属性,还遍历继承的属性。
举例来说,对象都继承了toString
属性,但是for-in循环不会遍历到这个属性,因为它默认是“不可遍历”的(enumerable = false
)( Object.defineProperty()方法中可设置 )。
如果继承的属性是可遍历的,那么就会被for-in循环遍历到。但是,一般情况下,都是只想遍历对象自身的属性,所以使用for-in的时候,应该结合马上会讲到的hasOwnProperty()
方法,在循环内部判断一下,某个属性是否为对象自身的属性。
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
console.log(obj,obj[key]);
}
}
1.7 属性的删除(delete)
代码格式为:delete 对象 . 属性
-
delete
命令用于删除对象的属性,删除成功后返回true
。删除成功后该属性就不可访问,而且Object.keys
方法的返回值也不再包括该属性。
var obj = {
name:zevin
};
Object.keys(obj) // ["name"]
delete obj.name // true
obj.name // undefined
Object.keys(obj) // [ ]
- 如果删除的是一个不存在的属性,不会报错反而返回
true
。因此,不能根据delete
命令的结果,判断某个属性是存在的。
var obj = {};
delete obj.name // true
- 只有当该属性存在,并且是不得删除的,
delete
命令才会返回false
。 -
delete
命令只能删除对象本身的属性,无法删除继承的属性。
var obj = {};
delete obj.toString // true
obj.toString // [Function: toString]
上面代码中,toString
是对象obj
继承的属性,虽然delete
命令返回true
,但该属性并没有被删除,依然存在。这个例子还说明,即使delete
返回true
,该属性依然可能读取到值。
1.8 对象序列化
将内存中的对象转化成字符串的描述,解决对象在io流中的传输。
在网络当中传输时,只允许传输字符或者字节流。具体的有两种交互方式:json字符串和qs查询字符串。
具体的序列化方法:
- 转换成JSON字符串:
console.log(JSON.stringify(obj)); // {"name":"zevin","age":21,"gender":"male"}
- 转化成qs查询字符串 :
在node.js环境下适用。
var qs = require("querystring");
console.log(qs.stringify(obj)); // name=zevin&age=12&gender=male
二. Object对象
Object作为根构造函数,其他所有的构造函数(Number,Boolean,String,Array等等)都直接间接的继承Object。
Object对象的原生方法分成两类:
- Object本身的方法;
- Object的实例方法;
这一章主要讲的是第一点:Object本身的方法,也就是直接定义在Object对象的方法,也称静态方法。注意,静态方法只能Object构造函数本身调用,它的实例对象是不能调用的,它们能调用的是第二点:Object的实例方法(下一章节介绍)。
2.1 Object()作为构造函数
我们常常把Object()用作构造函数,新生成一个对象。这种写法与下边字面量的写法等价。
var obj = new Object();
var obj = {};
2.2 Object()作为工具函数
Object()本身也可以是一个工具函数,它可以将任意值转为对象。如果参数为空(或者为undefined
和null
),Object()返回一个空对象。
var obj = Object(); // {}
var obj = Object(undefined); // {}
var obj = Object(null); // {}
obj instanceof Object // true
instanceof
运算符用来验证,一个对象是否为指定的构造函数的实例。
如果参数是基本类型,那么Object()方法会将其转换成对应构造函数的实例。
var obj = Object(1);
// 等价于:var obj = Number(1);
obj instanceof Object // true
obj instanceof Number // true
obj.valueOf() // 1
Object.keys(obj) // []
1 + obj // 2
但是如果参数本来就是一个对象,那么它就不会再做任何转换直接返回该对象。利用这一点,可以写一个判断变量是否为对象的函数。
function isObject(value) {
return value === Object(value);
};
isObject([]) // true
isObject({}) // true
isObject(function(){}) // true
isObject(true) // false
还有一点:大家有没有觉得Object作为构造函数和工具函数其实用法和作用很类似,比如:
var obj1 = Object(1);
var obj2 = new Object(1);
obj1 == obj2 // false ?
obj1 === obj2 // false ?
obj1 // [Number: 1]
obj2 // [Number: 1]
obj1.valueOf() // 1
obj2.valueOf() // 1
typeof obj1 //'object'
typeof obj2 //'object'
看似一样,但是Object(1)
与new Object(1)
两者的语义是不同的,Object(1)
表示将1转成一个对象,new Object(1)
则表示新生成一个对象,它的值是1。
2.3 Object 的静态方法
2.3.1 Object.keys(),Object.getOwnPropertyNames()
Object.keys方法在上边已经说过啦,这里再给大家介绍一下他的“好兄弟”——Object.getOwnPropertyNames()。他们的作用都是用来遍历对象的属性的:接受一个对象作为参数,返回一个数组,包含了该对象自身的所有属性名。对于一般的对象来说,它们的返回结果是一模一样的。
var obj = {
name:"zevin",
1107:21,
gender:"male"
};
Object.keys(obj);
// ['name','1107','gender']
Object.getOwnPropertyNames(obj);
// ['name','1107','gender']
他俩唯一的区别就是:Object.keys方法只遍历可枚举属性;Object.getOwnPropertyNames()方法还返回不可枚举的属性名。比如面对数组对象时:
(数组中的length属性是不可枚举属性)
var arr = ["zevin",21,"male"];
Object.keys(arr);
// ['0','1','2']
Object.getOwnPropertyNames(arr);
// ['0','1','2','length']
2.3.2 其他静态方法
Object 的静态方法其实还有很多很多,下一篇中来详细说明Object.defineProperty()方法,其他的之后的等到原型链,继承那一块再详细说。这里就先罗列一下。
对象属性模型的相关方法
- Object.getOwnPropertyDescriptor():获取某个属性的描述对象。
- Object.defineProperty():通过描述对象,定义某个属性。
- Object.defineProperties():通过描述对象,定义多个属性。
控制对象状态的方法
- Object.preventExtensions():防止对象扩展。
- Object.isExtensible():判断对象是否可扩展。
- Object.seal():禁止对象配置。
- Object.isSealed():判断一个对象是否可配置。
- Object.freeze():冻结一个对象。
- Object.isFrozen():判断一个对象是否被冻结。
原型链相关方法
- Object.create():该方法可以指定原型对象和属性,返回一个新的对象。
- Object.getPrototypeOf():获取对象的Prototype对象。
三. Object.prototype的实例方法
由于JavaScript 的所有其他对象都继承自Object对象(即那些对象都是Object的实例),所以它们都可以调用这一章节讲的Object的实例方法。这些方法用处非常大,使用频率也非常高,受众面也很广。
(Object.prototype的实例方法其实有很多,这里只介绍常见的6个吧,还有一些有关继承的方法放到继承那块详细再说。)
3.1 Object.prototype.constructor
检测创建这个对象的构造函数是什么。
var obj = {};
console.log(obj.constructor); // [function:Object] 说明该对象的构造函数是Object
3.2 Object.prototype.valueOf()
返回当前对象对应的值,默认情况下返回对象本身。
var obj = {
name:"zevin",
gender:"male"
};
console.log(obj.valueOf()); // {name:"zevin",gender:"male"}
obj.valueOf() === obj // true
常用于JavaScript 的自动类型转换,会默认调用这个方法。所以我们可以自定义该方法,达到我们的需求。例如:我们需要obj在参与运算的时候相当于数字 2 :
var obj = new Object();
obj.valueOf = () => 2;
1 + obj // 3
3.3 Object.prototype.toString()
将对象转换成字符串的描述,默认转换成[object,Object]
的形式。
var obj = new Object();
console.log(obj.toString()); // [object,Object]
[object,Object]
单单一个字符串并没有什么实际作用,我们仍然可以使用上述的自定义方法:
var obj = new Object();
obj.toString = () => "hello";
console.log(obj + "world"); // "helloworld"
由于所有的构造函数都直接或间接的继承Object,所以之前的数据类型(Number,String,Function,Array,Date)都可以调用toString
方法,JS都系统自定义了toString
方法:
(123).toString() // "123"
'123'.toString() // "123"
[1,2,3].toString() // "1,2,3"
(function () {
return 123;
}).toString()
// "function () {
// return 123;
// }"
(new Date()).toString()
// Mon Aug 03 2020 10:33:04 GMT+0800 (GMT+08:00)
虽然这些数据类型有系统自定义的toString
方法,默认会覆盖掉Object.prototype.toString()
方法。但是并不代表他们就不能调用原型实例方法了,同时配合函数的call方法可以达到类似typeof
运算符的效果:
Object.prototype.toString.call(1) // "[object Number]"
Object.prototype.toString.call('') // "[object String]"
Object.prototype.toString.call(true) // "[object Boolean]"
Object.prototype.toString.call(undefined) // "[object Undefined]"
Object.prototype.toString.call(null) // "[object Null]"
Object.prototype.toString.call(Math) // "[object Math]"
Object.prototype.toString.call({}) // "[object Object]"
Object.prototype.toString.call([]) // "[object Array]"
3.4 Object.prototype.toLocaleString()
toLocaleString()方法大部分时候的返回值都与toString()方法相同,但是也有例外,比如日期对象的两者返回值就不同:
(123).toString() // "123"
(123).toLocaleString() // "123"
'123'.toString() // "123"
'123'.toLocaleString() // "123"
[1,2,3].toString() // "1,2,3"
[1,2,3].toLocaleString() // "1,2,3"
(new Date()).toString()
// Mon Aug 03 2020 10:33:04 GMT+0800 (GMT+08:00)
(new Date()).toLocaleString()
// 2020-8-3 10:50:19
3.5 Object.prototype.hasOwnProperty()
判断某个属性值是否是属于当前实例自己的。(toString是继承来的,并不属于他自己)
var obj = {
name:"zevin"
};
console.log(obj.hasOwnProperty("name")); // true
console.log(obj.hasOwnProperty("toString")); // false
3.6 Object.prototype.propertyIsEnumerable()
判断属性是否是可枚举的——for-in循环可遍历或者Object.keys可例举的。默认赋值的属性都是可枚举的。不过我们可以通过Object.defineProperty()方法添加自定义属性或修改已有属性。
var obj = {
name:"zevin"
};
console.log(obj.propertyIsEnumerable("name")); // true
console.log(obj.propertyIsEnumerable("toString")); // false
本来打算在本文末介绍Object.defineProperty()方法,可奈何它越写越多,字数直逼一万大关🐶
索性我就把它独立出去了,哈哈哈~ 传送门👇:
【JavaScript笔记(五)】为对象添加自定义属性 —— Object.defineProperty()方法详细解读