创建对象的方式

(1)Object 构造函数方式 Object 构造函数将给定的值包装为一个新对象。

let o = new Object() o.foo = 42 console.log(o)

对象的类型和特征 对象基础详解(1)

(2)对象字面量方式

var obj1 = { foo: 'bar', x: 42 };

(3)Object.create() 方法用于创建一个新对象,使用现有的对象来作为新创建对象的原型(prototype)。

如果该参数被指定且不为 undefined,则该传入对象的自有可枚举属性(即其自身定义的属性,而不是其原型链上的枚举属性)将为新创建的对象添加指定的属性值和对应的属性描述符。这些属性对应于 Object.defineProperties() 的第二个参数。https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/create

const person = { isHuman: false, printIntroduction: function() { console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`); } }; const me = Object.create(person); console.log(me) me.name = 'Matthew'; // "name" is a property set on "me", but not on "person" me.isHuman = true; // inherited properties can be overwritten me.printIntroduction(); // expected output: "My name is Matthew. Am I human? true"

对象的静态方法

Object.assign() 通过复制一个或多个对象来创建一个新的对象。

Object.create() 使用指定的原型对象和属性创建一个新对象。

Object.defineProperty() 给对象添加一个属性并指定该属性的配置。

Object.defineProperties() 给对象添加多个属性并分别指定它们的配置。

Object.entries() 返回给定对象自身可枚举属性的 [key, value] 数组。

Object.freeze() 冻结对象:其他代码不能删除或更改任何属性。

Object.getOwnPropertyDescriptor() 返回对象指定的属性配置。

Object.getOwnPropertyNames() 返回一个数组,它包含了指定对象所有的可枚举或不可枚举的属性名。

Object.getOwnPropertySymbols() 返回一个数组,它包含了指定对象自身所有的符号属性。 Object.getPrototypeOf() 返回指定对象的原型对象。

Object.is() 比较两个值是否相同。所有 NaN 值都相等(这与==和===不同)。

Object.isExtensible() 判断对象是否可扩展。

Object.isFrozen() 判断对象是否已经冻结。

Object.isSealed() 判断对象是否已经密封。

Object.keys() 返回一个包含所有给定对象自身可枚举属性名称的数组。

Object.preventExtensions() 防止对象的任何扩展。

Object.seal() 防止其他代码删除对象的属性。

Object.setPrototypeOf() 设置对象的原型(即内部 [[Prototype]] 属性)。

Object.values() 返回给定对象自身可枚举值的数组。

对象的实例实例属性方法定义

对象属性也可以是一个函数、getter、setter方法。

var o = { property: function ([parameters]) {},//or property([parameters]) {} get property() {}, set property(value) {}, };

属性

对象拥有两种属性:数据属性和访问器属性

数据属性

数据属性是键值对,并且每个数据属性拥有下列特性:

configurable :当且仅当该属性的 configurable 键值为 true 时,该属性的描述符才能够被改变,同时该属性也能从对应的对象上被删除。默认值为false

Value: 包含这个属性的数据值。

Writable:如果该值为 false,则该属性的Value 特性不能被修改。

Enumerable : 如果该值为 true,则该属性可以用 for...in 循环来枚举。

let obj = { foo: 123 }; console.log(Object.getOwnPropertyDescriptor(obj, 'foo'))

对象的类型和特征 对象基础详解(2)

访问器属性

访问器属性有一个或两个访问器函数(get 和 set)来存取数值。 自定义 Setters和getter

function Archiver() { var temperature = null; var archive = []; Object.defineProperty(this, 'temperature', { get: function() { console.log('get!'); return temperature; }, set: function(value) { temperature = value; archive.push({ val: temperature }); } }); this.getArchive = function() { return archive; }; } var arc = new Archiver(); arc.temperature; // 'get!' arc.temperature = 11; arc.temperature = 13; arc.getArchive(); // [{ val: 11 }, { val: 13 }]

可枚举性

对象的每个属性都有一个描述对象(Descriptor),用来控制该属性的行为。Object.getOwnPropertyDescriptor方法可以获取该属性的描述对象

可枚举属性是指那些内部 “可枚举” 标志设置为 true 的属性,对于通过直接的赋值和属性初始化的属性,该标识值默认为即为 true,对于通过 Object.defineProperty 等定义的属性,该标识值默认为 false。

var o = {}; Object.defineProperty(o, "a", { value: 1, enumerable: true }); Object.defineProperty(o, "b", { value: 2, enumerable: false }); Object.defineProperty(o, "c", { value: 3 }); // enumerable 默认为 false console.log(o) for (var i in o) { console.log(i) }

对象的类型和特征 对象基础详解(3)

let obj = { foo: 123 }; Object.getOwnPropertyDescriptor(obj, 'foo') // { // value: 123, // writable: true, // enumerable: true, // configurable: true // }

for ...in

for...in语句以任意顺序迭代一个对象的除Symbol以外的可枚举属性,包括继承的可枚举属性。

const user = {}; Object.prototype.authenticated = true; for (var i in user) { console.log(i) }

对象的类型和特征 对象基础详解(4)

示例2

var obj = {}; obj.name = 'xkx'; obj.age = 18; obj.run = function() { //创建一个 run()方法并返回值 return this.name this.age '运行中...'; }; // 给原型添加属性和方法 Object.prototype.gaga = function() { console.log('gaga') } Object.prototype.names = 'names' // 给原型添加一个可枚举的属性 Object.defineProperty(Object.prototype, "ages", { enumerable: false, configurable: false, writable: false, value: 20 }); var descriptor = Object.create(null); // 没有继承的属性 // descriptor.value = 'static'; // // 默认没有 enumerable,没有 configurable,没有 writable // Object.defineProperty(obj, 'key', descriptor); // console.log(Object.getOwnPropertyDescriptor(obj, 'key')) //显式 Object.defineProperty(obj, "key", { enumerable: false, configurable: false, writable: false, value: "static" }); console.log(Object.getOwnPropertyDescriptor(obj, 'key')) console.log(obj) console.log(Object.getPrototypeOf(obj)) for (var i in obj) { console.log(i) }

原型上添加的不可枚举的ages属性,没有被枚举

对象的类型和特征 对象基础详解(5)

如果将enumerable 改为true,原型上添加的ages 可枚举属性被遍历

Object.defineProperty(Object.prototype, "ages", { enumerable: true, configurable: false, writable: false, value: 20 });

对象的类型和特征 对象基础详解(6)

创建属性

如果对象中不存在指定的属性,Object.defineProperty() 会创建这个属性。当描述符中省略某些字段时,这些字段将使用它们的默认值。

Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。

var o = {}; // 创建一个新对象 // 在对象中添加一个属性与数据描述符的示例 Object.defineProperty(o, "a", { value : 37, writable : true, enumerable : true, configurable : true });

getPrototypeOf方法

getPrototypeOf()是JavaScript中的内置函数,用于检查用户创建的对象的原型。大多数时候,它用于检查两个对象是否具有相同的原型。这里的原型是指用户在JavaScript代码中定义的对象的内部定义

const person = { isHuman: false, printIntroduction: function() { console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`); } }; const me = Object.create(person); console.log(me) me.name = 'Matthew'; // "name" is a property set on "me", but not on "person" me.isHuman = true; // inherited properties can be overwritten me.printIntroduction(); // expected output: "My name is Matthew. Am I human? true" console.log(Object.getPrototypeOf(me))

