前言
在 JavaScript 中,万物皆为对象,函数其实也是对象,数组其实也是另一种形式的对象,就连 ES6 新增的数据结构 Set、Map也其实都是对象中的一种
Object 里面其实有很多方法是很实用的,值得我们去研究
而且,所有的“对象”的原型链的末端都是 Object.prototype 对象,也就是说它们都可以访问到这个对象的属性/方法,所以研究 Object 和 Object.prototype 的属性/方法都是非常有必要的
我们利用 Object.getOwnPropertyNames() 方法查看 Object 和 Object.prototype 都有哪些属性/方法:
Object 的属性的特性
数据属性
它包含的是一个数据值的位置,在这可以对数据值进行读写
- var obj = { property: ... } 方法 / obj.property = ... 方法创建对象属性的默认值
configurable: true, // 1. 能通过delete删除属性从而重新定义属性;2. 能修改属性的特性
enumerable: true, // 属性可通过 for...in 枚举到
writable: true, // 属性的值可修改
value: ... // 属性的值
- Object.defineProperty() 方法创建对象属性的默认值
configurable: false, // 1. 不能通过delete删除属性从而重新定义属性;2. 不能修改属性的特性
enumerable: false, // 属性不可通过 for...in 枚举到
writable: false, // 属性的值不可修改
value: ... // 属性的值
访问器属性
configurable: true(默认值)// 1. 表示能否通过delete删除属性从而重新定义属性;2. 表示能否修改属性的特性
enumerable: true(默认值) // 表示能否通过for-in循环返回属性
get: undefined(默认值) // 在读取属性时调用的函数
set: undefined(默认值) // 在写入属性时调用的函数
值得注意的是:访问器属性不能直接定义,要通过 Object.defineProperty() 这个方法来定义
var obj = {
age: 18
}
Object.defineProperty(obj,"say",{ // 注意:这里必须是新方法
// 定义 getter
get: function() {
return this.age // 注意需要用 this 指向 obj 对象,否则会认为 age 是 window.age
},
// 定义 setter
set: function(value) {
if( this.age !== value ){
this.age = value
}
}
})
控制台输出 obj 对象:
测试其 say 属性的 set() 和 get() 方法如下:
Object 的方法
下面的方法的参数解读:
- obj:表示创建出来的实例对象
- property:表示某个属性的名称,注意是字符串形式(也就是说要加" ")
- props:表示包含了属性的属性的一个对象,如:
{
configurable: true, // 1. 能通过delete删除属性从而重新定义属性;2. 能修改属性的特性
enumerable: true, // 属性可通过 for...in 枚举到
writable: true, // 属性的值可修改
value: ... // 属性的值
}
- [props]:表示 props 的一个数组集合,格式为:
[
property1: {
configurable: true, // 1. 能通过delete删除属性从而重新定义属性;2. 能修改属性的特性
enumerable: true, // 属性可通过 for...in 枚举到
writable: true, // 属性的值可修改
value: ... // 属性的值
},
property2: {
configurable: true, // 1. 能通过delete删除属性从而重新定义属性;2. 能修改属性的特性
enumerable: true, // 属性可通过 for...in 枚举到
writable: true, // 属性的值可修改
value: ... // 属性的值
},
...
]
Object.defineProperty( obj , property , props)
两个功能:
- 新增属性,并设置其特性
- 对已有属性设置特性(前提是该属性的 configurable 值是 true 才可以,否则会报错)
var obj = {}
Object.defineProperty( obj , "name" , {
configurable: true, // 1. 能通过delete删除属性从而重新定义属性;2. 能修改属性的特性
enumerable: true, // 属性可通过 for...in 枚举到
writable: false, // 属性的值不可修改
value: "Jack" // 属性的值
})
console.log(obj) // {name: "Jack"}
obj.name = "Deck"
控制台输出,可以看到该属性的值没有被改变,但使用 Object.defineProperty() 方法即可改变其值
Object.defineProperties( obj , [props])
功能见 Object.defineProperty() 方法,只不过是可以同时设置多个属性的特性
var obj = {};
Object.defineProperties(obj, {
'name': {
configurable: true,
enumerable: true,
value: "Jack",
writable: true
},
'age': {
configurable: true,
enumerable: false,
value: 18,
writable: true
}
})
console.log(obj)
for(let i in obj){
console.log(i)
}
控制台可以把 name 属性和 age 属性都输出出来,但使用 for...in 循环遍历不到 age 属性
Object.create( __proto__ , [props] )
功能:
- 新建一个对象,传入第一个参数作为该对象的原型对象
- 第二个参数可选,可用于同时创建该对象本身的属性
const person = {
isHuman: false,
printIntroduction: function() {
console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`);
}
};
const me = Object.create(person);
- 注意:使用
.创建新属性,该属性是归属于该对象的本身的属性
me.name = 'Matthew';
- 注意:如果本身属性和原型的属性重名,以本身属性的值为准
me.isHuman = true; // inherited properties can be overwritten
me.printIntroduction();
这就意味着,虽然说每个对象会访问到的原型链上任何一个对象的属性/方法,但使用属性/方法时要是同名是不会干扰到的
Object.assign( { } , obj ) 、Object.assign( [ ] , arr )
功能:
- 用于拷贝 对象 / 数组
- 第一层为深拷贝,第二层及以上为浅拷贝 第一层是深拷贝:
var arr1 = [1,2,3]
var arr2 = Object.assign([],arr1)
第二层(指的是【对象属性/ 数组元素】是【对象 / 数组】)为浅拷贝
var obj1 = {
"info": {
"name": "Jack",
age: 18
},
gender: 1
}
var obj2 = Object.assign({},obj1)
Object.getOwnPropertyDescriptor( obj , property )
功能:获取实例对象的指定属性的数据属性
参数:
- obj: 要定义/修改属性的属性的对象
- propertyName: 要定义/修改属性的属性的名称
功能:通过
Object.getOwnPropertyDescriptor()方法,我们可以查看person对象里的自身属性 name 及其数据属性(注意,这里只是能查到自身属性的,并不能查到原型链上的)
Object.getOwnPropertyDescriptors(obj)
参数:obj: 要定义/修改属性的属性的对象
功能:和Object.getOwnPropertyDescriptor()方法一样,区别在于是获取到全部属性及其数据属性(包括 自身和原型 的 可枚举和不可枚举 的所有属性)
Object.defineProperty()是ES5中的新方法,IE9(IE8部分实现,只有dom对象才支持)以下浏览器不支持,一些旧的浏览器可以通过非标准方法Object.defineGetter()和Object.defineSetter()来设置
附加内容:
Vue 实例的 data 选项里面的对象的字段都还有各自的 setter 和 getter 方法:
直接打印出来会出现 get xxx 和 set xxx 的属性,其值即是 xxx 字段的 get 和 set 方法,用 Object.getOwnPropertyDescriptors 方法打印出来可以知道这些都属于
_ob_观察器里的内容
Object.getOwnPropertyNames( obj )
功能:获取实例对象的全部自身属性的名称(包括不可枚举的),以数组形式返回,每个元素均为字符串形式
Object.keys( obj )
功能:获取实例对象的全部自身属性的名称(只包括可枚举的),以数组形式返回,每个元素均为字符串形式
注意:for...in 和 Object.getOwnPropertyNames() 和 Object.keys() 的区别:
共同点:都是获取属性名称,返回数据类型为数组;
不同点:
- for...in:返回
自身和原型中所有可枚举的属性- Object.getOwnPropertyNames():可返回
自身所有属性,包括可枚举和不可枚举的属性- Object.keys():只返回自身所有
可枚举的属性
Object.values( obj )
功能:获取实例对象的全部自身的可枚举属性的值,以数组形式返回
Object.entries( obj )
功能:获取实例对象的全部自身的可枚举属性的名称及其值,以二元数组形式返回,每个元素均为[propertyName,value]
Object.fromEntries()
功能:是 Object.entries() 的逆方法,把二维数组变成对象
Object.is( value1 , value2)
功能:用于判断两个值是否为同一个值 Object.is() 方法判断两个值是否为同一个值。
判断规则:如果满足以下条件则两个值相等:
- 都是 undefined
- 都是 null
- 都是 true 或 false
- 都是相同长度的字符串且相同字符按相同顺序排列
- 都是相同对象(意味着每个对象有同一个引用)
- 都是数字且
- 都是 +0
- 都是 -0
- 都是 NaN
- 或都是非零而且非 NaN 且为同一个值
与 == 运算符、=== 运算符的区别:
- 与== 运算不同。 == 运算符在判断相等前对两边的变量(如果它们不是同一类型) 进行强制转换 (这种行为的结果会将 "" == false 判断为 true),而 Object.is() 不会强制转换两边的值。
- 与=== 运算也不相同。 === 运算符 (也包括 == 运算符) 将数字 -0 和 +0 视为相等 ,而将 Number.NaN 与 NaN 视为不相等。而 Object.is() 解决了这两个问题
Object.getPrototypeOf( obj )
功能:返回对象的原型(__proto__对象)
每次我们新建一个对象:
var obj = {}
var obj = new Object
var obj = new Fun
得到的对象的原型(__proto__)都是 Object.prototype,即上面第三行的输出内容
Object.setPrototypeOf( obj , __proto__ )
功能:设置一个指定的对象作为另一个对象的原型
Object.prototype 的方法
Object.prototype.hasOwnProperty( property )
功能:判断当前对象的某个属性是否为自身属性
Object.prototype.isPrototypeOf( obj )
功能:判断目标对象是否在当前对象的原型链上
Object.prototype.propertyIsEnumerable( property )
功能:检测当前对象的某个属性能否被 for...in 或 Object.keys() 遍历到
Object.prototype.valueOf()
JavaScript 调用 valueOf 方法将对象转换为原始值。你很少需要自己调用 valueOf 方法;当遇到要预期的原始值的对象时,JavaScript 会自动调用它。
默认情况下,valueOf 方法由 Object 后面的每个对象继承。 每个内置的核心对象都会覆盖此方法以返回适当的值。如果对象没有原始值,则v alueOf 将返回对象本身。
JavaScript的许多内置对象都重写了该函数,以实现更适合自身的功能需要。因此,不同类型对象的valueOf()方法的返回值和返回值类型均可能不同
Object.prototype.toString( obj )
功能:检测具体的数据类型。结合 call() 方法把要检测类型的数据放在 call() 里面
改变 Object.prototype.toString() 方法里面的 this 指向,规定其指向我们要检测的数据
var Type = function(obj){
return Object.prototype.toString.call( obj ).slice(8,-1).toLocaleLowerCase()
}
console.log(Type([1,2,1]))
console.log(Type({a: 6}))
console.log(Type(5))
console.log(Type("hhh"))
console.log(Type(undefined))
console.log(Type(null))
console.log(Type(false))
console.log(Type(Symbol))
Object.prototype.toLocaleString()
功能:返回一个该对象的字符串表示。此方法被用于派生对象为了特定语言环境的目的(locale-specific purposes)而重载使用
附加内容
给对象添加函数的四种写法的区别
// 写法1
var person1 = {
name: "p1",
sayThis() {
console.log(this);
}
};
// 写法2
var person2 = {
name: "p2",
sayThis: function() {
console.log(this);
}
};
// 写法3
var person3 = {
name: "p3",
sayThis:()=> {
console.log(this);
}
};
// 写法4
var person4 = {
name: "p4",
};
person4.sayThis = function(){
console.log(this)
}
- 写法1 和 写法2
写法1 和 写法2 没有区别,this 指向一致
- 写法1 和 写法3
this 指向的区别:
person1.sayThis(); // person1 对象
person3.sayThis(); // window 对象
- 写法2 和 写法4
-
从意义上理解:
- 写法2 相当于在创建一个对象的同时创建其中的函数方法
- 写法4 相当于创建对象之后再给它
继续添加函数
-
从 bug 的角度上看
- 写法2 会重置对象
- 写法4 会保留对象之前的属性和方法