# Object 构造器及原型上的方法
整理这些常用 api 是为了能够更好的理解与记忆
在此之前,我们需要知道构造函数的静态方法和原型方法的区别
静态方法,只能有构造函数Object
自己进行调用。而构造函数实例无法使用该方法,同时,在Object.prototype
上面定义的方法也必须通过Object.prototype.方法
进行调用。
原型方法,构造函数和构造函数实例共享的方法。实例可以直接调用实例.方法
,js 会沿着原型链查找到该方法。如下例
class MyObject {
static getName
}
MyObject.getName = function() {
console.log('我是静态方法')
}
MyObject.prototype.sayName = function() {
console.log('我是原型上面的方法')
}
const myInstance = new MyObject()
myInstance.getName() // 报错:Uncaught TypeError: myInstance.getName is not a function
myInstance.sayName() // 打印成功
MyObject.getName() // 打印成功
MyObject.sayName() // 报错 Uncaught TypeError: MyObject.sayName is not a function
MyObject.prototype.sayName() // 打印成功
Object 构造函数静态方法
当我们想获取到一个对象有几个键值对时,我们可以使用Object.keys(object)
来返回一个包含对象属性的数组。这样就可以使用数组的方法对该对象进行处理了。与该方法相同的还有Object.getOwnPropertyNames(object)
可以返回一个包含当前属性的数组。
当我们想对一个对象上的属性进行值属性进行详细定义时,可以使用Object.difineProperty(object, prop, descriptor)
对值属性的选项进行配置,也可以使用该方法对访问器属性进行配置。与之同理的还有Object.defineProperties(object, {...props})
定义了值属性或者访问器属性配置后,我们还可以通过Object.getOwnPropertyDescriptor(object, prop)
来获取单个属性的配置选项。与之同理的还有Object.getOwnPropertyDescriptors(object)
可以获取当前对象的所有属性的配置选项。
当我们想把一个对象进行复制(浅拷贝)时,我们可以使用Objecct.assign(target, ...sources)
返回一个新的对象,target 也会被合并。
当我们想创建一个对象,并且指定它的原型时,可以使用Object.create(proto, {...props})
,如果想创建一个无原型链的对象时,只需要把第一个参数设置为 null
即可
Object 原型
当我们想判断某个变量是具体哪个基本值或者复杂值时,可以使用Object.prototype.toString.call(variable)
,当变量是字符串时,会得到[object String]
的结果,当变量是数组时,会得到[object Array]
,当变量是对象时,会得到[object Object]
等等。
当我们想判断某个对象是否是另一个对象的原型时,Object.prototype.isPrototypeOf()
,判断某个属性是该实例自身属性而不是prototype
属性时,Object.prototype.hasOwnProperty()
把对象转换为基本值类型时会调用Object.prototype.valueOf()
方法
# 1. Object 构造器上的方法(Methods of the Object constructor)
# 1.1 Object.keys()
用于遍历对象,该方法会返回一个数组,该数组输出的属性顺序与 for-in 输出顺序一致
const object1 = {
a: 'somestring',
b: 42,
c: false,
}
Object.keys(object1).forEach((item) => {
console.log(object1[item])
})
// 输出: "somestring", 42, false
for (key in object1) {
console.log(object1[key])
}
// 输出: "somestring", 42, false
# 1.2 Object.create()
参数:Object.create(proto, [propertiesObject]) 第二个参数可选, 是一个属性描述
Object.create()
可以实现继承(因为可以显式指定原型)
const person = {
isHuman: false,
printIntroduction: function() {
console.log(`my name is ${this.name}, am i human ${this.isHuman}`)
}
}
// 为me指定原型,即me.prototype = person
const me = Object.create(person, {
age: {
value: 17,
writeable: true
}
})
// 为me添加属性的最常见形式
me.name = 'jack'
me.isHuman = true
// 调用me原型链上面方法
me.printIntroduction() // my name is jack, am i human true
console.dir(me)
// 打印出一个对象
{
isHuman: true,
name:jack,
age: 17,
__proto__: {
isHuman: false,
printIntroduction: f,
__proto__: f Object()
}
}
// 可以看出me的原型是person,person的原型是Object
# Object.create 声明无原型的空对象
const methodCreateNull = Object.create(null) // 创建了一个非常干净的Object对象,没有原型proto,非常纯净
const methodCreateObj = Object.create({})
const literalCreateObj = {}
console.dir(methodCreateNull)
{
// No properties
}
console.dir(methodCreateObj)
{
// methodCreateObj 的原型是一个空对象,空对象的原型是Object
__proto__: {
__proto__: constructor: Object()
}
}
console.dir(literalCreateObj)
{
__proto__: constructor: Object()
}
// 可以看出Object.create({})创建的对象多了一层__proto__
所以我们可以使用 Object.create()方法指定自己的原型 prototype,可以用于构造函数继承,更多请看继承与原型链 (opens new window)
# 1.3 Object.defineProperty()
Object.defineProperty(obj, prop, descriptor)
该方法可以直接向 object 对象上定义属性,并且对属性进行一些选项设置
数据属性 可以定义以下几种属性
- configurable 是否可删除?,默认 false
- enumerable 是否可枚举,默认 false
- value 值
- writable 是否可写,默认 false
存取器属性描述符还包括下面两个重要方法,但不包含 value 属性
- get 如果没有 getter,默认是 undefined
- set 如果没有 setter,默认是 undefined
const object1 = {};
// 下面这个操作:向object1对象添加'property1'属性,值为42,并且不可写
Object.defineProperty(object1, 'property1', {
value: 42,
writable: false // 不可写,即不可重新赋值
});
object1.property1 = 77; // throws an error in strict mode
console.log(object1.property1); // expected output: 42
console.dir(object1)
{
property1: 42,
__proto__: {
constructor: f Object()
}
}
使用 get 和 set 方法,存取器属性描述独有,存取器的好处是可以对传进来的值做一定处理
下为数据属性描述与存取器描述示例
var o = {}
// 添加属性,并添加数据属性描述 data property descriptor
Object.defineProperty(o, 'a', {
value: 37,
writable: true,
enumerable: true,
configurable: true,
})
// 添加属性,并添加存取器属性描述 accessor property descriptor
var bValue = 38
Object.defineProperty(o, 'b', {
// Using shorthand method names (ES2015 feature).
// This is equivalent to:
// get: function() { return bValue; },
// set: function(newValue) { bValue = newValue; },
get() {
return bValue
}, // 取值操作
set(newValue) {
bValue = newValue
}, // 设置值
enumerable: true,
configurable: true,
})
o.b // 38 b值被定义与bValue相等
// o.b和bValue是相等的,除非被重新修改
// 数据属性与存取器属性不能同时使用,即value与get()不能同时使用
Object.defineProperty(o, 'conflict', {
value: 0x9f91102,
get() {
return 0xdeadbeef
},
})
// 报错信息 TypeError: Invalid property descriptor. Cannot both specify accessors and a value or writable attribute
// 类型错误:非法属性描述,不能同时指定存取器或者value
存取器属性描述的一个例子
function Archiver() {
var temperature = null
var archive = []
// 通过new 调用 this是其本身
Object.defineProperty(this, 'temperature', {
get() {
console.log('get!')
return temperature
},
// 为temperature设置值
set(value) {
temperature = value
archive.push({ val: temperature }) // 用数组保存temperature的历史值
},
})
this.getArchive = function() {
return archive
}
}
var arc = new Archiver()
arc.temperature // 'get!' 执行了存取器的get方法
arc.temperature = 11 // 执行了存取器的set方法
arc.temperature = 13
arc.temperature // 13 执行了存取器的get方法
arc.getArchive() // [{ val: 11 }, { val: 13 }]
# 1.4 Object.defineProperties()
与Object.defineProperty()
类似,但是可以定义多个属性,可以定义属性描述或者存取器描述
const object1 = {}
Object.defineProperties(object1, {
property1: {
value: 42,
writable: true,
},
property2: {},
})
console.log(object1.property1) // expected output: 42
# 1.5 Object.assign()
Object.assign(target, ...sources) || (目标对象,源对象...)
复制一个对象的可迭代属性到目标对象上,参数为多个对象,ES6 新增
可用于浅拷贝,拓展运算符 ...
也可以实现浅拷贝
const target = { a: 1, b: 2 }
const source = { b: 4, c: 5 }
const returnedTarget = Object.assign(target, source)
console.log(target)
// expected output: Object { a: 1, b: 4, c: 5 }
console.log(returnedTarget)
// expected output: Object { a: 1, b: 4, c: 5 }
source 对象会覆盖 target 上面的同名的属性值,没有则直接添加
另一个例子
const target = {}
const source = {
null: null,
undefined,
c: function() {},
d: 3,
[Symbol('foo')]: 233,
} // null不可以用键值省略的写法,会报syntaxError
const returnedTarget = Object.assign(target, source)
// const returnedTarget = Object.assign(target, {...source})
console.log(target) // { null:null, undefined, c: function(){}, d: 3, [Symbol('foo')]: 233}
console.log(returnedTarget) // 同上
const target = {}
const v1 = 'a'
const v2 = null
const v3 = undefined
const v4 = Symbol('foo')
const v5 = function() {}
const returnedTargetDiff = Object.assign(target, v1, v2, v3, v4, v5) // 参数为单个对象的情况,会忽略null,undefined,symbol,function
console.log(target, returnedTargetDiff) // {0: 'a'} 输出键为下标序号
对于属性值是对象的情况,拷贝的是对象的值(指针),即无法实现深拷贝
const source = {
a: 11,
b: {
bb: 22,
},
}
const returnedTarget = Object.assign({}, source)
returnedTarget.b.bb = 33
console.log(source) // {a:11, b: {bb:33}}
// 直接改变了原对象的值
使用 JSON.parse(JSON.stringify({...}))可以实现不完整的深拷贝
更多深浅拷贝,请看这里:赋值与深浅拷贝 (opens new window)
# 1.6 Object.getOwnPropertyDescriptor()
获取单个属性的描述,是否可修改,可枚举等等
const obj = {
a: 1,
}
console.log(Object.getOwnPropertyDescriptor(obj, 'a'))
// 输出: {configurable: true, enumerable: true, value: 1, writable: true}
# 1.6 Object.getOwnPropertyDescriptors()
获取多个属性的描述
# 1.7 Object.getOwnPropertyNames()
遍历对象自身属性,与 Object.keys()类似
const object1 = {
a: 1,
b: 2,
c: 3
}
console.log(Object.getOwnPropertyNames(object1)); // 输出数组Array: ["a", "b", "c"]
const object2 = Object.create(object1, {
d: {
value: 16,
writeable: true
}
})
console.dir(object2)
{
d: 16,
__proto__: {
a: 1,
b: 2,
c: 3,
__proto__: {
constructor: f Object()
}
}
}
console.log(Object.getOwnPropertyNames(object2)) // ["d"]
类的例子(即构造函数类)
class object1 {
constructor() {
this.a = 1 // 定义在实例上面,即通过new object1()可以得到
}
sayHello() {
console.log('hello, my friend')
}
}
class object2 extends object1 {
constructor(args) {
super(args)
this.d = 4
}
}
console.log(Object.getOwnPropertyNames(object1)); // 输出 ["length", "prototype", "name"]
console.log(Object.getOwnPropertyNames(object2)); // 输出 ["length", "prototype", "name"]
const objectInstance = new object2()
console.dir(object1)
{
arguments: {TypeError...},
caller: {TypeError...}
length: 0,
name: 'object1',
prototype: {
constructor: class object1,
sayHello: f sayHello()
__proto__: {
constructor: f Object()
}
}
}
console.dir(object2)
{
arguments: {TypeError...},
caller: {TypeError...}
length: 0,
name: 'object1',
prototype: { // object1
constructor: class object2,
__proto__: {
constructor: class object1,
__proto__: {
constructor: f Object()
}
}
}
}
console.dir(objectInstance)
object2:
{
a:1,
d:4,
__proto__: { object1
constructor: class object2
__proto__: {
constructor: class object1
sayHello: f sayHello(),
__proto__: {
constructor: f Object()
}
}
}
}
console.log(Object.getOwnPropertyNames(objectInstance)); // ["a", "d"]
# 2. Object 原型上上的方法(Methods of the Object prototype)
# 2.1 Object.prototype.toString()
这个方法也可以判断 js 的数据类型,比如Object.prototype.toString.call(a)
const obj = { name: 'jack' }
console.log(obj.toString()) // [object Object]
console.dir(obj.toString()) // [object Object]
const a = '1'
console.log(Object.prototype.toString.call(a)) // [object String]
console.dir(JSON.parse(obj.toString())) // SyntaxError: Unexpected token o in JSON at position 1
console.log(JSON.parse(JSON.stringify(obj))) // {name: "jack"}
# 2.2 Object.prototype.toLocaleString()
转换时间格式用
console.log(new Date().toLocaleString()) // Sat Jul 27 2019 16:30:55 GMT+0800 (中国标准时间) 转换成 2019/7/27 下午4:30:55
# 2.3 Object.prototype.valueOf()
用于把对象转换成原始值,如 string...待补充
# 2.4 Object.prototype.hasOwnProperty()
判断是否是一个对象的自身属性,而不是原型 prototype 上面的属性
const object1 = new Object()
object1.property1 = 42
console.log(object1.hasOwnProperty('property1')) // object1自身属性 输出: true
console.log(object1.hasOwnProperty('toString')) // object1 prototype的属性 输出: false
console.log(object1.hasOwnProperty('hasOwnProperty')) // object1 prototype的属性 输出: false
# 2.5 Object.prototype.isPrototypeOf()
function object1() {}
function object2() {}
object1.prototype = Object.create(object2.prototype) // 赋值表达式右边返回的对象(假设为x)的prototype是object2的prototype
const object3 = new object1() // object3是object1的实例,他们两个是相等的
console.log(object1.prototype.isPrototypeOf(object3)) // expected output: true
console.log(object2.prototype.isPrototypeOf(object3)) // expected output: true
# 2.6 Object.prototype.propertyIsEnumerable()
属性是否可枚举