对象的类型和特征 对象基础详解(7)

应用场景: (1)完整克隆一个对象,还拷贝对象原型的属性,可以采用下面的写法。

// 写法一 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) )

getOwnPropertyDescriptor 方法

Object.getOwnPropertyDescriptor方法可以获取该属性的描述对象。

let obj = { foo: 123 }; Object.getOwnPropertyDescriptor(obj, 'foo') // { // value: 123, // writable: true, // enumerable: true, // configurable: true // }

console.log(Object.getOwnPropertyDescriptor(Object.prototype, 'toString').enumerable) console.log(Object.getOwnPropertyDescriptor(Object.prototype, 'ages').enumerable)

对象的类型和特征 对象基础详解(8)

defineProperty方法

Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象

Object.keys(obj)

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

var obj = {}; obj.name = 'xkx'; obj.age = 18; obj.run = function() { //创建一个 run()方法并返回值 return this.name this.age '运行中...'; }; // 给原型添加属性和方法 Object.prototype.gaga = function() { console.log('gaga') } Object.prototype.names = 'names' // 给原型添加一个可枚举的属性 Object.defineProperty(Object.prototype, "ages", { enumerable: true, configurable: false, writable: false, value: 20 }); var descriptor = Object.create(null); // 没有继承的属性 // descriptor.value = 'static'; // // 默认没有 enumerable,没有 configurable,没有 writable // Object.defineProperty(obj, 'key', descriptor); // console.log(Object.getOwnPropertyDescriptor(obj, 'key')) //显式 Object.defineProperty(obj, "key", { enumerable: true, configurable: false, writable: false, value: "static" }); // console.log(Object.getOwnPropertyDescriptor(obj, 'key')) // console.log(obj) // console.log(Object.getPrototypeOf(obj)) // console.log(Object.getOwnPropertyDescriptor(Object.prototype, 'toString').enumerable) // console.log(Object.getOwnPropertyDescriptor(Object.prototype, 'ages').enumerable) // 查看对象原型上toString console.log(Object.getOwnPropertyDescriptor(Object.prototype, 'toString')) // 修改前 console.log(obj.toString()) // 原型链方式修改 Object.prototype.toString = function() { return '修改了toString方法' } // defineProperty方式修改 // Object.defineProperty(Object.prototype, 'toString', { // value: '.........' // }) // 修改后 console.log(obj.toString()) // 修改 重写 对象原型上的toString 方法 // console.log(Object.getOwnPropertyDescriptor(Object.prototype, 'toString').enumerable) for (var i in obj) { console.log(i) } console.log(Object.keys(obj))

对象的类型和特征 对象基础详解(9)

可以发现原型上的属性并没有被枚举出来

常用方法

(1) 怎样判断某个对象是否为另一个对象的原型对象 使用Object.prototype.constructor.prototype进行比较

var obj1 = {name: "李雷"}; var obj2 = {age: 23}; obj1.constructor.prototype === Object.prototype; // true

(2)使用: Object.prototype.isPrototypeOf()进行比较

var obj1 = {name: "Lilei"}; var obj2 = Object.create(obj1); obj1.isPrototypeOf(obj2); // true

(3)扩展属性 ECMAScript 提案(第 3 阶段)的剩余/扩展属性将扩展属性添加到对象字面量。它将自己提供的对象的枚举属性复制到一个新的对象上。

使用比Object.assign()更短的语法,可以轻松克隆(不包括原型)或合并对象。

var obj1 = { foo: 'bar', x: 42 }; var obj2 = { foo: 'baz', y: 13 }; var clonedObj = { ...obj1 }; // Object { foo: "bar", x: 42 } var mergedObj = { ...obj1, ...obj2 }; // Object { foo: "baz", x: 42, y: 13 }

(4)hasOwnProperty 继承的属性不显示

var triangle = {a: 1, b: 2, c: 3}; function ColoredTriangle() { this.color = 'red'; } ColoredTriangle.prototype = triangle; var obj = new ColoredTriangle(); for (var prop in obj) { if (obj.hasOwnProperty(prop)) { console.log(`obj.${prop} = ${obj[prop]}`); } } // Output: // "obj.color = red"

(4)Object.create(null)生生的对象里面没有任何属性,非常“空”,我们称它为字典,这种字典对象适合存放数据,不必担心原型带来的副作用。

bject.prototype.isPrototypeOf({}) // true Object.prototype.isPrototypeOf([]) // true Object.prototype.isPrototypeOf(/xyz/) // true Object.prototype.isPrototypeOf(Object.create(null)) // false

关于 _proto和 prototype

而构造函数的prototype是什么,它的原型链最终又指向了哪里呢?这个问题的答案是:函数的prototype是一个在函数声明阶段就会产生的对象(prototype只有函数才会有),这个对象只有两个属性constructor和__proto__,其中__proto__指向了我们原型链的顶点Object.prototype。constructor指向函数本身,里面包括了函数一些基本描述信息比如函数名称,参数个数等。

有意思的是constructor里面即有__proto__又有prototype,他们之间有什么关系和差别呢?首先prototype描述的是原型对象,它是一个实实在在的对象,__proto__是用来描述对象间的关联关系的。最终会指向一个prototype原型。函数由Function衍生而来,所以函数的__proto__指向的是Function.prototype。Function是由Object衍生而来,有意思的事情又发生了,Function的__proto__却并不指向Object.prototype,而是指向Function.prototype这个标准的内置对象。Function.prototype.__proto__才是指向我们的原型链顶点Object.prototype

Function.proto === Function.prototype // true

,