JavaScript Object对象

218 阅读6分钟

概述

JavaScript 的所有其他对象都继承自Object对象,即那些对象都是Object的实例

Object对象的原生方法分成两类:Object本身的方法与Object的实例方法

1)Object对象本身的方法

所谓“本身的方法”就是直接定义在Object对象的方法

2)Object的实例方法

所谓实例方法就是定义在Object原型对象Object.prototype上的方法。它可以被Object实例直接使用。

Object本身是一个函数,可以当作工具方法使用,将任意值转为对象

如果参数为空(或者为undefined和null),Object()返回一个空对象

如果参数是原始类型的值,Object方法将其转为对应的包装对象的实例

var obj = Object(1);
obj instanceof Object // true
obj instanceof Number // true
var obj = Object('foo');
obj instanceof Object // true
obj instanceof String // true
var obj = Object(true);
obj instanceof Object // true
obj instanceof Boolean // true

如果Object方法的参数是一个对象,它总是返回该对象,即不用转换

利用这一点,可以写一个判断变量是否为对象的函数。

function isObject(value) {
 return value === Object(value);
}
isObject([]) // true
isObject(true) // false

Object 构造函数

Object不仅可以当作工具函数使用,还可以当作构造函数使用,即前面可以使用new命令

Object(value)与new Object(value)两者的语义是不同的,Object(value)表示将value转成一个对象,new Object(value)则表示新生成一个对象,它的值是value。

Object 的实例方法 toString() 的应用:判断数据类型

Object.prototype.toString.call(value)
var type = function (o){
 var s = Object.prototype.toString.call(o);
 return s.match(/\[object (.*?)\]/)[1].toLowerCase();
};
type({}); // "object"
type([]); // "array"
type(5); // "number"
type(null); // "null"
type(); // "undefined"
type(/abcd/); // "regex"
type(new Date()); // "date"

对象(object)是 JavaScript 最重要的数据结构。ES6 对它进行了重大升级

数据结构本身的改变

(1)属性的简洁表示法

ES6 允许直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。

const foo = 'bar';
const baz = {foo};
baz // {foo: "bar"}

// 等同于

const baz = {foo: foo};

(2)属性名表达式

JavaScript 定义对象的属性,有两种方法。

方法一 直接用标识符作为属性名

obj.foo = true;

方法二 用表达式作为属性名 要将表达式放在方括号之内

obj['a' + 'bc'] = 123;

注意,属性名表达式与简洁表示法,不能同时使用,会报错。

报错:

const foo = 'bar';
const bar = 'abc';
const baz = { [foo] };

正确:

const foo = 'bar';
const baz = { [foo]: 'abc'};

(3)方法的 name 属性

函数的name属性,返回函数名。对象方法也是函数,因此也有name属性。

const person = {
 sayName() {
 console.log('hello!');
 },
};
person.sayName.name // "sayName"

(4)属性的遍历

ES6 一共有 5 种方法可以遍历对象的属性。

1)for...in

for...in循环遍历对象自身的和继承的可枚举属性(不含 Symbol 属性)。

2)Object.keys(obj)

Object.keys返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)的键名。

3)Object.getOwnPropertyNames(obj)

Object.getOwnPropertyNames返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)的键名。

4)Object.getOwnPropertySymbols(obj)

Object.getOwnPropertySymbols返回一个数组,包含对象自身的所有 Symbol 属性的键名。

5)Reflect.ownKeys(obj)

Reflect.ownKeys返回一个数组,包含对象自身的所有键名,不管键名是 Symbol 或字符串,也不管是否可枚举。

以上的 5 种方法遍历对象的键名,都遵守同样的属性遍历的次序规则。

首先遍历所有数值键,按照数值升序排列。

其次遍历所有字符串键,按照加入时间升序排列。

最后遍历所有 Symbol 键,按照加入时间升序排列。

(5)super 关键字

我们知道,this关键字总是指向函数所在的当前对象,ES6 又新增了另一个类似的关键字super,指向当前对象的原型对象。

注意,super关键字表示原型对象时,只能用在对象的方法之中,用在其他地方都会报错

JavaScript 引擎内部,super.foo等同于Object.getPrototypeOf(this).foo(属性)或Object.getPrototypeOf(this).foo.call(this)(方法)。

const proto = {
 x: 'hello',
 foo() {
 console.log(this.x);
 },
};
const obj = {
 x: 'world',
 foo() {
 super.foo();
 }
}
Object.setPrototypeOf(obj, proto);
obj.foo() // "world"

上面代码中,super.foo指向原型对象proto的foo方法,但是绑定的this却还是当前对象obj,因此输出的就是world。

(6)克隆和拷贝

对象的扩展运算符等同于使用Object.assign()方法。

let aClone = { ...a };

// 等同于

let aClone = Object.assign({}, a);

上面的例子只是拷贝了对象实例的属性,如果想完整克隆一个对象,还拷贝对象原型的属性,可以采用下面的写法。

写法一

const clone1 = {
 __proto__: Object.getPrototypeOf(obj),
 ...obj
};

写法二

const clone2 = Object.assign(
 Object.create(Object.getPrototypeOf(obj)),
 obj
);

写法三

const clone3 = Object.create(
 Object.getPrototypeOf(obj),
 Object.getOwnPropertyDescriptors(obj)
)

上面代码中,写法一的__proto__属性在非浏览器的环境不一定部署,因此推荐使用写法二和写法三。

扩展运算符可以用于合并两个对象。

let ab = { ...a, ...b };

等同于

let ab = Object.assign({}, a, b);

Object对象的新增方法

1)Object.is()

它用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。

不同之处只有两个:一是+0不等于-0,二是NaN等于自身。

 +0 === -0 //true
 NaN === NaN // false
 Object.is(+0, -0) // false
 Object.is(NaN, NaN) // true

(2) Object.assign()

Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)

 const target = { a: 1, b: 1 };
 const source1 = { b: 2, c: 2 };
 const source2 = { c: 3 };
 Object.assign(target, source1, source2);
 target // {a:1, b:2, c:3}

Object.assign方法实行的是浅拷贝

(3)Object.getOwnPropertyDescriptors()

ES5 的Object.getOwnPropertyDescriptor()方法会返回某个对象属性的描述对象(descriptor)。ES2017 引入了Object.getOwnPropertyDescriptors()方法,返回指定对象所有自身属性(非继承属性)的描述对象。

4)__proto__属性,Object.setPrototypeOf(),Object.getPrototypeOf()

__proto__属性(前后各两个下划线),用来读取或设置当前对象的prototype对象。目前,所有浏览器(包括 IE11)都部署了这个属性。

Object.setPrototypeOf方法的作用与__proto__相同,用来设置一个对象的prototype对象,返回参数对象本身。它是 ES6 正式推荐的设置原型对象的方法

(5)Object.keys(),Object.values(),Object.entries() Object.fromEntries()

ES5 引入了Object.keys方法,返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键名

Object.values方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值。

Object.entries()方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组。

Object.fromEntries()方法是Object.entries()的逆操作,用于将一个键值对数组转为对象。

// 例一

const entries = new Map([
 ['foo', 'bar'],
 ['baz', 42]
]);
Object.fromEntries(entries)
// { foo: "bar", baz: 42 }

// 例二

const map = new Map().set('foo', true).set('bar', false);
Object.fromEntries(map)
// { foo: true, bar: false }

该方法的一个用处是配合URLSearchParams对象,将查询字符串转为对象。

Object.fromEntries(new URLSearchParams('foo=bar&baz=qux'))
// { foo: "bar", baz: "qux" }