【JavaScript笔记 · 基础篇(四)】Object全家桶(引用数据类型中的对象 / Object对象 / Object.prototype)

2023-01-12 20:26:47

一. 引用数据类型中的对象

1.1 概述

对象就是一组“键值对”(key-value)的集合,是一种无序的复合数据集合。

var obj = {

    name:"zevin",
    
    "age":21,
    
    7:{lost:shanxi,city:yuncheng},
    
    get:funciton(a){
        return true; 
    }
};

上述代码中,大括号就定义了一个对象(对象是以{ }作为边界),它被赋值给变量obj,所以变量obj中保存的引用地址就指向了一个对象。该对象中包含了四个键值对:

  1. name,“age”,7,get被称为键名:
    js规定对象的所有键名都是字符串(ES6 又引入了 Symbol 值也可以作为键名),所以加不加引号都可以。数字也可以是键名,会被自动转换成字符串。

  2. “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 对象 . 属性

  1. delete命令用于删除对象的属性,删除成功后返回true。删除成功后该属性就不可访问,而且Object.keys方法的返回值也不再包括该属性。
var obj = {
    name:zevin
};
Object.keys(obj)  // ["name"]
delete obj.name  // true
obj.name  // undefined
Object.keys(obj) // [ ]
  1. 如果删除的是一个不存在的属性,不会报错反而返回true。因此,不能根据delete命令的结果,判断某个属性是存在的。
var obj = {};
delete obj.name  // true
  1. 只有当该属性存在,并且是不得删除的,delete命令才会返回false
  2. delete命令只能删除对象本身的属性,无法删除继承的属性。
var obj = {};
delete obj.toString // true
obj.toString // [Function: toString]

上面代码中,toString是对象obj继承的属性,虽然delete命令返回true,但该属性并没有被删除,依然存在。这个例子还说明,即使delete返回true,该属性依然可能读取到值。

1.8 对象序列化

将内存中的对象转化成字符串的描述,解决对象在io流中的传输。
在网络当中传输时,只允许传输字符或者字节流。具体的有两种交互方式:json字符串和qs查询字符串。
在这里插入图片描述

具体的序列化方法:

  1. 转换成JSON字符串:
console.log(JSON.stringify(obj));  // {"name":"zevin","age":21,"gender":"male"}
  1. 转化成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()本身也可以是一个工具函数,它可以将任意值转为对象。如果参数为空(或者为undefinednull),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()方法详细解读

  • 作者:Code_Zevin_J
  • 原文链接:https://blog.csdn.net/JZevin/article/details/107714734
    更新时间:2023-01-12 20:26:47