对象


2020-06-07 上次更新时间:4/29/2022, 9:34:08 AM 0

对象是属性的无序集合。

# 创建

  1. 使用对象直接量
var obj = {}
1
  1. 通过 new 创建(构造函数)
var obj = new Object();
var arr = new Array();
1
2
  1. 使用 Object.create() 创建 (ES6)

Object.create(原型对象,自定义(不可枚举)属性对象)方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__

原型有关请看:原型

Object有关请看: 对象方法

var tempObj = {
  x: 1,
  y: 2
}
var newAttrObj = {
  a: {
    writable: true,
    configurable: true,
    value: 100
  }
}
var obj = Object.create(tempObj, newAttrObj);
console.log(obj);
// {
//   a: 100,
//   __proto__: {
//     x: 1,
//     y: 2
//   }
// }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

创建一个普通空对象

var obj3 = Object.create(Object.prototype);
obj3.toString(); // [object Object]
1
2

创建一个空对象,但该对象不继承任何属性和方法

var obj2 = Object.create(null);
obj2.toString(); // TypeError: obj2.toString is not a function
1
2

# 属性查询与设置

  1. 通过 . 来访问或赋值
var obj = {x:1}
obj.x; // 1
obj.y = 2;
1
2
3
  1. 通过 [] 来访问或赋值
var obj = {y:2}
obj[y]; // 2
obj[x] = 1;
1
2
3

区别:

方法 右侧 右侧是否可以为数字/变量/中划线
. 一个以 “属性名称” 命名的简单标识符 N
[] 一个计算结果为 ”字符串“ 的表达式 Y
var obj = {name: 'jack', 12: '哈哈哈', 'old-price': 188};
obj[12]; // 哈哈哈
obj.12; // SyntaxError: Unexpected number
obj['old-price']; // 188
obj.old-price; // ReferenceError: price is not defined

var attrName = 'name';
obj[attrName]; // jack
obj.attrName; // undefined
1
2
3
4
5
6
7
8
9

# 属性类型

对象属性是由名字、值和一组特性(attribute)组成。在ECMAScript5中,属性值可以用一个或者两个方法替代,就是getter和setter。 由getter和setter定义的属性称为“存储器属性”(accessor property),也可以叫访问器属性。

对象的每个属性都有一个描述对象(Descriptor),用来控制该属性的行为。

ECMAScript 中有两种属性:数据属性和访问器属性。

# 数据属性

数据属性包含一个数据值的位置。在这个位置可以读取和写入值。数据属性有 4 个描述其行为的特性,又叫属性特性。

属性名 说明 默认值
configurable 能否通过 delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性 true
enumerable 能否通过 for-in 循环返回属性 true
writable 能否修改属性的值 true
value 属性的数据值,属性值存储和读取都是在这个位置 undefined

1. 可通过 Object.getOwnPropertyDescriptor() 查看属性的描述对象

使用:Object.getOwnPropertyDescriptor(属性所在对象, 属性名)

let obj = { foo: 123 };
Object.getOwnPropertyDescriptor(obj, 'foo')
//  {
// configurable: true
// enumerable: true
// value: 123
// writable: true
//  }
1
2
3
4
5
6
7
8

2. 可通过 Object.defineProperty() 修改属性默认特性

使用:Object.defineProperty(属性所在对象, 属性名字, 描述符对象)

var person = {}; 
Object.defineProperty(person, "name", { 
  configurable: false,
  writable: false, 
  value: "Nicholas" 
}); 
console.log(person.name); //"Nicholas"

person.name = "Greg"; 
console.log(person.name); //"Nicholas" => writable为false,修改无效

delete person.name;
console.log(person.name); //"Nicholas" => configurable为false,删除无效
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14

注意:

  1. 使用Object.defineProperty()定义新的属性,描述对象的属性默认值为false。
var person = {
  age: 10
}; 
Object.defineProperty(person, "name", {
  value: "Nicholas" 
}); 
Object.getOwnPropertyDescriptor(person, 'name')

// 新的属性,描述对象的属性默认是false
// {
//   configurable: false
//   enumerable: false
//   value: "Nicholas"
//   writable: false
// }


Object.defineProperty(person, "age", {
  value: "30",
  configurable: false
}); 
Object.getOwnPropertyDescriptor(person, 'age')
// 已有属性则只影响设置的属性
// {
//   configurable: false
//   enumerable: true
//   value: "30"
//   writable: true
// }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
  1. 一旦把属性定义为不可配置的,就不能再把它变回可配置了。此时,再调用 Object.defineProperty()方法修改除 writable 之外的特性,都会导致错误。
