文章目录
- Object介绍
- Object.prototype
- Object.prototype的属性
- Object.prototype的方法(Object的实例方法)
- Object的方法(静态方法)
- Object.keys()、Object.getOwnPropertyNames()
- Object.assign()
- Object.create()
- Object.defineProperty()——Vue响应式原理
- Object.defineProperties()
- Object.entries()
- Object.freeze()、 Object.isFrozen()
- Object.getOwnPropertyDescriptor()、Object.getOwnPropertyDescriptors()
- Object.getPrototypeOf()
- Object.is()
- Object.values()
- Object.setPrototypeOf()
- Object.getOwnPropertyNames()
Object介绍
Object是JavaScript所有对象的父类,其他对象都继承于Object。
Object构造函数为给定值创建一个对象包装器。如果给定值是null或undefined,将会创建一个空对象。
当以非构造函数被调用时,Object相当于new Object()
Object独享的原生方法分为两类:Object本身的方法与Object的实例方法
本身的方法就是定义在Object对象上的方法,通过Object.直接使用
Object的实例方法是定义在Object原型对象Object.prototype上的方法,被Object实例直接使用
Object.prototype
prototype是Object自带的一个属性,是Object的原型,本身也是一个对象,因此也有一些属性和方法
Object.prototype的属性
属性 | 默认值 |
---|---|
configurable | false |
enumerable | false |
writable | false |
configurable 决定能否使用delete、能否需改属性特性、或能否修改访问器属性;
enumerable决定对象属性能否通过for - in循环来获取,默认值为false不可循环
writable决定对象属性是否可被修改,默认值false为不可修改
constructor用于指向原型对象的构造函数,可以修改其值来改变原型对象的构造函数
Object.prototype的方法(Object的实例方法)
Object.prototype.valueOf()
Object.prototype.valueOf():返回当前对象对应的值,如下表所示。valueOf()可通过实例直接调用
对象 | 返回值 |
---|---|
Array | 返回数组对象本身。 |
Boolean | 布尔值。 |
Date | 存储的时间是从 1970 年 1 月 1 日午夜开始计的毫秒数 UTC。 |
Function | 函数本身。 |
Number | 数字值。 |
Object | 对象本身。这是默认情况。 |
String | 字符串值。 |
Math 和 Error 对象没有 valueOf 方法。 |
console.log([1, 2, 3, 'aa'].valueOf()); //[1, 2, 3, 'aa']
console.log('ssss'.valueOf()); //ssss
console.log(true.valueOf()); //true
console.log(new Date().valueOf()); //1587481454574
Object.prototype.toString()
Object.prototype.toString()是返回一个对象的字符串形式
Object的toSting()方法并没有什么实际用途,但是实例自定义的方法,如字符串、数组等自定义的toSting()方法能够得到实例转换成字符串的形式
返回的都是字符串
console.log(new Object().toString()); //[object Object]
console.log([1, 2, 3, 'aa'].toString()); //1,2,3,aa
console.log('ssss'.toString()); //ssss
console.log(true.toString()); //true
console.log(new Date().toString()); //Tue Apr 21 2020 23:25:55 GMT+0800 (中国标准时间)
Object.prototype.toString.call()——面试常考题
console.log(new Object().toString()); //[object Object]中返回的后一个Object是指定的该值的构造函数,因此根据这个方法可以用于判断数据类型的方法。但是实例对象自定义的toString()方法会覆盖Object.prototype.toString方法,因此需要使用call来改变this,通过Object.prototype.toString.call()方法可以判断值的类型。
console.log(new Object().toString()); //[object Object]
console.log(Object.prototype.toString.call([1, 2, 3, 'aa'])); //[object Array]
console.log(Object.prototype.toString.call('ssss')); //[object String]
console.log(Object.prototype.toString.call(true)); //[object Boolean]
console.log(Object.prototype.toString.call(new Date())); //[object Date]
类型 | 返回值 |
---|---|
数值 | [object Number] |
字符串 | [object String] |
布尔值 | [object Boolean] |
undefined | [object Undefined] |
null | [object Null] |
数组 | [object Array] |
arguments对象 | [object Argument] |
函数 | [object Function] |
Error对象 | [object Error] |
Date对象 | [object Date] |
RegExp对象 | [object RegExp] |
其他对象 | [object Object] |
Object.prototype.hasOwnProperty()
Object.prototype.hasOwnProperty():该方法会返回一个布尔值,指定对象自身属性中是否存在指定的属性,并且这个属性是非原型链上继承的,in运算符会获取从原型链上继承的属性。只要有该属性便返回true,即使该属性值为null或undefined
let person = {
name: 'simon',
age: 24,
job: undefined,
girlFriend: null
};
console.log(person.hasOwnProperty('name')); //true
console.log(person.hasOwnProperty('job')); //true
console.log(person.hasOwnProperty('girlFriend')); //true
console.log(person.hasOwnProperty('toString')); //false
Object.prototype.isPrototypeOf()
prototypeobj.isPrototypeOf(object):用于判断object对象是否存在于另外prototyepeobj对象的原型链上,返回一个布尔值。
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
instanceof运算符
instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上,返回一个布尔值。
// 定义构造函数
function C(){}
function D(){}
var o = new C();
o instanceof C; // true,因为 Object.getPrototypeOf(o) === C.prototype
o instanceof D; // false,因为 D.prototype 不在 o 的原型链上
o instanceof Object; // true,因为 Object.prototype.isPrototypeOf(o) 返回 true
C.prototype instanceof Object // true,同上
C.prototype = {};
var o2 = new C();
o2 instanceof C; // true
o instanceof C; // false,C.prototype 指向了一个空对象,这个空对象不在 o 的原型链上.
D.prototype = new C(); // 继承
var o3 = new D();
o3 instanceof D; // true
o3 instanceof C; // true 因为 C.prototype 现在在 o3 的原型链上
String对象会出现一些特殊情况
let str = 'aaaaa';
let newStr = new String('aaaa');
console.log(str instanceof String); //false
console.log(str instanceof Object); //false
console.log(newStr instanceof String); //true
console.log(newStr instanceof Object); //true
Object.prototype.propertyIsEnumerable()
obj.propertyIsEnumerable(prop):判断指定的属性是否可枚举。每个对象都有一个propertyIsEnumerable方法,该方法可以确定对象中指定的属性是否可以被for-in循环枚举。返回一个布尔值。
let obj = {
prop: 'is Enumerable',
}
obj.propertyIsEnumerable('prop'); //true
let arr = [];
arr[0] = 'is Enumerable too';
arr.propertyIsEnumerable(0); //true
arr.propertyIsEnumerable('length'); //false
//内置对象
Math.propertyIsEnumerable('random'); //false
this.propertyIsEnumerable('Math'); //false
Object的方法(静态方法)
Object.keys()、Object.getOwnPropertyNames()
Object.keys()、Object.getOwnPropertyNames()都是用于遍历对象的属性
Object.keys(obj)方法的参数是一个对象,并返回一个包含该对象属性的数组。数组中的属性是对象自身的,不是继承的属性。因此该方法返回的是自身的属性
Object.getOwnPropertyNames(obj)方法与Object.keys(obj)类似,参数是一个对象,返回值是一个包含对象属性的数组。并且是该对象本身的,不是继承的属性。
两个方法的区别在于:
Object.keys(obj)返回的是可枚举的属性,而Object.getOwnPropertyNames(obj)还可以返回不可枚举的属性
let person = {
name: 'simon',
age: 24,
job: undefined,
girlFriend: null
};
console.log(Object.keys(person)); // ["name", "age", "job", "girlFriend"]
console.log(Object.getOwnPropertyNames(person)); // ["name", "age", "job", "girlFriend"]
let arr = [1,2,'aaa'];
console.log(Object.keys(arr)); //["0", "1", "2"]
console.log(Object.getOwnPropertyNames(arr)); //["0", "1", "2", "length"]
//通过这两个方法可用于获取对象属性的个数
console.log(Object.keys(person).length); //4
console.log(Object.getOwnPropertyNames(person).length); //4
Object.assign()
Object.assign(target, …sources):用于将所有**
可枚举属性
的值从一个或多个源对象复制到目标对象,并返回目标对象。继承属性和不可枚举数据不能被复制
**如果目标对象中的属性具有相同的键,则属性将被源对象中的属性覆盖,并且后面的源对象也会类似的覆盖前面的源对象属性。
//复制一个对象
const obj = {a:1};
const copy = Object.assign({},obj);
console.log(copy); //{a:1}
//合并对象
const o1 = { a: 1 };
const o2 = { b: 2 };
const o3 = { c: 3 };
const obj = Object.assign(o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }
console.log(o1); // { a: 1, b: 2, c: 3 }, 由于o1作为目标对象,因此也会发生改变
//合并具有相同属性类型的对象
const o1 = { a: 1, b: 1, c: 1 };
const o2 = { b: 2, c: 2 };
const o3 = { c: 3 };
const obj = Object.assign({}, o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 } 后面的对象中的属性会将前面对象的相同的属性所覆盖
该方法无法用于深拷贝,因为Object.assign()拷贝的是属性值,若源对象的属性值是一个对象的引用,那它也只指向那个引用
let obj1 = { a: 0 , b: { c: 0}};
let obj2 = Object.assign({}, obj1);
console.log(JSON.stringify(obj2)); // { a: 0, b: { c: 0}}
obj1.a = 1;
console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 0}}
console.log(JSON.stringify(obj2)); // { a: 0, b: { c: 0}}
obj2.b.c = 3;
console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 3}}
console.log(JSON.stringify(obj2)); // { a: 2, b: { c: 3}}
// Deep Clone
obj1 = { a: 0 , b: { c: 0}};
let obj3 = JSON.parse(JSON.stringify(obj1));
obj1.a = 4;
obj1.b.c = 4;
console.log(JSON.stringify(obj3)); // { a: 0, b: { c: 0}}
Object.create()
Object.create(obj, [,propertiesObject]):可创建一个拥有指定原型和若干个指定属性的对象。obj是新创建对象的原型对象,propertiesObject是可选参数,默认为undefined。如果有值的话是要添加到新创建对象的不可枚举(默认)属性(即自身的属性,而不是其原型链上的枚举属性)。方法返回一个新对象。
//用 Object.create实现类式继承多个对象
function MyClass() {
SuperClass.call(this);
OtherSuperClass.call(this);
}
// 继承一个类
MyClass.prototype = Object.create(SuperClass.prototype);
// 混合其它
Object.assign(MyClass.prototype, OtherSuperClass.prototype);
// 重新指定constructor
MyClass.prototype.constructor = MyClass;
MyClass.prototype.myMethod = function() {
// do a thing
};
//使用 Object.create 的 propertyObject参数
var o;
// 创建一个原型为null的空对象
o = Object.create(null);
o = {};
// 以字面量方式创建的空对象就相当于:
o = Object.create(Object.prototype);
o = Object.create(Object.prototype, {
// foo会成为所创建对象的数据属性
foo: {
writable:true,
configurable:true,
value: "hello"
},
// bar会成为所创建对象的访问器属性
bar: {
configurable: false,
get: function() { return 10 },
set: function(value) {
console.log("Setting `o.bar` to", value);
}
}
});
function Constructor(){}
o = new Constructor();
// 上面的一句就相当于:
o = Object.create(Constructor.prototype);
// 当然,如果在Constructor函数中有一些初始化代码,Object.create不能执行那些代码
// 创建一个以另一个空对象为原型,且拥有一个属性p的对象
o = Object.create({}, { p: { value: 42 } })
// 省略了的属性特性默认为false,所以属性p是不可写,不可枚举,不可配置的:
o.p = 24
o.p
//42
o.q = 12
for (var prop in o) {
console.log(prop)
}
//"q"
delete o.p
//false
//创建一个可写的,可枚举的,可配置的属性p
o2 = Object.create({}, {
p: {
value: 42,
writable: true,
enumerable: true,
configurable: true
}
});
Object.defineProperty()——Vue响应式原理
Object.defineProperty(obj, prop, descriptor)方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象
通过该方法添加的属性是可枚举的,在枚举对象属性时会被枚举到(for-in或Object.keys方法),可以改变这些属性的值,也可以删除这些属性。
属性 | 描述 |
---|---|
obj | 要定义属性的对象 |
prop | 要定义或修改的属性的名称 |
descriptor | 要定义或修改的属性描述符 |
对象(prop)中存在两种属性描述符:数据描述符和存取描述符。数据描述符是一个具有值的属性,该值是可写的,也可以是不可写的。存取描述符是由getter函数和setter函数所描述的属性。一个描述符只能是两者之一。
这两种描述符都是对象,并共享以下可选的键值(若不选择则在定义属性时选择默认值),即数据描述符和存取描述符都具有以下四个键值
属性 | 默认值 | 描述 |
---|---|---|
configurable | false | 当且仅当该属性的configurable键值为true时,该属性的描述符才能被改变,同时该属性也能从对应的对象上被删除 |
enumerable | false | 当且仅当该属性的enumerable键值为true时,该属性才会出现在对象的枚举属性中(for-in循环和Object.keys()方法) |
writable | false | 当且仅当该属性的writable键值为true时,属性的值,也就是value,才能被改变。当为false时,value不能被重新赋值 |
value | undefined | 该属性对应的值,可以是任何数据类型(数值,对象,函数等) |
存取描述符还具有以下可选键值:
get:属性的getter函数,如果没有getter,则为undefined。当访问该属性时,就会默认调用此函数。默认为undefined
set:属性的setter函数,如果没有setter,则为undefined。当属性值被修改时,就会默认调用此函数。该方法会传入一个参数(也就是被赋予的新值)。默认为undefined
描述符可拥有的键值
configurable | enumerable | value | writable | get | set | |
---|---|---|---|---|---|---|
数据描述符 | 可以 | 可以 | 可以 | 可以 | 不可以 | 不可以 |
存取描述符 | 可以 | 可以 | 不可以 | 不可以 | 可以 | 可以 |
var o = {}; // 创建一个新对象
// 在对象中添加一个属性与数据描述符的示例
Object.defineProperty(o, "a", {
value : 37,
writable : true,
enumerable : true,
configurable : true
});
// 对象 o 拥有了属性 a,值为 37
// 在对象中添加一个设置了存取描述符属性的示例
var bValue;
Object.defineProperty(o, "b", {
// 使用了方法名称缩写(ES2015 特性)
// 下面两个缩写等价于:
// get : function() { return bValue; },
// set : function(newValue) { bValue = newValue; },
get() { return bValue; },
set(newValue) { bValue = newValue; },
enumerable : true,
configurable : true
});
o.b; // 38
// 对象 o 拥有了属性 b,值为 38
// 现在,除非重新定义 o.b,o.b 的值总是与 bValue 相同
// 数据描述符和存取描述符不能混合使用
Object.defineProperty(o, "conflict", {
value: 0x9f91102,
get() { return 0xdeadbeef; }
});
添加属性的两种方式
var o = {};
o.a = 1;
// 等同于:
Object.defineProperty(o, "a", {
value: 1,
writable: true,
configurable: true,
enumerable: true
});
// 另一方面,
Object.defineProperty(o, "a", { value : 1 });
// 等同于:
Object.defineProperty(o, "a", {
value: 1,
writable: false,
configurable: false,
enumerable: false
});
Object.defineProperties()
Object.defineproperties(obj, props)方法直接在一个对象上定义新的属性或修改现有属性,并返回该对象。这个方法与Object.defineProperty()类似,但是可以对一个对象定义多个新的属性
var obj = {};
Object.defineProperties(obj, {
'property1': {
value: true,
writable: true
},
'property2': {
value: 'Hello',
writable: false
}
// etc. etc.
});
Object.entries()
object.entries(obj)方法返回一个数字,其元素是与直接在obj上找到可枚举属性键值对相对应的数组。属性的顺序与for-in循环对象的属性值所给的顺序相同(但是该方法只会返回自身的属性,而不会返回继承的可枚举属性,for-in循环还会枚举原型链中的可枚举属性)
const obj = { foo: 'bar', baz: 42 };
console.log(Object.entries(obj)); // [ ['foo', 'bar'], ['baz', 42] ]
const obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.entries(obj)); // [ ['0', 'a'], ['1', 'b'], ['2', 'c'] ]
const anObj = { 100: 'a', 2: 'b', 7: 'c' };
console.log(Object.entries(anObj)); // [ ['2', 'b'], ['7', 'c'], ['100', 'a'] ]
//使用for-of循环Object.entries()数组
const obj = { a: 5, b: 7, c: 9 };
for (const [key, value] of Object.entries(obj)) {
console.log(`${key} ${value}`); // "a 5", "b 7", "c 9"
}
Object.entries(obj).forEach(([key, value]) => {
console.log(`${key} ${value}`); // "a 5", "b 7", "c 9"
});
Object.freeze()、 Object.isFrozen()
Object.freeze(obj) 方法可以冻结一个对象。一个被冻结的对象再也不能被修改;冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值,访问器属性(getter和setter)也一样使用。此外,冻结一个对象后该对象的原型也不能被修改。freeze() 返回和传入的参数相同的对象,而不是传递对象的一个被冻结的副本。
Object.isFrozen(obj)用于判断一个对象是否被冻结,返回一个布尔值
//冻结对象
var obj = {
prop: function() {},
foo: 'bar'
};
// 新的属性会被添加, 已存在的属性可能会被修改或移除
obj.foo = 'baz';
obj.lumpy = 'woof';
delete obj.prop;
// 作为参数传递的对象与返回的对象都被冻结,所以不必保存返回的对象(因为两个对象全等)
var o = Object.freeze(obj);
o === obj; // true
Object.isFrozen(obj); // === true
// 现在任何改变都会失效
obj.foo = 'quux'; // 静默地不做任何事
// 静默地不添加此属性
obj.quaxxor = 'the friendly duck';
// 试图通过 Object.defineProperty 更改属性,下面两个语句都会抛出 TypeError.
Object.defineProperty(obj, 'ohai', { value: 17 });
Object.defineProperty(obj, 'foo', { value: 'eit' });
// 也不能更改原型,下面两个语句都会抛出 TypeError.
Object.setPrototypeOf(obj, { x: 20 })
obj.__proto__ = { x: 20 }
//冻结数组
let a = [0];
Object.freeze(a); // 现在数组不能被修改了.
//修改无效
a[0]=1;
a.push(2);
//被冻结的 对象是不可变的,但是对象里面的对象是可变的,因为被冻结对象里面的对象只是一个引用,函数和数组都是对象
obj1 = {
internal: {},
arr: []
};
Object.freeze(obj1);
obj1.internal.a = 'aValue';
obj1.arr[0] = 1;
console.log(obj1.internal.a);// 'aValue'
console.log(obj1.arr[0]); //1
Object.getOwnPropertyDescriptor()、Object.getOwnPropertyDescriptors()
Object.getOwnPropertyDescriptor(obj, prop)方法返回指定对象上一个自有属性对应的属性描述符。自有属性是指直接赋予该对象的属性,而不是从原型链上进行查找的属性;
Object.getOwnPropertyDescriptors(obj)方法返回一个对象的所有自身属性的描述符;如果没有任何自身属性,则返回一个空对象
var o, d;
o = { get foo() { return 17; } };
d = Object.getOwnPropertyDescriptor(o, "foo");
console.log(d);
// d {
// configurable: true,
// enumerable: true,
// get: /*the getter function*/,
// set: undefined
// }
o = { bar: 42 };
d = Object.getOwnPropertyDescriptor(o, "bar");
console.log(d);
// d {
// configurable: true,
// enumerable: true,
// value: 42,
// writable: true
// }
o = {};
Object.defineProperty(o, "baz", {
value: 8675309,
writable: false,
enumerable: false
});
d = Object.getOwnPropertyDescriptor(o, "baz");
console.log(d);
// d {
// value: 8675309,
// writable: false,
// enumerable: false,
// configurable: false
// }
Object.getPrototypeOf()
Object.getPrototypeOf(obj)方法返回指定对象的原型(内部[[prototype]]属性的值)
var proto = {};
var obj = Object.create(proto);
Object.getPrototypeOf(obj) === proto; // true
var reg = /a/;
Object.getPrototypeOf(reg) === RegExp.prototype; // true
Object.is()
Object.is(value1,value2)方法判断两个值是否是相同的值,返回一个布尔值
如果下列任何一项成立,则两个值相同(返回true)
- 两个值都是 undefined
- 两个值都是 null
- 两个值都是 true 或者都是false
- 两个值是由相同个数的字符按照相同的顺序组成的字符串
- 两个值指向同一个对象
- 两个值都是数字并且
- 都是正零 +0
- 都是负零 -0
- 都是NaN
- 都是除零和 NaN 外的其它同一个数字
这个方法与 == 运算符不同,==运算符会将两边的值做隐式转换(若两者类型不同),然后去比较。但是Object.is()不会去做转换
Object.is()与 **===**也不同,===运算符会将+0 和-0视为相等,并认为NaN不等于NaN
Object.is('foo', 'foo'); // true
Object.is(window, window); // true
Object.is('foo', 'bar'); // false
Object.is([], []); // false,数组也是对象,但是这两个数组并不是同一个数组
var foo = { a: 1 };
var bar = { a: 1 };
Object.is(foo, foo); // true
Object.is(foo, bar); // false,foo和bar不是同一个对象
Object.is(undefined, undefined); // true
Object.is(null, null); // true
// 特例
Object.is(0, -0); // false
Object.is(0, +0); // true
Object.is(-0, -0); // true
Object.is(NaN, 0/0); // true
Object.values()
Object.values(obj)方法返回一个给定对象自身的所有可枚举属性值的数组,值的顺序与使用for-in循环的顺序相同(区别在于for-in会循环原型链上的属性)
这个方法与Object.kyes()方法类似,只不过该方式获取的是属性值,而keys()方法获取的是属性。
而Object.entries()方法获取的是属性和属性值共同组成的数组
var obj = { foo: 'bar', baz: 42 };
console.log(Object.values(obj)); // ['bar', 42]
var obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.values(obj)); // ['a', 'b', 'c']
Object.setPrototypeOf()
Object.setPrototypeOf(obj, prototype)方法设置一个指定的对象的原型(即[[prototyoe]]属性)到另外一个对象或null
Object.getOwnPropertyNames()
Object.getOwnPropertyNames(obj)返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括Symbol值作为名称的属性)组成的数组
var arr = ["a", "b", "c"];
console.log(Object.getOwnPropertyNames(arr).sort()); // ["0", "1", "2", "length"]
// 类数组对象
var obj = { 0: "a", 1: "b", 2: "c"};
console.log(Object.getOwnPropertyNames(obj).sort()); // ["0", "1", "2"]
若只需要获取可枚举属性,可以使用Object.keys或用for-in循环并用hasOwnProperty()过滤掉原型链上的可枚举属性
参考: https://javascript.ruanyifeng.com/stdlib/object.html#toc1
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object