一切对象都是 Object
的实例,一切函数都是 Function
的实例, Object
是构造函数,函数是 Function
的实例, Function.prototype
是对象,对象是 Object
的实例,可以说 Object
与 Function
是一对密不可分的兄弟,让我们一起解开 Object
与 Function
的神秘面纱,本章主要了解 Object
相关知识,下章再来看 Function
[TOC]
Function instanceof Object // true Object instanceof Function // true
Object 构造函数可以创建一个对象包装器
JS 中所有对象都来自 Object, 所有对象从 Object.prototype 继承方法和属性
传入的值为 null
或 undefined
将返回一个 {}
空对象
Object(null) // {} Object(undefined) // {}
Object()
等同于 new Object()
由 {}
包含零个或多个键值对组成以 逗号
分隔的列表构成
下面是 ES6(ECMAScript 2015) 中对象的定义方式
const a = 1; var obj = { a, get a(){}, set a(){}, ['a' + 'b']: 'hello', say(){}, baz: { b: 2 } }
重复的属性,后面的属性会覆盖前面的属性,在ES5中,重复的属性会抛出语法错误 SyntaxError
Object.assign()
可以合并两个对象,也可以使用展开符 ...
合并两个对象, Object.assign()
会触发 setter
,而展开操作符则不会
var a = { a: 1 }; var b = { b: 2 }; var mergedObj = Object.assign({}, a, b) var mergedObj = { ...a, ...b }
如果给对象的 __proto__
赋值为 null
, 则会更改对象原型,赋值为其它任何值则不会改变对象原型
Object.getPrototypeOf({__proto__:null}) === null // true Object.getPrototypeOf({__proto__:{}}) === null // false
在对象字面值中,只有一次改变原型的机会,多次变更会报语法错误
不使用 :
定义 __proto__
不会变更原型,而是会变成对象的普通属性
var __proto__ = "variable"; var obj1 = { __proto__ }; Object.getPrototypeOf(obj1) === Object.prototype // true var obj2 = { __proto__() { return "hello"; } }; obj2.__proto__() === "hello"
对象字面量 与 JSON(JavaScript Object Notation)
的区别
"property": value
的形式定义属性,且属性名必须以 "
号括起来,属性定义不允许简写
返回 Object()
构造函数形参数量,值为 1
检查对象自身中是否有某属性(忽略原型链中的属性)
检查当前原型是否在指定对象的原型中 (作用等同于 instanceof)
判断属性是否可枚举
返回对象的字符串表示(用于派生对象重载)
新的 locales
和 options
参数让应用程序可以指定要进行格式转换的语言,并且定制函数的行为
在旧的实现中,会忽略 locales
和 options
参数,使用的语言环境和返回的字符串的形式完全取决于实现方式。
locales
和 options
不同的浏览器及版本有兼容性问题,所以这个api并没有得到广泛的应用
var prices = ['¥7', 500, 8123, 12]; prices.toLocaleString('ja-JP', { style: 'currency', currency: 'JPY' }); // "¥7,¥500,¥8,123,¥12" const num = 2333333; num.toLocaleString('zh', { style: 'decimal' }); //2,333,333 num.toLocaleString('zh', { style: 'percent' }); //233,333,300% const num = 2333333; num.toLocaleString('zh', { style: 'currency', currency: 'CNY' }); //¥2,333,333.00 num.toLocaleString('zh', { style: 'currency', currency: 'cny', currencyDisplay: 'code' }); //CNY2,333,333.00 let num = 2333.3; num.toLocaleString('zh', { minimumIntegerDigits: 5 }); //02,333.3 //如果不想有分隔符,可以指定useGrouping为false num.toLocaleString('zh', { minimumIntegerDigits: 5, useGrouping: false }); //02333.3 num.toLocaleString('zh', { minimumFractionDigits: 2, useGrouping: false }); //2333.30 num = 666.666 num.toLocaleString('zh', { maximumFractionDigits: 2, useGrouping: false }); //666.67
返回对象的字符串表示
使用 toString
来检查对象类型
function isNull(){ Object.prototype.toString.call(obj) === '[object Null]' }
返回一个表示该对象的字符串
将可枚举的属性从一个或多个源拷贝到目标对象
但是只能深拷贝非嵌套的对象,嵌套属性为浅拷贝
简单的深拷贝
JSON.parse(JSON.stringify(Obj))
, 缺点是会破坏对象的原型链,并且会丢失对象的方法
使用指定的原型创建对象
// ES5实现 (不支持 propsObj) function extend(proto){ function F(){}; F.prototype = proto; return new F(); } // 创建一个原型为null的空对象 var o = Object.create(null) var o = {} // 相当于 var o = Object.create(Object.prototype) // 为所创建的对象添加属性 (如果缺省配置,默认为false) var o = Object.create(Object.prototype, { foo: { writeable: true, configurable: true, enumerable: true, value: 'hello' } }) function MyClass (){} var o = new MyClass() // 相当于 var o = Object.create(Myclass.prototype)
Object.assign
只会拷贝源对象自身且可枚举的属性,会调用 源对象的 get
和 目标对象的 set
方法, get
方法是自定义的,合并后,将会丢失 null
或 undefined
的源对象 Ojbect.assign 的ES5中的 polyfill
Object.defineProperty(Object, 'assign', { value: function assign(target, varArgs) { 'use strict'; if (target == null) { throw new TypeError('Connot convert undefined or null to object'); } var to = Object(target); for (var index = 1; index < arguments.length; index++) { var nextSource = arguments[index] for (var nextKey in nextSource) { if(Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { to[nextKey] = nextSource[nextKey]; } } } return to; }, writeable: true, configurable: true, })
定义或修改属性对象
属性描述 descriptor
分为两种:数据描述符 和 存取描述符
getter
、 setter
函数的属性,也可配置 是否可配置及是否可枚举 属性描述只能是 数据描述 或 存取描述 中的一种
configurable
、 enumerable
、 writeable
、 value
get
、 set
、 configurable
、 enumerable
四个配置 如果一个描述符不具有value,writable,get 和 set 任意一个关键字,那么它将被认为是一个数据描述符
// 数据描述符 描述的属性 Object.defineProperty({}, 'a', { // 是否可枚举 决定了是否可被 for...in 和 Object.keys() 获取属性名 enumerabel: true, // 属性描述是否可改变,该属性是否可删除,当前配置为false时,不能在数据和访问器属性类型之间切换,不可删除,除 writable 之外的描述不可修改 configurable: true, // 是否可赋值 writeable: true, // 属性值,默认为 undefined value: 1 }) // 存取描述符 描述的属性 var bValue; Object.defineProperty({}, 'b', { get: function(){ return value; }, set: function(newValue){ bValue = newValue }, enumerable: true, configurable: true, })
function Archiver () { var temp = null; var history = [] Object.defineProperty(this, 'temp', { get: function () { console.log('get') return temp; }, set: function (newV) { temp = newV history.push({val: temp}) } }) this.getHistory = function(){ return history; }; } var arc = new Archiver() arc.temp arc.temp = 11 arc.temp = 13 arc.getHistory() // [{ val: 11 }, { val: 13 }]
定义或修改多个属性对象 与 Object.defineProperty
一样
var obj = {}; Object.defineProperties(obj, { 'property1': { value: true, writable: true }, 'property2': { value: 'Hello', writable: false } });
返回可枚举属性的键值对数组
// 获取 const obj = { foo: 'bar', baz: 42 }; console.log(Object.entries(obj)); // [ ['foo', 'bar'], ['baz', 42] ] // 遍历 for...of const obj = { a: 5, b: 7, c: 9}; for(const [key, value] of Object.entries(obj)) { console.log(`${key} ${value}`); } // 遍历 forEach Object.entries(obj).forEach(([key, value]) => { console.log(`${key} ${value}`); // "a 5", "b 7", "c 9" }); // Object 转 Map new Map(Object.entries(obj))
冻结一个对象,被冻结的对象,不可添加、删除、修改其属性,也不可修改属性的描述配置,返回被冻结的对象
只能冻结对象中的第一层的常量对象,不可冻结复杂对象
obj1 = { internal: {} }; Object.freeze(obj1); obj1.internal.a = 'aValue'; obj1.internal.a // 'aValue'
自定义一个深冻结函数
function deepFreeze(obj){ const propNames = Object.getOwnPropertyNames(obj); propNames.forEach(name => { const prop = obj[name]; if(typeof prop === 'object' && prop !== null) { deepFreeze(prop) } }) return Object.freeze(obj) }
返回指定对象是否是冻结对象
封闭一个对象,对象属性不可删除,不可增加新属性,不可修改属性描述配置,但 可以修改属性值
const object1 = { property1: 42 }; Object.seal(object1); object1.property1 = 33; console.log(object1.property1); // expected output: 33 delete object1.property1; // cannot delete when sealed console.log(object1.property1); // expected output: 33
返回指定对象是否是封闭对象
获取对象自身可枚举的属性
Object.keys()
与 for...in
不同的是: for...in
还会遍历对象原型链中的属性
使用 for...in
获取对象自身可枚举的属性
for(key in obj) {
obj.hasOwnProperty(key){
console.log(key)
}
}
返回可枚举的属性数组
const obj = { foo: 'bar', baz: 42 }; console.log(Object.entries(obj)); // [ ['foo', 'bar'], ['baz', 42] ]
获取对象自身的属性 (枚举&不能枚举)
获取对象自身不可枚举的属性?
var allProps = Object.getOwnPropertyNames(obj)
var enumProps = Object.keys(obj)
var noenumProps = allProps.filter(v => enumProps.indexOf(v) === -1)
返回对象自身所有 Symbol
属性数组
对象自身属性 包含了两种类型,一种是 字符串属性,一种是 Symbol
数据属性,
字符串属性名可由 Object.getOwnPropertyNames()
来获取
Symbol
属性名可由 Ojbect.getOwnPropertySymbols()
来获取
默认对象是不含有 Symbol
属性的,除非手动添加了 Symbol
属性
返回 对象自身属性的 描述配置信息
返回 对象自身所有属性的 描述配置信息
属性描述分为两种:存取描述 与 数据描述
var o = { get foo() { return 17; } }; var d = Object.getOwnPropertyDescriptor(o, "foo"); // d { // configurable: true, // enumerable: true, // get: /*the getter function*/, // set: undefined // } var o = { bar: 42 }; var d = Object.getOwnPropertyDescriptor(o, "bar"); // d { // configurable: true, // enumerable: true, // value: 42, // writable: true // }
Object.assign()
无法拷贝源对象属性的特性,且属性描述会被转为数据描述,也无法拷贝源对象的原型
使用 Object.create()
可以完整的浅拷贝一个对象
Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj))
返回指定对象的原型
var proto = {}; var obj = Object.create(proto); Object.getPrototypeOf(obj) === proto; // true
设置指定对象的原型
这个方法是ES6中的方法,用于替换ES5中 Object.prototype.__proto__ = newProto
的方式修改原型
修改原型对于浏览器是一个耗性能的方法,应避免去修改原型,使用 Object.create()
去创建新的对象
同值相等比较 比较两个值是否相等
与抽象相等 ==
区别:
同值相等不会去隐式转换数据类型
与严格相等 ===
区别:
-0
与 +0
返回 false
, 而严格相等返回 true
对于 NaN
与 NaN
,同值相等返回 true
, 严格相等返回 false
Object.is(0, -0); // false Object.is(-0, -0); // true Object.is(NaN, 0/0); // true 0 === -0 //true +0 === -0 //true NaN === NaN //false
判断指定对象是否可以扩展 (是否可以添加属性及原型)返回 Boolean
var empty = {}; Object.isExtensible(empty); // === true