var person = {}; 
Object.defineProperty(person, "name", { 
 configurable: false, 
 value: "Nicholas" 
}); 
//抛出错误
Object.defineProperty(person, "name", { 
 configurable: true, 
 value: "Nicholas" 
});
1
2
3
4
5
6
7
8
9
10

# 访问器属性getter和setter

访问器属性不包含数据值;它们包含一对 getter 和 setter 函数(不过,这两个函数都不是必需的)。

  • 读取访问器属性时,会调用 getter 函数,这个函数负责返回有效的值;
  • 写入访问器属性时,会调用setter 函数并传入新值,这个函数负责决定如何处理数据。

访问器属性有如下 4 个特性。

属性名 说明 默认值
configurable 能否通过 delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性 true
enumerable 能否通过 for-in 循环返回属性 true
get 读取属性时调用的函数,只有get则是一个只读属性 undefined
set 写入属性时调用的函数,只有set则是一个只写属性 undefined

注意:访问器属性不能直接定义,必须使用 Object.defineProperty()来定义

  1. 使用Object.defineProperty()定义单个属性
var book = { 
  _year: 2018, 
  edition: 1 
}; 
Object.defineProperty(book, "year", { 
  get: function(){ 
    return this._year; 
  }, 
  set: function(newValue){
    if (newValue > 2004) { 
      this._year = newValue; 
      this.edition += newValue - 2018; 
    } 
  } 
}); 
book.year = 2020; // year: 2020
book.edition; // 3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  1. 使用Object.defineProperties()定义多个属性
var book = {}; 
Object.defineProperties(book, { 
    _year: { // 数据属性
        value: 2018 
    }, 
    edition: { // 数据属性
          value: 1 
    },
    year: { // 访问器属性
        get: function(){
            return this._year; 
        }, 
        set: function(newValue){ 
            if (newValue > 2018) { 
                this._year = newValue; 
                this.edition += newValue - 2018; 
            } 
        } 
    } 
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 应用场景

《javascript属性类型应用场景》

# 总结

  • 数据属性是属性自带的,数据属性可以被继承
  • 访问器属性是需要创建的,访问器属性可以被继承

相关方法:

方法 作用 说明
Object.getOwnPropertyDescriptor() 属性的描述对象
Object.defineProperty() 修改属性默认特性/定义单个属性 数据属性和访问器属性都可用,使用后数据属性默认值为false
Object.defineProperties() 定义多个属性 数据属性和访问器属性可用,但注意数据属性设置false后不能改为true,所以慎用

# 删除属性

delete 操作符可以删除对象属性,并返回一个布尔值(成功返回true,否则返回false)。

语法:

  • delete object.property
  • delete object['property']

删除对象属性

var obj = {x:1}
delete obj.x; // true
1
2

删除数组元素,但数组的长度不受影响

var arr = ['jack', 'tony', 'jenny']
delete arr[1];
console.log(arr); // ['jack', empty, 'jenny']
console.log(arr[1]); // undefined
console.log(arr.length); // 3

1
2
3
4
5
6

注意:

  • 删除属性不存在,delete 不会起作用,但仍会返回 true
  • delete 只能删除自身属性,无法删除原型属性,除非从原型上删除
  • 不可设置的(Non-configurable)属性不能被移除
  • 无法删除任何使用 var 声明的属性(包括全局和函数作用域)
  • 无法删除 let/const 声明的属性
/**
 * 删除属性不存在,delete 不会起作用,但仍会返回 true
 **/
var obj = {x:1}
delete obj.y; // true

/**
 * delete 只能删除自身属性,无法删除原型属性,除非从原型上删除
 */
function Foo() {
  this.bar = 10;
}
Foo.prototype.bar = 42;
var foo = new Foo();
delete foo.bar; // true,因为删除的是 foo 对象的自身属性     
console.log(foo.bar); // 42, foo.bar 仍然可用,因为它在原型链上可用。 
// 从原型上删除属性
delete Foo.prototype.bar; // true
// 由于已删除“ bar”属性,因此不能再从Foo继承它。
console.log(foo.bar);  //undefined

/**
 *  不可设置的(Non-configurable)属性不能被移除
 */
var Employee = {};
Object.defineProperty(Employee, 'name', {configurable: false});
console.log(delete Employee.name); // false

/**
 * 无法删除任何使用 var 声明的属性
 */
var ss = 100;
delete ss; // false
window.ss; // 100
console.log(Object.getOwnPropertyDescriptor(window, 'ss')); 
// {configurable: false, enumerable: true, value:100,writable: true}

/**
 * 无法删除 let/const 声明的属性
 */

let name = 'jack';
const age = '10';
console.log(Object.getOwnPropertyDescriptor(this, 'name'))
// {configurable: false, enumerable: true}
console.log(delete name); // false
console.log(delete age); // false
console.log(name, age) // 'jack' 10
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

# 检测属性

检测某个属性是否存在于某个对象中。 传送门 =》检测是否是对象

  1. in

可检测自有属性和继承属性,如果包含则返回true。

var o = {x: 1,  k: undefined}
"x" in o; // true - 自有属性
"k" in o; // true - 自有属性
"y" in o; // false - 非 o 对象属性
"toString" in o; // true - 继承属性 
1
2
3
4
5
  1. hasOwnProperty()

可检测自有属性,包含则返回true。如果是继承属性将返回false。

var o = {x: 1,  k: undefined};
o.hasOwnProperty("x"); // true - 自有属性
o.hasOwnProperty("k"); // true - 自有属性
o.hasOwnProperty("y"); // false - 非 o 对象属性
o.hasOwnProperty("toString"); // false - 继承属性 
1
2
3
4
5
  1. propertyIsEnumerable()

hasOwnpreperty()的增强版, 只有检测到是自有属性且该属性的可枚举性为true时,才返回true。

var o = {x: 1}
// todo, 输出x的可枚举行
o.propertyIsEnumerable("x"); // true - 自有属性,且有可枚举
// todo 设置y,并且y的可枚举性为false
o.propertyIsEnumerable("y"); // false - 非 o 对象属性
o.propertyIsEnumerable("toString"); // false - 继承属性 
1
2
3
4
5
6
  1. 使用!==

通过 !== 判断一个属性是否是 undefined,如果属性值为 undefined 则无法检测

var o = {x: 1, k: undefined}
o.x !== undefined; // true - 自有属性
o.y !== undefined; // false - 非 o 对象属性
o.toString !== undefined; // true - 继承属性 

o.k !== undefined; // false - 如果属性值为undefined,则无法检测
1
2
3
4
5
6

对比

方法 检测自有属性 检测继承属性 检测值为undefined的属性 检测属性的可枚举性
in Y Y Y N
hasOwnProperty() Y N Y N
propertyIsEnumerable() Y N ? Y
!== Y Y N N

# 固有属性

每个对象都有原型属性(prototype)、类(class)、可拓展性(extensible) 这三个属性。

# 1.原型属性(prototype)

对象的原型属性是用来继承属性的,原型属性也简称为原型。

# 原型链

当谈到继承时,JavaScript 只有一种结构:对象。每个实例对象( object )都有一个私有属性(称之为 __proto__ )指向它的构造函数的原型对象(prototype )即。该原型对象也有一个自己的原型对象( proto ) ,层层向上直到一个对象的原型对象为 null。根据定义,null 没有原型,并作为这个原型链中的最后一个环节。

使用不同的方法创建对象,原型链的查找有差异,具体请看:《使用不同的方法创建对象和生成原型链》

# __proto__ 和 prototype 的关系和区别

在所有实现中,对象实例都没办法通过 [[prototype]] 来访问原型,所以定义了 __proto__ 指向对象构造函数的 prototype

  • prototype 是对象的原型属性,可被继承
  • __proto__ 指向对象构造函数的 prototype
  • constructor 属性将原型对象指向关联的构造函数
function Person() {};

var p1= new Person();
var p2 = new Person();

typeof Person; // function
typeof p1; // object

p1.prototype; // undefined =>  实例对象没有 prototype 属性
Object.getPrototypeOf(p1) == Person.prototype; // true => p1 的原型对象是 Person 的原型对象
p1.constructor == Person; // true => p1 的constructor 指向关联的构造函数
Person.prototype.constructor == Person; // true =>  constructor属性将原型对象指向关联的构造函数

p1.__proto__ === p2.__proto__; // true

p1.__proto__ === p1.constructor.prototype; // true
p1.__proto__ === Person.prototype; // true
p1.constructor.prototype === Person.prototype; // true

Person.__proto__ == Person.constructor.prototype; // true
Person.__proto__ == Object.constructor.prototype // true
Person.constructor.prototype == Object.constructor.prototype // true

Object.__proto__ == Object.constructor.prototype // true

// 字面量方法创建
var a = {}

a.__proto__ == a.constructor.prototype; // true
a.__proto__ == Object.prototype; // true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

参考文章:一篇文章看懂_proto_和prototype的关系及区别

# isPrototypeOf()

isPrototypeOf():测试一个对象是否存在于另一个对象的原型链上

function Foo() {}
function Bar() {}
function Baz() {}

Bar.prototype = Object.create(Foo.prototype);
Baz.prototype = Object.create(Bar.prototype);

var baz = new Baz();

console.log(Baz.prototype.isPrototypeOf(baz)); // true
console.log(Bar.prototype.isPrototypeOf(baz)); // true
console.log(Foo.prototype.isPrototypeOf(baz)); // true
console.log(Object.prototype.isPrototypeOf(baz)); // true
1
2
3
4
5
6
7
8
9
10
11
12
13

# 2.类属性

对象的类属性(class attribute)是一个字符串,用来表示对象的类型信息。可以通过 toString() 方法来查询它。 默认的 toString() 方法会返回下面格式的字符串:

[object class]

toString()方法可以被重写,所以为了能得到正确的数据,需要间接调用Function.call()方法

function classof(o) {
  return Object.prototype.toString.call(o);
  // return Object.prototype.toString.call(o).slice(8, -1);
}

classof(null);         // [object Null]
classof(undefined);    // [object Undefined]
classof(1);            // [object Number]
classof("");           // [object String]
classof(false);        // [object Boolean]
classof({});           // [object Object]
classof([]);           // [object Array]
classof(/./);          // [object RegExp]
classof(new Date());   // [object Date]
classof(window);       // [object Window]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 3.可拓展性

对象的可拓展性表示是否可以给对象添加新属性。所有内置对象和自定义对象都是显示可拓展的。

# 检测可拓展性

  • Object.isExtensible()
  • Object.isSealed()
  • Object.isFrozen()

# 影响可拓展性的方法

  • Object.preventExtensions()
  • Object.seal()
  • Object.freeze()

# 注意

  • 一旦转为不可拓展后,就无法再转回可拓展
  • preventExtensions 只影响到对象本身的可拓展性
var a = {};
Object.isExtensible(a); // true
Object.preventExtensions(a); // Object.seal()、Object.freeze() 同理
a.name = 'a';
a;  // {}
Object.isExtensible(a); // false
1
2
3
4
5
6

# 对象方法

这些方法主要是从原型上继承的。

方法名 功能 备注
hasOwnProperty() 检测自有属性
propertyIsEnumerable() 检测自有属性且该属性的可枚举性为true时
isPrototypeOf() 测试一个对象是否存在于另一个对象的原型链上
toString() 返回表示调用这个方法的对象值的字符串
toLocaleString() 返回对象本地化的字符串
toJSON() 返回序列化的结果
valueOf() 将对象转换为原始值
var a = {name: 123};
a.toString(); // [object Object]
a.toLocaleString(); // [object Object]
a.valueOf(); // {name: 123}

var b = [1, 2, 3] // 1,2,3
b.toString(); // 1,2,3
b.toLocaleString();
b.valueOf(); // [1, 2, 3]

var d = new Date();
d.toString(); // Fri Aug 28 2020 11:29:42 GMT+0800 (中国标准时间)
d.toLocaleString(); // 2020/8/28 上午11:29:42
d.toJSON(); // 2020-08-28T03:29:42.460Z
d.valueof(); // 1598585382460
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# Object 构造函数

# 属性

  • Object.length:值为 1
  • Object.prototype:可以为所有 Object 类型的对象添加属性

# 方法

ES5

方法名 功能
Object.create() 使用指定的原型对象和属性创建一个新对象
Object.defineProperty() 给对象添加一个属性并指定该属性的配置
Object.defineProperties() 给对象添加多个属性并分别指定它们的配置
Object.getOwnPropertyNames() 返回一个数组,它包含了指定对象所有的可枚举或不可枚举的属性名
Object.getOwnPropertySymbols() 返回一个数组,它包含了指定对象自身所有的符号属性
Object.getOwnPropertyDescriptor() 返回对象指定的属性配置
Object.isExtensible() 判断对象是否可扩展
Object.preventExtensions() 防止对象的任何扩展
Object.seal() 创建一个“密封”对象,实际上是调用Object.preventExtensions()把所有属性标记为configurable:false,仅可修改属性的值
Object.isSealed() 判断对象是否已经密封
Object.freeze() 创建一个“冻结”对象,实际上调用Object.seal(),并把所有属性标记为writable:false
Object.isFrozen() 判断对象是否已经冻结

ES6

方法名 功能
Object.is() 比较两个值是否相同。所有 NaN 值都相等(这与==和===不同)
Object.assign() 通过复制一个或多个对象来创建一个新的对象
Object.getOwnPropertyDescriptors() 返回对象所有属性的描述对象
Object.setPrototypeOf() 设置对象的原型(即内部 [[Prototype]] 属性)
Object.getPrototypeOf() 返回指定对象的原型对象
Object.keys() 返回一个包含所有给定对象自身可枚举属性名称的数组
Object.values() 返回给定对象自身可枚举值的数组
Object.entries() 返回给定对象自身可枚举属性的 [key, value] 数组
Object.fromEntries() Object.entries()的逆操作,用于将一个键值对数组转为对象
上次更新时间: 4/29/2022, 9:34:08 AM