**Object.prototype**
属性表示 Object
的原型对象
Object.prototype 属性的属性特性: | |
---|---|
writable | true |
enumerable | false |
configurable | true |
描述
几乎所有的 JavaScript 对象都是 Object 的实例;一个典型的对象继承了Object.prototype的属性(包括方法),尽管这些属性可能被遮蔽(亦称为覆盖)。但是有时候可能故意创建不具有典型原型链继承的对象,比如通过Object.create(null)创建的对象,或者通过Object.setPrototypeOf方法改变原型链。
改变Object原型,会通过原型链改变所有对象;除非在原型链中进一步覆盖受这些变化影响的属性和方法。这提供了一个非常强大的、但有潜在危险的机制来覆盖或扩展对象行为。
1、Object.assign()
Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
1.1 语法
Object.assign(target, ...sources) | |
---|---|
target | 目标对象 |
sources | 源对象 |
1.2 描述
如果目标对象中的属性具有相同的键,则属性将被源对象中的属性覆盖。后面的源对象的属性将类似地覆盖前面的源对象的属性。
Object.assign
方法只会拷贝源对象自身的并且可枚举的属性到目标对象。该方法使用源对象的[[Get]]
和目标对象的[[Set]]
,所以它会调用相关 getter 和 setter。因此,它分配属性,而不仅仅是复制或定义新的属性。如果合并源包含getter,这可能使其不适合将新属性合并到原型中。为了将属性定义(包括其可枚举性)复制到原型,应使用Object.getOwnPropertyDescriptor()
和Object.defineProperty()
。
String
类型和 Symbol
函数会返回symbol类型的属性都会被拷贝。
在出现错误的情况下,例如,如果属性不可写,会引发TypeError
,如果在引发错误之前添加了任何属性,则可以更改target
对象。
注意,Object.assign
不会跳过那些值为 null
或 undefined
的源对象。
1.3 示例
复制一个对象
const obj = { a: 1 };
const copy = Object.assign({}, obj);
console.log(copy); // { a: 1 }
深拷贝问题
针对深拷贝,需要使用其他办法,因为 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.a = 2;
console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 0}}
console.log(JSON.stringify(obj2)); // { a: 2, 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}}
所以要想实现深拷贝,可以使用JSON的API。也可以使用其他方法,可以参考这篇文章JavaScript 实现深拷贝
合并对象
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 }, 注意目标对象自身也会改变。
合并具有相同属性的对象,属性被后续参数中具有相同属性的其他对象覆盖。
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 }
继承属性和不可枚举属性是不能拷贝的
const obj = Object.create({foo: 1}, { // foo 是个继承属性。
bar: {
value: 2 // bar 是个不可枚举属性。
},
baz: {
value: 3,
enumerable: true // baz 是个自身可枚举属性。
}
});
const copy = Object.assign({}, obj);
console.log(copy); // { baz: 3 }
原始类型会被包装为对象
const v1 = "abc";
const v2 = true;
const v3 = 10;
const v4 = Symbol("foo")
const obj = Object.assign({}, v1, null, v2, undefined, v3, v4);
// 原始类型会被包装,null 和 undefined 会被忽略。
// 注意,只有字符串的包装对象才可能有自身可枚举属性。
console.log(obj); // { "0": "a", "1": "b", "2": "c" }
2、Object.create()
Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__
.
生成实例对象的常用方法是,使用new命令让构造函数返回一个实例。但是很多时候,只能拿到一个实例对象,它可能根本不是由构建函数生成的,那么能不能从一个实例对象,生成另一个实例对象呢?
JavaScript 提供了Object.create方法,用来满足这种需求。该方法接受一个对象作为参数,然后以它为原型,返回一个实例对象。该实例完全继承原型对象的属性。
2.1 语法
Object.create(proto, [propertiesObject])
参数:
proto
:新创建对象的原型对象。
propertiesObject
:可选。如果没有指定为 undefined
,则是要添加到新创建对象的可枚举属性(即其自身定义的属性,而不是其原型链上的枚举属性)对象的属性描述符以及相应的属性名称。这些属性对应Object.defineProperties()
方法直接在一个对象上定义新的属性或修改现有属性,并返回该对象。")的第二个参数。
返回值
一个新对象,带着指定的原型对象和属性。
2.2 示例
// 原型对象
let A = {
print: function () {
console.log('hello');
}
};
// 实例对象
let B = Object.create(A);
console.log(Object.getPrototypeOf(B) === A); // true
B.print(); // hello
console.log(B.print === A.print); // true
上面代码中,Object.create方法以A对象为原型,生成了B对象。B继承了A的所有属性和方法。
使用Object.create方法的时候,必须提供对象原型,即参数不能为空,或者不是对象,否则会报错。
Object.create();
// TypeError: Object prototype may only be an Object or null
Object.create(123);
// TypeError: Object prototype may only be an Object or null
Object.create方法生成的新对象,动态继承了原型。在原型上添加或修改任何方法,会立刻反映在新对象之上。
let obj1 = { p: 1 };
let obj2 = Object.create(obj1);
obj1.p = 2;
console.log(obj2.p) // 2
上面代码中,修改对象原型obj1会影响到实例对象obj2。
除了对象的原型,Object.create方法还可以接受第二个参数。该参数是一个属性描述对象,它所描述的对象属性,会添加到实例对象,作为该对象自身的属性。
// let obj = Object.create({}, {
// p1: {
// value: 123,
// enumerable: true,
// configurable: true,
// writable: true,
// },
// p2: {
// value: 'abc',
// enumerable: true,
// configurable: true,
// writable: true,
// }
// });
// 等同于
let obj = Object.create({});
obj.p1 = 123;
obj.p2 = 'abc';
console.log(obj.p1);//123
console.log(obj.p2);//bac
Object.create方法生成的对象,继承了它的原型对象的构造函数。所以可以使用Object.create实现类式继承,js实现继承,可以参考这篇文章JavaScript 继承
function A() {}
let a = new A();
let b = Object.create(a);
console.log(b.constructor === A); // true
console.log(b instanceof A); // true
上面代码中,b对象的原型是a对象,因此继承了a对象的构造函数A。
3、Object.defineProperties()
Object.defineProperties() 方法直接在一个对象上定义新的属性或修改现有属性,并返回该对象。
3.1语法
Object.defineProperties(obj, props)
3.2 参数
obj
在其上定义或修改属性的对象。
props
要定义其可枚举属性或修改的属性描述符的对象。对象中存在的属性描述符主要有两种:数据描述符和访问器描述符。描述符具有以下键:
configurable
true 当且仅当该属性描述符的类型可以被改变并且该属性可以从对应对象中删除。
默认为 false。
enumerable
true 当且仅当在枚举相应对象上的属性时该属性显现。
默认为 false
value
与属性关联的值。可以是任何有效的JavaScript值(数字,对象,函数等)。
默认为 undefined
writable
true当且仅当与该属性相关联的值可以用赋值运算改变时。
默认为 false
get
作为该属性的 getter 函数,如果没有 getter 则为undefined。数返回值将被用作属性的值。
默认为undefined
set
作为属性的 setter 函数,如果没有 setter 则为undefined。函数将仅接受参数赋值给该属性的新值。
默认为undefined
3.3 示例
Object.defineProperties本质上定义了obj 对象上props的可枚举属性相对应的所有属性。
var obj = {};
Object.defineProperties(obj, {
'property1': {
value: true,
writable: true
},
'property2': {
value: 'Hello',
writable: false
}
// etc. etc.
});
4、Object.defineProperty()
Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。
在ES6中,由于 Symbol类型的特殊性,用Symbol类型的值来做对象的key与常规的定义或修改不同,而Object.defineProperty 是定义key为Symbol的属性的方法之一。
4.1语法
Object.defineProperty(obj, prop, descriptor)
4.2参数
obj
要在其上定义属性的对象。
prop
要定义或修改的属性的名称。
descriptor
将被定义或修改的属性描述符。
4.3属性描述符
对象里目前存在的属性描述符有两种主要形式:数据描述符和存取描述符。数据描述符是一个具有值的属性,该值可能是可写的,也可能不是可写的。存取描述符是由getter-setter函数对描述的属性。描述符必须是这两种形式之一;不能同时是两者。
数据描述符和存取描述符均具有以下可选键值:configurable和enumerable。
数据描述符同时具有以下可选键值:value和writable。
存取描述符同时具有以下可选键值:get和set。
描述符可同时具有的键值
configurable | enumerable | value | writable | get | set | |
---|---|---|---|---|---|---|
数据描述符 | Yes | Yes | Yes | Yes | NO | NO |
存取描述符 | Yes | Yes | NO | NO | Yes | Yes |
如果一个描述符不具有value,writable,get 和 set 任意一个关键字,那么它将被认为是一个数据描述符。如果一个描述符同时有(value或writable)和(get或set)关键字,将会产生一个异常。
4.4 示例
4.4.1 创建属性
如果对象中不存在指定的属性,Object.defineProperty()就创建这个属性。当描述符中省略某些字段时,这些字段将使用它们的默认值。拥有布尔值的字段的默认值都是false。value,get和set字段的默认值为undefined。一个没有get/set/value/writable定义的属性被称为“通用的”,并被“键入”为一个数据描述符。
var o = {}; // 创建一个新对象
// 在对象中添加一个属性与数据描述符的示例
Object.defineProperty(o, "a", {
value : 37,
writable : true,
enumerable : true,
configurable : true
});
// 对象o拥有了属性a,值为37
// 在对象中添加一个属性与存取描述符的示例
var bValue;
Object.defineProperty(o, "b", {
get : function(){
return bValue;
},
set : function(newValue){
bValue = newValue;
},
enumerable : true,
configurable : true
});
o.b = 38;
// 对象o拥有了属性b,值为38
4.4.2 修改属性
如果属性已经存在,Object.defineProperty()将尝试根据描述符中的值以及对象当前的配置来修改这个属性。如果旧描述符将其configurable 属性设置为false,则该属性被认为是“不可配置的”,并且没有属性可以被改变(除了单向改变 writable 为 false)。当属性不可配置时,不能在数据和访问器属性类型之间切换。
当试图改变不可配置属性(除了value和writable 属性之外)的值时会抛出TypeError,除非当前值和新值相同。
Writable 属性
当writable属性设置为false时,该属性被称为“不可写”。它不能被重新分配。
var o = {};
Object.defineProperty(o, "a", {
value: 37,
writable: false
});
console.log(o.a); //37
o.a = 25; // 设置无效,不报错
console.log(o.a); //37
如示例所示,试图写入非可写属性不会改变它,也不会引发错误。
Enumerable 特性
enumerable定义了对象的属性是否可以在 for...in 循环和 Object.keys() 中被枚举。
let 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
// 如果使用直接赋值的方式创建对象的属性,则这个属性的enumerable为true
o.d = 4;
for (var i in o) {
console.log("可枚举的属性:"+i);
}
console.log(Object.keys(o)); // ["a", "d"]
Configurable 特性
configurable特性表示对象的属性是否可以被删除,以及除value和writable特性外的其他特性是否可以被修改。
let o = {};
Object.defineProperty(o, "a", {
configurable: false,
writable: true
});
// throws a TypeError
Object.defineProperty(o, "a", { configurable: true });
// throws a TypeError
Object.defineProperty(o, "a", { enumerable: true });
// throws a TypeError (set was undefined previously)
Object.defineProperty(o, "a", { set: function() {} });
// throws a TypeError (even though the new get does exactly the same thing)
Object.defineProperty(o, "a", {
get: function() {
return 1;
}
});
//writable为true,可以设置value
Object.defineProperty(o, "a", { value: 1 });
console.log(o.a); //1
//configurable为false,无法删除
delete o.a; // 无效,不报错
console.log(o.a); //1
Object.defineProperty(o, "a", { writable: false });
//writable为false,无法设置value
o.a=2;
console.log(o.a); //1
如果o.a的configurable属性为true,则不会抛出任何错误,并且该属性将在最后被删除。
添加多个属性和默认值
考虑特性被赋予的默认特性值非常重要,通常,使用点运算符和Object.defineProperty()为对象的属性赋值时,数据描述符中的属性默认值是不同的,如下例所示。
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
});
一般的 Setters 和 Getters
下面的例子展示了如何实现一个自存档对象。 当设置temperature 属性时,archive 数组会获取日志条目。
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;
console.log(arc.getArchive());
继承属性
如果访问者的属性是被继承的,它的 get 和set 方法会在子对象的属性被访问或者修改时被调用。如果这些方法用一个变量存值,该值会被所有对象共享。
function myclass() {
}
var value;
Object.defineProperty(myclass.prototype, "x", {
get() {
return value;
},
set(x) {
value = x;
}
});
var a = new myclass();
var b = new myclass();
a.x = 1;
console.log(b.x); // 1
这可以通过将值存储在另一个属性中解决。在 get 和 set 方法中,this 指向某个被访问和修改属性的对象。
function myclass() {}
Object.defineProperty(myclass.prototype, "x", {
get() {
return this.stored_x?this.stored_x:"我是默认值";
},
set(x) {
this.stored_x = x;
}
});
var a = new myclass();
var b = new myclass();
a.x = 1;
console.log(b.x); // 我是默认值
不像访问者属性,值属性始终在对象自身上设置,而不是一个原型。然而,如果一个不可写的属性被继承,它仍然可以防止修改对象的属性。
function myclass() {
}
myclass.prototype.x = 1;
Object.defineProperty(myclass.prototype, "y", {
writable: false,
value: 1
});
var a = new myclass();
a.x = 2;
console.log(a.x); // 2
console.log(myclass.prototype.x); // 1
a.y = 2; // Ignored, throws in strict mode
console.log(a.y); // 1
console.log(myclass.prototype.y); // 1
5、Object.entries()
Object.entries()
方法返回一个给定对象自身可枚举属性的键值对数组,其排列与使用 for...in
循环遍历该对象时返回的顺序一致(区别在于 for-in 循环也枚举原型链中的属性)。
5.1 语法
Object.entries(obj)
5.2 参数
obj
可以返回其可枚举属性的键值对的对象。
5.3 示例
Object.entries()返回一个数组,其元素是与直接在object上找到的可枚举属性键值对相对应的数组。属性的顺序与通过手动循环对象的属性值所给出的顺序相同。
const obj = { foo: 'bar', baz: 42 };
console.log(Object.entries(obj)); // [ ['foo', 'bar'], ['baz', 42] ]
5.4 将Object转换为Map
new Map()构造器接受一个可迭代的entries。借助Object.entries
方法你可以很容易的将Object
转换Map
。
var obj = { foo: "bar", baz: 42 };
var map = new Map(Object.entries(obj));
console.log(map); // Map { foo: "bar", baz: 42 }
6、Object.freeze()
Object.freeze() 方法可以冻结一个对象。一个被冻结的对象再也不能被修改;冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。此外,冻结一个对象后该对象的原型也不能被修改。freeze() 返回和传入的参数相同的对象。
6.1语法
Object.freeze(obj)
6.2 参数
obj
要被冻结的对象。
6.3 示例
被冻结对象自身的所有属性都不可能以任何方式被修改。任何修改尝试都会失败,无论是静默地还是通过抛出typeError异常
数据属性的值不可更改,访问器属性(有getter和setter)也同样(但由于是函数调用,给人的错觉是还是可以修改这个属性)。如果一个属性的值是个对象,则这个对象中的属性是可以修改的,除非它也是个冻结对象。数组作为一种对象,被冻结,其元素不能被修改。没有数组元素可以被添加或移除。
这个方法返回传递的对象,而不是创建一个被冻结的副本。
冻结对象
//一般对象
var obj = {
prop: function() {},
foo: "bar"
};
// 新的属性会被添加, 已存在的属性可能会被修改或移除
obj.foo = "baz"; //修改
obj.lumpy = "woof"; //添加
console.log(obj); //{prop: ƒ, foo: "baz", lumpy: "woof"}
delete obj.prop; //删除
console.log(obj); //{foo: "baz", lumpy: "woof"}
//冻结对象,作为参数传递的对象与返回的对象都被冻结,所以不必保存返回的对象(因为两个对象全等)
var o = Object.freeze(obj);
console.log(o === obj); // true
// 现在任何改变都会失效
obj.foo = "myFoo"; //修改属性
obj.bar = "the add property"; //添加属性
//无法修改
console.log(obj); //{foo: "baz", lumpy: "woof"}
// 在严格模式,如此行为将抛出 TypeErrors
function fail() {
"use strict";
console.log(delete obj.bar); // 返回true,因为bar属性从来未被添加
obj.foo = "myFoo"; // throws a TypeError
obj.bar = "the add property"; // throws a TypeError
}
// fail();
// 试图通过 Object.defineProperty 更改属性,下面两个语句都会抛出 TypeError.
// Object.defineProperty(obj, "foo", { value: 'myFoo' });
// Object.defineProperty(obj, "bar", { value: "the add property" });
// 也不能更改原型,下面两个语句都会抛出 TypeError.
//Object.setPrototypeOf(obj, { myName: 20 });
//obj.__proto__ = { myName: 20 };
冻结数组
let a = [0];
Object.freeze(a); // 现在数组不能被修改了.
a[0]=1; // fails silently
a.push(2); // fails silently
// In strict mode such attempts will throw TypeErrors
function fail() {
"use strict"
a[0] = 1;
a.push(2);
}
fail();
被冻结的对象是不可变的。但也不总是这样。下例展示了冻结对象不是常量对象(浅冻结)
obj1 = {
internal: {}
};
Object.freeze(obj1);
obj1.internal.a = 'aValue';
obj1.internal.a // 'aValue'
对于一个常量对象,整个引用图(直接和间接引用其他对象)只能引用不可变的冻结对象。冻结的对象被认为是不可变的,因为整个对象中的整个对象状态(对其他对象的值和引用)是固定的。注意,字符串,数字和布尔总是不可变的,而函数和数组是对象。
要使对象不可变,需要递归冻结每个类型为对象的属性(深冻结)。当你知道对象在引用图中不包含任何环(循环引用)时,将根据你的设计逐个使用该模式,否则将触发无限循环。对 deepFreeze() 的增强将是具有接收路径(例如Array)参数的内部函数,以便当对象进入不变时,可以递归地调用 deepFreeze() 。仍然有冻结不应冻结的对象的风险,例如[window]。
// 深冻结函数.
function deepFreeze(obj) {
// 取回定义在obj上的属性名
var propNames = Object.getOwnPropertyNames(obj);
// 在冻结自身之前冻结属性
propNames.forEach(function(name) {
var prop = obj[name];
// 如果prop是个对象,冻结它
if (typeof prop == 'object' && prop !== null)
deepFreeze(prop);
});
// 冻结自身(no-op if already frozen)
return Object.freeze(obj);
}
obj2 = {
internal: {}
};
deepFreeze(obj2);
obj2.internal.a = 'anotherValue';
obj2.internal.a; // undefined
7、Object.fromEntries()
Object.fromEntries() 把键值对列表转换为一个对象。
Object.fromEntries() 函数传入一个键值对的列表,并返回一个带有这些键值对的新对象。这个迭代参数应该是一个能够实现@iterator方法的的对象,返回一个迭代器对象。它生成一个具有两个元素的类似数组的对象,第一个元素是将用作属性键的值,第二个元素是与该属性键关联的值。
Object.fromEntries() 是 Object.entries的反转。
7.1语法
Object.fromEntries(iterable);
7.2参数
iterable
类似实现了可迭代协议Array或者Map或者其他对象的可迭代对象。
7.3示例
Map转Object
通过 Object.fromEntries, 可以将 Map转Object
const map = new Map();
map.set("foo", "bar");
map.set("baz", "1122");
const obj = Object.fromEntries(map);
console.log(obj); // { foo: "bar", baz: 1122 }
Array转Object
通过 Object.fromEntries, 可以将Array转Objcet
const arr = [ ['0', 'a'], ['1', 'b'], ['2', 'c'] ];
const obj = Object.fromEntries(arr);
console.log(obj); // { 0: "a", 1: "b", 2: "c" }
对象转换
Object.fromEntries 是Object.entries()的反函数,借用array manipulation methods 可以转换对象
const object1 = { a: 1, b: 2, c: 3 };
const object2 = Object.fromEntries(
Object.entries(object1)
.map(([ key, val ]) => [ key, val * 2 ])
);
console.log(object2);
// { a: 2, b: 4, c: 6 }
8、Object.getOwnPropertyDescriptor()
Object.getOwnPropertyDescriptor() 方法返回指定对象上一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)。
8.1 语法
Object.getOwnPropertyDescriptor(obj, prop)
8.2 参数
obj
需要查找的目标对象
prop
目标对象内属性名称
该方法允许对一个属性的描述进行检索。在 Javascript 中, 属性 由一个字符串类型的“名字”(name)和一个“属性描述符”(property descriptor)对象构成。更多关于属性描述符类型以及他们属性的信息可以查看:Object.defineProperty。
一个属性描述符是一个记录,由下面属性当中的某些组成的:
value
该属性的值(仅针对数据属性描述符有效)
writable
当且仅当属性的值可以被改变时为true。(仅针对数据属性描述有效)
get
获取该属性的访问器函数(getter)。如果没有访问器, 该值为undefined。(仅针对包含访问器或设置器的属性描述有效)
set
获取该属性的设置器函数(setter)。 如果没有设置器, 该值为undefined。(仅针对包含访问器或设置器的属性描述有效)
configurable
当且仅当指定对象的属性描述可以被改变或者属性可被删除时,为true。
enumerable
当且仅当指定对象的属性可以被枚举出时,为 true。
8.3 示例
var o, d;
o = {
get foo() {
return 17;
}
};
d = Object.getOwnPropertyDescriptor(o, "foo");
console.log(d);
9、Object.getOwnPropertyDescriptors()
Object.getOwnPropertyDescriptors() 方法用来获取一个对象的所有自身属性的描述符。
9.1 语法
Object.getOwnPropertyDescriptors(obj)
9.2 参数
obj
任意对象
9.3 示例
浅拷贝一个对象
Object.assign()方法只能拷贝源对象的可枚举的自身属性,同时拷贝时无法拷贝属性的特性们,而且访问器属性会被转换成数据属性,也无法拷贝源对象的原型,该方法配合 Object.create()方法可以实现上面说的这些。
Object.create(
Object.getPrototypeOf(obj),
Object.getOwnPropertyDescriptors(obj)
);
创建子类
创建子类的典型方法是定义子类,将其原型设置为超类的实例,然后在该实例上定义属性。这么写很不优雅,特别是对于 getters 和 setter 而言。 相反,您可以使用此代码设置原型:
function superclass() {}
superclass.prototype = {
// 在这里定义方法和属性
};
function subclass() {}
subclass.prototype = Object.create(superclass.prototype, Object.getOwnPropertyDescriptors({
// 在这里定义方法和属性
}));
10、Object.getOwnPropertyNames()
Object.getOwnPropertyNames()方法返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括Symbol值作为名称的属性)组成的数组。
10.1 语法
Object.getOwnPropertyNames(obj)
10.2 参数
obj
一个对象,其自身的可枚举和不可枚举属性的名称被返回。
10.3 示例
Object.getOwnPropertyNames() 返回一个数组,该数组对元素是 obj自身拥有的枚举或不可枚举属性名称字符串。 数组中枚举属性的顺序与通过for...in循环(或Objcet.keys)迭代该对象属性时一致。数组中不可枚举属性的顺序未定义。
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"]
// 使用Array.forEach输出属性名和属性值
Object.getOwnPropertyNames(obj).forEach(function(val, idx, array) {
console.log(val + " -> " + obj[val]);
});
// 输出
// 0 -> a
// 1 -> b
// 2 -> c
//不可枚举属性
var my_obj = Object.create({}, {
getFoo: {
value: function() { return this.foo; },
enumerable: false
}
});
my_obj.foo = 1;
console.log(Object.getOwnPropertyNames(my_obj).sort()); // ["foo", "getFoo"]
该方法不会获取到原型链上的属性:
function ParentClass() {}
ParentClass.prototype.inheritedMethod = function() {};
function ChildClass() {
this.prop = 5;
this.method = function() {};
}
ChildClass.prototype = new ParentClass;
ChildClass.prototype.prototypeMethod = function() {};
console.log(
Object.getOwnPropertyNames(
new ChildClass() // ["prop", "method"]
)
);
11、Object.getOwnPropertySymbols()
Object.getOwnPropertySymbols() 方法返回一个给定对象自身的所有 Symbol 属性的数组。
11.1 语法
Object.getOwnPropertySymbols(obj)
11.2 参数
obj
要返回 Symbol 属性的对象。
11.3 示例
与Object.getOwnPropertyNames()
类似,您可以将给定对象的所有符号属性作为 Symbol 数组获取。 请注意,Object.getOwnPropertyNames()
本身不包含对象的 Symbol 属性,只包含字符串属性。
因为所有的对象在初始化的时候不会包含任何的 Symbol,除非你在对象上赋值了 Symbol 否则Object.getOwnPropertySymbols()只会返回一个空的数组。
var obj = {};
var a = Symbol("a");
var b = Symbol.for("b");
obj[a] = "localSymbol";
obj[b] = "globalSymbol";
var objectSymbols = Object.getOwnPropertySymbols(obj);
console.log(objectSymbols.length); // 2
console.log(objectSymbols) // [Symbol(a), Symbol(b)]
console.log(objectSymbols[0]) // Symbol(a)
12.Object.getPrototypeOf()
Object.getPrototypeOf() 方法返回指定对象的原型(内部[[Prototype]]属性的值)。这是获取原型对象的标准方法。
12.1 语法
Object.getPrototypeOf(object)
12.2 参数
obj
要返回其原型的对象。
12.3 返回值
给定对象的原型。如果没有继承属性,则返回 null
。
12.4 示例
let Func = function () {};
let mFunc = new Func();
console.log(Object.getPrototypeOf(mFunc) === Func.prototype); // true
// 空对象的原型是 Object.prototype
console.log( Object.getPrototypeOf({}) === Object.prototype); // true
// Object.prototype 的原型是 null
console.log( Object.getPrototypeOf(Object.prototype) === null); // true
// 函数的原型是 Function.prototype
function f() {}
console.log(Object.getPrototypeOf(f) === Function.prototype); // true
let proto = {};
let obj = Object.create(proto);
console.log(Object.getPrototypeOf(obj) === proto);// true
let reg = /a/;
console.log(Object.getPrototypeOf(reg) === RegExp.prototype); // true
上面代码中,实例对象mFunc的原型是Func.prototype。(包含几种特殊对象的原型)。
12.5 说明
Object.getPrototypeOf(Object) 不是 Object.prototype
JavaScript中的 Object 是构造函数(创建对象的包装器)。
//一般用法是:
var obj = new Object();
// 所以:
console.log(Object.getPrototypeOf(Object)); // ƒ () { [native code] }
console.log(Object.getPrototypeOf(Function)); // ƒ () { [native code] }
console.log(Object.getPrototypeOf(Object) === Function.prototype); // true
Object.getPrototypeOf( Object )是把Object这一构造函数看作对象,返回的当然是函数对象的原型,也就是 Function.prototype。
而Object.prototype是通过Object构造出来的实例对象的原型。
console.log(Object.prototype === Object.getPrototypeOf(obj)); // true
console.log(Object.prototype === Object.getPrototypeOf({})); // true
13、Object.is()
**Object.is()**
方法判断两个值是否是相同的值。
13.1 语法
Object.is(value1, value2);
13.2 参数
value1
第一个需要比较的值。
value2
第二个需要比较的值。
13. 3 描述
Object.is()
判断两个值是否相同。如果下列任何一项成立,则两个值相同:
- 两个值都是
undefined
- 两个值都是
null
- 两个值都是
true
或者都是false
- 两个值是由相同个数的字符按照相同的顺序组成的字符串
- 两个值指向同一个对象
- 两个值都是数字并且
- 都是正零
+0
- 都是负零
-0
- 都是
NaN
- 都是除零和
NaN
外的其它同一个数字
- 都是正零
这种相等性判断逻辑和传统的 ==
运算不同,==
运算符会对它两边的操作数做隐式类型转换(如果它们类型不同),然后才进行相等性比较,(所以才会有类似 "" == false
等于 true
的现象),但 Object.is
不会做这种类型转换。
JavaScript 有两种比较方式:严格比较运算符和转换类型比较运算符。对于严格比较运算符(===)来说,仅当两个操作数的类型相同且值相等为 true,而对于被广泛使用的比较运算符(==)来说,会在进行比较之前,将两个操作数转换成相同的类型。对于关系运算符(比如 <=)来说,会先将操作数转为原始值,使它们类型相同,再进行比较运算。
这与 ===
运算符的判定方式也不一样。===
运算符(和==
运算符)将数字值 -0
和 +0
视为相等,并认为 Number.NaN
不等于 NaN
。
13.4 例子
console.log(Object.is("foo", "foo")); // true
console.log(Object.is(window, window)); // true
console.log(Object.is("foo", "bar")); // false
console.log(Object.is([], [])); // false
var foo = { a: 1 };
var bar = { a: 1 };
console.log(Object.is(foo, foo)); // true
console.log(Object.is(foo, bar)); // false
console.log(Object.is(null, null)); // true
// 特例
console.log(Object.is(0, -0)); // false
console.log(Object.is(0, +0)); // true
console.log(Object.is(-0, -0)); // true
console.log(Object.is(NaN, 0 / 0)); // true
13.5 Polyfill
if (!Object.is) {
Object.is = function(x, y) {
// SameValue algorithm
if (x === y) { // Steps 1-5, 7-10
// Steps 6.b-6.e: +0 != -0
return x !== 0 || 1 / x === 1 / y;
} else {
// Step 6.a: NaN == NaN
return x !== x && y !== y;
}
};
}
14 Object.isExtensible()
Object.isExtensible() 方法判断一个对象是否是可扩展的(是否可以在它上面添加新的属性)。
14.1 语法
Object.isExtensible(obj)
14.2 参数
obj
需要检测的对象
14.3 描述
默认情况下,对象是可扩展的:即可以为他们添加新的属性。以及它们的__proto__
(该特性已经从 Web 标准中删除,虽然一些浏览器目前仍然支持它,但也许会在未来的某个时间停止支持,请尽量不要使用该特性。)属性可以被更改。Object.preventExtensions
,Object.seal
或 Object.freeze
方法都可以标记一个对象为不可扩展(non-extensible)。
14.4 例子
// 新对象默认是可扩展的.
let empty = {};
console.log(Object.isExtensible(empty)); //true
//让一个对象变的不可扩展.
Object.preventExtensions(empty);
console.log(Object.isExtensible(empty)); //false
// 密封对象是不可扩展的.
let sealed = Object.seal({});
console.log( Object.isExtensible(sealed)); //false
// 冻结对象也是不可扩展.
let frozen = Object.freeze({});
console.log(Object.isExtensible(frozen)); //false
15、Object.isFrozen()
Object.isFrozen()方法判断一个对象是否被冻结。
15.1 语法
Object.isFrozen(obj)
15.2 参数
obj
被检测的对象
15.3 描述
一个对象是冻结的是指它不可扩展,所有属性都是不可配置的,且所有数据属性(即没有getter或setter组件的访问器的属性)都是不可写的。
15.4 例子
1.一个对象默认是可扩展的,所以它也是非冻结的
let emptyObj = {};
console.log(Object.isFrozen(emptyObj)); //false
.2.一个不可扩展的空对象同时也是一个冻结对象
let objFrozen = Object.preventExtensions(emptyObj);
console.log(Object.isFrozen(objFrozen)); //true
3.一个非空对象默认也是非冻结的
let oneProp = { p: 42 };
console.log(Object.isFrozen(oneProp)); //false
// 让这个对象变的不可扩展,并不意味着这个对象变成了冻结对象,
// 因为p属性仍然是可以配置的(而且可写的).
let onePropFrozen = Object.preventExtensions(oneProp);
console.log(Object.isFrozen(onePropFrozen)); //false
//如果删除了这个属性,则它会成为一个冻结对象.
delete oneProp.p;
console.log(Object.isFrozen(oneProp)); // true
4.一个不可扩展的对象,拥有一个不可写但可配置的属性,则它仍然是非冻结的
let nonWritable = { a: "123" };
Object.preventExtensions(nonWritable);
Object.defineProperty(nonWritable, "a", { writable: false }); // 变得不可写
console.log(Object.isFrozen(nonWritable)); // false
// 再把这个属性改为不可配置,会让这个对象成为冻结对象.
Object.defineProperty(nonWritable, "a", { configurable: false }); // 变得不可配置
console.log(Object.isFrozen(nonWritable)); //true
5.一个不可扩展的对象,拥有一个不可配置但可写的属性,则它仍然是非冻结的
let nonConfigurable = { b: 456 };
Object.preventExtensions(nonConfigurable);
Object.defineProperty(nonConfigurable, "b", {
configurable: false
});
console.log(Object.isFrozen(nonConfigurable)); //false
// 再把这个属性改为不可写,会让这个对象成为冻结对象.
Object.defineProperty(nonConfigurable, "b", { writable: false });
console.log(Object.isFrozen(nonConfigurable)); //true
6.一个不可扩展的对象,值拥有一个访问器属性,则它仍然是非冻结的.
var accessor = {
get food() {
return "abc";
}
};
Object.preventExtensions(accessor);
console.log(Object.isFrozen(accessor)); //false
//但把这个属性改为不可配置,会让这个对象成为冻结对象.
Object.defineProperty(accessor, "food", { configurable: false });
console.log(Object.isFrozen(accessor)); //true
冻结一个对象
//使用Object.freeze是冻结一个对象最方便的方法
var frozen = { c: 789 };
console.log(Object.isFrozen(frozen)); //false
Object.freeze(frozen);
console.log(Object.isFrozen(frozen)); //true
// 一个冻结对象也是一个密封对象.
console.log(Object.isSealed(frozen)); //true
//更是一个不可扩展的对象.
console.log(Object.isExtensible(frozen)); //false
16、Object.preventExtensions()
Object.preventExtensions()方法让一个对象变的不可扩展,也就是永远不能再添加新的属性。
16.1 语法
Object.preventExtensions(obj)
16.2 参数
obj
将要变得不可扩展的对象
16.3 描述
如果一个对象可以添加新的属性,则这个对象是可扩展的。Object.preventExtensions()将对象标记为不再可扩展,因此它将永远不会具有超出它被标记为不可扩展的属性。注意,一般来说,不可扩展对象的属性可能仍然可被删除。尝试将新属性添加到不可扩展对象将静默失败或抛出TypeError(最常见但不排除其他情况,如在strict mode中)。
Object.preventExtensions()仅阻止添加自身的属性。但属性仍然可以添加到对象原型。
一旦使其不可扩展,就无法再对象进行扩展。
16.4 例子
// Object.preventExtensions将原对象变的不可扩展,并且返回原对象.
let obj = {};
let obj2 = Object.preventExtensions(obj);
console.log(obj === obj2); // true
// 字面量方式定义的对象默认是可扩展的.
let empty = {};
console.log(Object.isExtensible(empty)); //true
//变的不可扩展
Object.preventExtensions(empty);
Object.isExtensible(empty);//false
// 使用Object.defineProperty方法为一个不可扩展的对象添加新属性会抛出异常.
let nonExtensible = { removable: true };
Object.preventExtensions(nonExtensible);
nonExtensible.addNew=2134;//不报错,但是无法添加
console.log(nonExtensible);//{removable: true}
Object.defineProperty(nonExtensible, "new", { value: 123456 }); // 抛出TypeError异常
// 一个不可扩展对象的原型是不可更改的,__proto__是个非标准属性,可以更改一个对象的原型.
let fixed = Object.preventExtensions({});
fixed.__proto__ = { oh: "hai" }; // 抛出TypeError异常
17、Object.isSealed()
Object.isSealed() 方法判断一个对象是否被密封。
17.1 语法
Object.isSealed(obj)
17.2 参数
obj
要被检查的对象
17.3 描述
如果这个对象是密封的,则返回 true
,否则返回 false
。
密封对象是指那些不可 扩展
的,且所有自身属性都不可配置且因此不可删除(但不一定是不可写)的对象。
17.4 例子
1.对象默认不是密封的
let obj= {
id:103
};
console.log(Object.isSealed(obj));//false
2.空对象变的不可扩展,则它同时也会变成个密封对象
let empty = {};
Object.preventExtensions(empty);
console.log(Object.isSealed(empty)); //true
3.对象不是空对象,即使变的不可扩展,它不会变成密封对象,因为密封对象的所有自身属性必须是不可配置的;如果把这个属性变的不可配置,则这个对象也就成了密封对象
let hasProp = { a: "abcd!" };
Object.preventExtensions(hasProp);
console.log(Object.isSealed(hasProp)); //false
Object.defineProperty(hasProp, "a", { configurable: false });
console.log(Object.isSealed(hasProp)); //true
4.生成一个密封对象
//最简单的使用Object.seal
let sealed = {};
Object.seal(sealed);
console.log(Object.isSealed(sealed));
//根据定义:对象不可扩展且属性不可配置的
let obj = {};
//定义属性不可配置
Object.defineProperty(obj, "key", {
enumerable: true,
configurable: false,
writable: true,
value: "112233"
});
//定义对象不可扩展
Object.preventExtensions(obj);
console.log(Object.isSealed(obj));//true
5.密封对象同时也是不可扩展的
console.log(Object.isExtensible(sealed));//false
6.密封对象也可以是一个冻结对象,也可以不是,因为密封对象不考虑可写不可写,只考虑是否可配置
//密封对象也可以是一个冻结对象,但不是必须的
console.log(Object.isFrozen(sealed));//true
//密封对象也可以不是一个冻结对象,属性"p"可写
let s2 = Object.seal({ p: 3 });
console.log(Object.isFrozen(s2));//false
//访问器属性不考虑可写不可写,只考虑是否可配置
let s3 = Object.seal({ get p() { return 0; } });
console.log(Object.isFrozen(s3));//true
18、Object.keys()
Object.keys() 方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和使用 for...in 循环遍历该对象时返回的顺序一致 。
18.1 语法
Object.keys(obj)
18.2 参数
obj
要返回其枚举自身属性的对象
18.3 返回值
一个表示给定对象的所以可枚举的字符串数组
18.4 描述
Object.keys 返回一个所有元素为字符串的数组,其元素来自于从给定的object上面可直接枚举的属性。这些属性的顺序与手动遍历该对象属性时的一致。
18.5 例子
// simple array
let arr = ["a", "b", "c"];
console.log(Object.keys(arr)); //['0', '1', '2']
// array like object
let obj = { 0: "a", 1: "b", 2: "c" };
console.log(Object.keys(obj)); //['0', '1', '2']
// array like object with random key ordering
let anObj = { 100: "a", 2: "b", 7: "c" };
console.log(Object.keys(anObj)); //['2', '7', '100']
// getFoo的enumerable为false,getBar的enumerable为true
let myObj = Object.create(
{},
{
getFoo: {
enumerable: false,
value: function() {
return this.foo;
}
},
getBar: {
enumerable: true,
value: function() {
return this.bar;
}
}
}
);
myObj.foo = 1;//enumerable为true
myObj.bar = 2;//enumerable为true
console.log(Object.keys(myObj)); //["getBar", "foo", "bar"]
19、Object.values()
Object.values()方法返回一个给定对象自身的所有可枚举属性值的数组,值的顺序与使用for...in循环的顺序相同 ( 区别在于 for-in 循环枚举原型链中的属性 )。
19.1 语法
Object.values(obj)
19.2 参数
obj
被返回可枚举属性值的对象
19.3 返回值
一个包含对象自身的所有可枚举属性值的数组
19.4 描述
Object.values()返回一个数组,其元素是在对象上找到的可枚举属性值。属性的顺序与通过手动循环对象的属性值所给出的顺序相同。
18.5 例子
let obj1 = { foo: "bar", baz: 42 };
console.log(Object.values(obj1)); // ['bar', 42]
// array like object
let obj2 = { 0: "a", 1: "b", 2: "c" };
console.log(Object.values(obj2)); // ['a', 'b', 'c']
// array like object with random key ordering
//当我们使用数字键时,返回值的顺序根据键以数字顺序来排序
let an_obj = { 100: "a", 2: "b", 7: "c" };
console.log(Object.values(an_obj)); // ['b', 'c', 'a']
// getFoo is property which isn't enumerable
let my_obj = Object.create(
{},
{
getFoo: {
enumerable:false,//默认为false
value: function() {
return this.foo;
}
}
}
);
my_obj.foo = "bar";
console.log(Object.values(my_obj)); // ['bar']
// 非对象参数将被强制转为为对象
console.log(Object.values("foo")); // ['f', 'o', 'o']
20.Object.setPrototypeOf()
Object.setPrototypeOf()方法为参数对象设置原型,返回该参数对象。
20.1语法
Object.setPrototypeOf(obj, prototype)
20.2参数
obj
要设置其原型的对象
prototype
该对象的新原型(一个对象 或 `null).
20.3 描述
如果对象的[[Prototype]]被修改成不可扩展(通过 Object.isExtensible()
查看),就会抛出 TypeError
异常。如果prototype参数不是一个对象或者null(例如,数字,字符串,boolean
,或者 undefined
),则什么都不做。否则,该方法将obj的[[Prototype]]修改为新的值。
Object.setPrototypeOf()是ECMAScript 6最新草案中的方法,相对于 Object.prototype.__proto__
,它被认为是修改对象原型更合适的方法
20.4 示例
let a = {};
let b = {x: 1};
Object.setPrototypeOf(a, b);
console.log(Object.getPrototypeOf(a) === b); // true
console.log(a.x); // 1
上面代码中,Object.setPrototypeOf方法将对象a的原型,设置为对象b,因此a可以共享b的属性。
new命令可以使用Object.setPrototypeOf方法模拟
let F = function () {
this.foo = 'bar';
};
let f = new F();
// 等同于
let f = Object.setPrototypeOf({}, F.prototype);
F.call(f);
上面代码中,new命令新建实例对象,其实可以分成两步。
第一步,将一个空对象的原型设为构造函数的prototype属性(上例是F.prototype);
第二步,将构造函数内部的this绑定这个空对象,然后执行构造函数,使得定义在this上面的方法和属性(上例是this.foo),都转移到这个空对象上。
21.Object.prototype.isPrototypeOf()
实例对象的isPrototypeOf方法,用于测试一个对象是否存在于另一个对象的原型链上。
21.1 语法
prototypeObj.isPrototypeOf(object)
21.2 参数
object
在该对象的原型链上搜寻
prototypeObj
搜寻的原型对象
21.3示例
let o1 = {};
let o2 = Object.create(o1);
let o3 = Object.create(o2);
console.log(o2.isPrototypeOf(o3)); // true
console.log(o1.isPrototypeOf(o3)); // true
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
上面代码中,o1和o2都是o3的原型。这表明只要实例对象处在参数对象的原型链上,isPrototypeOf方法都返回true。
console.log(Object.prototype.isPrototypeOf({})); // true
console.log(Object.prototype.isPrototypeOf([])); // true
console.log(Object.prototype.isPrototypeOf(/xyz/)); // true
console.log(Object.prototype.isPrototypeOf(Object.create(null))); // false
上面代码中,由于Object.prototype处于原型链的最顶端,所以对各种实例都返回true,只有直接继承自null的对象除外。
22、Object.prototype.hasOwnProperty()
hasOwnProperty() 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性。
另外,hasOwnProperty方法是 JavaScript 之中唯一一个处理对象属性时,不会遍历原型链的方法。
22.1 语法
obj.hasOwnProperty(prop)
22.2 参数
prop
要检测的属性字符串名称或者Symbol
obj
检测的对象
22.3描述
所有继承了 Object
的对象都会继承到 hasOwnProperty
方法。这个方法可以用来检测一个对象是否含有特定的自身属性;和 in 运算符不同,该方法会忽略掉那些从原型链上继承到的属性。
22.4 示例
1.使用 hasOwnProperty 方法判断属性是否存在
let obj = new Object();
obj.prop = "exists";
function changeObj() {
obj.newProp = obj.prop;
delete obj.prop;
}
console.log(obj.hasOwnProperty("prop")); // 返回 true
changeObj();
console.log(obj.hasOwnProperty("prop")); // 返回 false
console.log(obj.hasOwnProperty("newProp")); // 返回 true
2.自身属性与继承属性
console.log(obj.hasOwnProperty("toString")); // 返回 false
console.log(obj.hasOwnProperty("hasOwnProperty")); // 返回 false
23、Object.prototype.propertyIsEnumerable()
propertyIsEnumerable() 方法返回一个布尔值,表示指定的属性是否可枚举。
23.1 语法
obj.propertyIsEnumerable(prop)
23.2 参数
prop
需要测试的属性名
obj
检测的对象
23.3描述
每个对象都有一个propertyIsEnumerable方法。此方法可以确定对象中指定的属性是否可以被for...in循环枚举,但是通过原型链继承的属性除外。如果对象没有指定的属性,则此方法返回false。
23.4示例
let arr = [];
arr[0] = 'is enumerable';
console.log(arr.propertyIsEnumerable(0));//true
console.log(arr.propertyIsEnumerable('length'));//false
24、Object.prototype._proto_
对象的_proto_属性(前后各两个下划线),返回该对象的原型。该属性可读写。
let obj = {};
let p = {};
obj.__proto__ = p;
console.log(Object.getPrototypeOf(obj) === p); // true
上面代码通过__proto__
属性,将p对象设为obj对象的原型。
根据语言标准,__proto__
属性只有浏览器才需要部署,其他环境可以没有这个属性。它前后的两根下划线,表明它本质是一个内部属性,不应该对使用者暴露。因此,应该尽量少用这个属性,而是用Object.getPrototypeOf()和Object.setPrototypeOf(),进行原型对象的读写操作。
原型链可以用__proto__
很直观地表示。
let A = {
name: '张三'
};
let B = {
name: '李四'
};
let proto = {
print: function () {
console.log(this.name);
}
};
A.__proto__ = proto;
B.__proto__ = proto;
A.print(); // 张三
B.print(); // 李四
console.log(A.print === B.print);// true
console.log(A.print === proto.print); // true
console.log(B.print === proto.print); // true
25、Object.prototype.constructor
每个对象都有一个返回创建实例对象的 Object 构造函数的引用。注意,此属性的值是对函数本身的引用,而不是一个包含函数名称的字符串。对原始类型来说,如1,true和"test",该值只可读。
let obj = {};
console.log(obj.constructor); //ƒ Object() { [native code] }
console.log(obj.constructor == Object); //true
function Fun() {
}
let fun = new Fun(); //实例化
console.log(fun.constructor); //ƒ Fun(){} :打印出来的引用是Fun函数,说明fun的引用是Fun函数
console.log(Fun.constructor); //ƒ Function() { [native code] } :打印出来的引用是Funcion函数,说明Fun的引用是Function函数
console.log(fun.constructor == Fun); //true :再次证明fun的constructor属性引用了fun对象的构造函数
console.log(fun.constructor == Fun.constructor); //false
上面代码,实例对象的constructor属性引用了该对象的构造函数。对于 Object 对象,该指针指向原始的 Object() 函数。
26、获取原型对象方法的比较
如上所述,proto属性指向当前对象的原型对象,即构造函数的prototype属性。
let obj = new Object();
console.log(obj.__proto__ === Object.prototype);// true
console.log(obj.__proto__ === obj.constructor.prototype);// true
上面代码,对象obj,它的_proto_属性,指向构造函数(Object或obj.constructor)的prototype属性。
因此,获取实例对象obj的原型对象,有三种方法:
1.obj._proto_
2.obj.constructor.prototype
3.Object.getPrototypeOf(obj)
上面三种方法之中,前两种都不是很可靠。_proto_属性只有浏览器才需要部署,其他环境可以不部署。而obj.constructor.prototype在手动改变原型对象时,可能会失效。
let P = function () {
};
let p = new P();
let C = function () {
};
C.prototype = p;
//C.prototype.constructor = C;
let c = new C();
console.log(c.constructor.prototype === p); // false
上面代码中,构造函数C的原型对象被改成了p,但是实例对象的c.constructor.prototype却没有指向p。所以,在改变原型对象时,一般要同时设置constructor属性。
let P = function () {
};
let p = new P();
let C = function () {
};
C.prototype = p;
C.prototype.constructor = C;
let c = new C();
console.log(c.constructor.prototype === p); // true
因此,推荐使用第三种Object.getPrototypeOf方法,获取原型对象。
27、in 运算符和 for...in 循环
in运算符返回一个布尔值,表示一个对象是否具有某个属性。它不区分该属性是对象自身的属性,还是继承的属性。
console.log('length' in Array); // true
console.log('toString' in Array); // true
in运算符常用于检查一个属性是否存在。
获得对象的所有可遍历属性(不管是自身的还是继承的),可以使用for...in循环。
let o1 = {age1: 18};
let o2 = Object.create(o1, {
age2: {
value: 27,
enumerable: true
}
});
for (let age in o2) {
console.info(age);//p2 p1
}
上面代码中,对象o2的p2属性是自身的,p1属性是继承的。这两个属性都会被for...in循环遍历。
28、对象的拷贝
拷贝对象,需要做到:
(1)与原对象具有同样的原型。
(2)与原对象具有同样的实例属性
利用 ES2017 才引入标准的Object.getOwnPropertyDescriptors方法
function copyObject(orig) {
return Object.create(
Object.getPrototypeOf(orig),
Object.getOwnPropertyDescriptors(orig)
);
}