原型链
原理
- 每个function上都有一个prototype属性,指向一个对象,叫做
原型对象
- 原型对象上又有一个constructor属性能够找到构造他的function
- 用 new 关键字调用function时主要做四件事:
- 生成一个新的对象 {}
- 给这个对象添加一个属性 _ _ proto _ _ = function的prototype;
- function 的this绑定到这个对象上去执行
- 返回新生对象(所以新对象就有一个_ _ proto _ _属性指向了这个function的prototype),就能说他的原型链上能找到这个function的原型对象
<head>
<title>Document</title>
</head>
<body>
<script>
function A() {
this.name = '张三';
this.age = 18;
}
let a = new A();
console.log(a.__proto__ === A.prototype);
console.log(a.__proto__.constructor === A);
console.log(a.__proto__.constructor.name === 'A');
console.log(a instanceof A);
</script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
证明
- new出来的对象能够通过_ _ proto _ _ 属性找到function的 prototype 对象
- prototype对象里又有一个constructor函数
- constructor函数有一个name属性,能够拿到函数名
- constructor还有一个prototype属性,又指回它的原型对象(循环指向)
<body>
<script>
function A() {
this.name = '张三';
this.age = 18;
}
A.prototype.height = 180;
let a = new A();
console.log(a);
</script>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
- new出来的对象能够通过_ _ proto _ _ 属性找到function的 prototype 对象
- prototype对象里又有一个constructor函数
- constructor函数有一个name属性,能够拿到函数名
原型链顶层
- 每个对象的原型链顶层都是 obj._ _ proto _ _ = Object.prototype
- 而Object._ _ proto _ _ 指向的是 Function.prototype
- Function._ _ proto _ _ 指向的也是 Function.prototype
- 而Function.prototype是一个对象,根据原则1,它的_ _ proto _ _ 指向的也是Object.prototype,所以原型链顶端就会出现循环指向的问题。
<body>
<script>
function A() {
this.name = '张三';
this.age = 18;
}
let a = new A();
console.log(a);
</script>
</body>
1
2
3
4
5
6
7
8
9
10
11
- 任意一个function都有一个原型对象
- A的原型对象是A.prototype
- 任何一个对象,都有一个 _ _ proto _ _ 属性,指向构造函数的原型对象
- a._ _ proto _ _ 就指向 A.prototype
a.__proto__ === A.prototype;
1
- 任何对象,原型链都能追溯到 Object.prototype (所以所有对象都有一些默认的原型方法)
A.prototype.__proto__ === Object.prototype;
1
- Object作为一个基础对象的构造函数,它也有一个_ _ proto _ _ 属性,指向的是Function.prototype
Object.__proto__ === Function.prototype;
1
- 在顶层,Function._ _ proto _ _指向他自己的prototype (唯一一个比较特殊的地方)
Function.__proto__ === Function.prototype;
1
- 最后,由于Function.prototype也是对象,所以根据第③条,他的向上追溯也能找到Object.prototype
- 由此,Object.prototype 和 Function.prototype 形成了 原型链顶端的闭环,因此有下列结论:
Function.__proto__ === Function.prototype;
Function instanceof Function;
Function.__proto__.__proto__ === Object.prototype;
Function instanceof Object;
Object.__proto__.__proto__ === Object.prototype;
Object instanceof Object;
Object.__proto__ === Function.prototype;
Object instanceof Function;
1
2
3
4
5
6
7
8
9
10
11
create
- 作用:创造一个带有指定原型链的对象
- 调用:Object.create(obj[, descriptors])
- 入参:Object[, definePropertiesObj]
- 返回:Object[, Object]
class A {}
A.prototype.age = 18;
const a1 = new A();
a1.name = 'hdy';
const a2 = Object.create(a1);
console.log(a2.name);
console.log(a2.age);
console.log(a2 instanceof A);
console.log(a2.__proto__ === a1);
1
2
3
4
5
6
7
8
9
10
11
12
- 普通继承是通过class B extends A {}来实现的
- 但是单个对象要继承另一个对象的属性就可以用Object.created()来实现,通过原型链的机制
class A {}
class B extends A {}
const b = new B();
console.log(b instanceof A);
function C() {}
function D() {
}
D.prototype.__proto__ = C.prototype;
const d = new D();
console.log(d instanceof C);
const f = Object.create(d);
console.log(f instanceof C);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class A {
constructor() {
this.name = 'hdy';
}
}
function B() {}
B.prototype = Object.create(A.prototype);
const b = new B();
console.log(b instanceof A);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
- 第二个参数类似Object.defineProperty的第二个参数
const obj1 = { name: 'hdy' }
const obj2 = Object.create(obj1, {
age: {
configurable: true,
enumerable: true,
writable: true,
value: 18
}
})
console.log(obj2);
console.log(obj2.name);
1
2
3
4
5
6
7
8
9
10
11
12
getPropertypeOf
- 作用:获取_ _ proto _ _
- 调用:Object.getPropertypeOf(obj)
- 入参:Object
- 返回:Object
class A {}
class B extends A {}
console.log(Object.getPrototypeOf(B.prototype) === A.prototype);
1
2
3
console.log(Object.getPrototypeOf(Object)=== Function.prototype);
1
setPrototypeOf
性能消耗大
- 官方说性能消耗较大,建议使用Object.create()替代写法
setPrototypeOf
- 作用:设置对象原型链
- 调用:Object.setPrototypeOf(obj, _ _ proto _ _)
- 入参:Object, Object
- 返回:Object(参数1)
const obj1 = { name: 'hdy' };
const obj2 = Object.setPrototypeOf({}, obj1);
console.log(obj2);
console.log(obj2.name);
1
2
3
4
5
const obj1 = { name: 'hdy' };
const obj2 = Object.create(obj1);
console.log(obj2);
console.log(obj2.name);
1
2
3
4
5
isPrototypeOf
- 作用:查看是否在指定对象的原型链上
- 调用:obj1.isPrototypeOf(obj2)
- 入参:Object
- 返回:Boolean
const obj1 = {}
const obj2 = Object.setPrototypeOf({}, obj1);
console.log(obj1.isPrototypeOf(obj2));
1
2
3
4
const obj1 = {};
const obj2 = Object.create(obj1);
const obj3 = Object.create(obj2);
console.log(obj2.isPrototypeOf(obj3));
console.log(obj1.isPrototypeOf(obj3));
1
2
3
4
5
6
迭代器
for in
for in
- 作用:迭代本身包括原型链的
可枚举属性
的键
- 调用:for (let key in obj) {}
const father = {
name: 'hdy'
}
const child = Object.create(father);
for (let key in child) {
console.log(key);
}
1
2
3
4
5
6
7
8
9
10
const father = {
name: 'hdy'
}
const child = Object.create(father);
if( 'name' in child) {
console.log(child['name']);
}
1
2
3
4
5
6
7
8
entries
- 作用:返回对象实例
可枚举属性
的键值对(不包括原型链) - 调用:Object.entries(obj);
- 入参:Object
- 返回:Array(二维数组)
const obj = {
name: 'hdy',
age: 18,
}
Object.defineProperty(obj, 'books', {
value: ['红宝书']
})
console.log([...Object.entries(obj)]);
1
2
3
4
5
6
7
8
const obj = {
name: 'hdy',
age: 18,
}
const map = new Map(Object.entries(obj));
console.log(map);
1
2
3
4
5
6
keys
- 作用:拿到对象自身的
可枚举属性
,不包括原型链属性 - 调用:Object.keys(obj)
- 入参:Object
- 返回:Array
const obj = {
name: 'hdy'
}
Object.defineProperty(obj, 'age', {
enumerable: false,
value: 18
})
console.log(Object.keys(obj));
1
2
3
4
5
6
7
8
9
values
- 作用:拿到自身所有
可枚举属性
的值,不包括原型链属性 - 调用:Object.keys(obj)
- 入参:Object
- 返回:Array
const obj = {
name: 'hdy'
}
Object.defineProperty(obj, 'age', {
enumerable: false,
value: 18
})
console.log(Object.values(obj));
1
2
3
4
5
6
7
8
9
getOwnPropertyNames
- 作用:拿到自身所有属性名(包括不可枚举属性)
- 调用:Object.getOwnPropertyNames(obj)
- 入参:Object
- 返回:Array
与Object.keys()对比
- 相同:都不会去原型链拿属性
- 不同:getOwnPropertyNames() 可以拿到自身不可枚举属性
const obj = {
name: 'hdy'
}
Object.defineProperty(obj, 'age', {
enumerable: false,
age: 18
})
console.log(Object.getOwnPropertyNames(obj));
1
2
3
4
5
6
7
8
9
- Object.keys()
- Object.entries()
- Object.values()
const obj = Object.defineProperties({}, {
name: {
enumerable: true,
value: 'hdy'
},
age: {
value: 18
}
})
console.log(obj);
console.log(Object.keys(obj));
1
2
3
4
5
6
7
8
9
10
11
12
const obj = Object.defineProperties({}, {
name: {
enumerable: true,
value: 'hdy'
},
age: {
value: 18
}
})
const unEnumerable = Object.getOwnPropertyNames(obj).filter(function(item) {
return !this.includes(item);
}, Object.keys(obj))
console.log(unEnumerable);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
getOwnPropertySymbols
- 作用:获取本对象的symbol
- 调用:Object.getOwnPropertySymbols(obj)
- 入参:Object
- 返回:Symbol []
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);
console.log(objectSymbols)
console.log(objectSymbols[0])
1
2
3
4
5
6
7
8
9
10
11
12
hasOwnProperty
- 作用:查看属性是不是属于实例本身
- 调用:obj.hasOwnProperty(key)
- 入参:String
- 返回:Boolean
const obj = { name: 'hdy' };
const obj2 = Object.create(obj);
console.log(obj2.name);
console.log(obj2.hasOwnProperty('name'));
1
2
3
4
5
const obj1 = { name: 'hdy' };
const obj2 = Object.create(obj1, { age: { enumerable: true } });
1
2
const ownProperty = [];
for (let key in obj2) {
if (obj2.hasOwnProperty(key)) {
ownProperty.push(key);
}
}
console.log(ownProperty);
1
2
3
4
5
6
7
描述符
defineProperty
- 作用:以描述符的形式给对象定义属性,可以支持get/set劫持
- 调用:Object.defineProperty(obj, key, descriptor)
- 入参:Object, String, Object
- 返回:Object(第一个入参的引用)
const obj = {
name: 'hdy'
}
Object.defineProperty(obj, '_age', {
configurable: true,
writable: true,
emuerable: false,
value: 18
})
Object.defineProperty(obj, 'age', {
configurable: true,
emumerable: true,
get() {
return this._age + '岁';
},
set(value) {
this._age = value;
}
})
console.log(obj.age);
obj.age = 20;
console.log(obj.age);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
- configurable为true时,所有属性都只能配置这一遍,包括configurable,且属性不可删(默认是false)
- emuerable配置是true打印对象时才会被枚举出来,(直接赋值出来的属性默认是true,defineProperty的属性默认是false)
- value: 值
- writable决定是否可改变value
- get 属性的getter器
- set 属性的setter器
- configurable为true时,所有属性都只能配置这一遍,包括configurable(除了value和writable),且属性不可删(默认是false)
不可配置不可删
const obj = {};
Object.defineProperty(obj, 'age', {
configurable: false,
value: 18
})
console.log(obj.age);
delete obj.age;
console.log(obj.age);
1
2
3
4
5
6
7
8
9
const obj = {};
Object.defineProperty(obj, 'age', {
configurable: true,
value: 18
})
console.log(obj.age);
delete obj.age;
console.log(obj.age);
1
2
3
4
5
6
7
8
9
可配置下可再改属性
var obj = {};
Object.defineProperty(obj, 'age', {
configurable: true,
writable: false
value: 18,
});
obj.age = 19;
console.log(obj.age);
Object.defineProperty(obj, 'age', {
writable: true
});
obj.age = 19;
console.log(obj.age);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- enumerable配置是true打印对象时才会被枚举出来,(直接赋值出来的属性默认是true,defineProperty的属性默认是false)
const obj = {};
Object.defineProperty(obj, 'age', {
value: 18
})
console.log(obj);
console.log(obj.age);
obj.height = 180;
console.log(obj.height);
console.log(obj.propertyIsEnumerable('age'));
console.log(obj.propertyIsEnumerable('height'));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- 包括for...in 和Object.keys()
const father = { name: 'hdy' };
const child = Object.create(father, {
age: {
enumerable: false,
value: 19
}
});
console.log(child);
console.log(child.age);
for (let key in child) {
console.log(key);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
- value: 值
- writable决定是否可改变value,默认false
const obj = {};
Object.defineProperty(obj, 'age', {
writable: false,
value: 10
})
console.log(obj.age);
obj.age = 11;
console.log(obj.age);
1
2
3
4
5
6
7
8
9
引用类型只判断引用是否改变
const obj = {};
Object.defineProperty(obj, 'books', {
writable: false,
value: ['红宝书']
})
console.log(obj.books);
obj.books.push('蝴蝶书');
console.log(obj.books);
obj.books = ['你不知道的js'];
console.log(obj.books);
1
2
3
4
5
6
7
8
9
10
11
12
13
const obj = {
_age: 18,
get age() { return this._age + '岁'; },
set age(value) { this._age = value; }
}
console.log(obj.age);
obj.age = 20;
console.log(obj.age);
1
2
3
4
5
6
7
8
9
const obj = { _age: 18 }
Object.defineProperty(obj, 'age', {
get() { return this._age; },
set(value) { this._age = value; }
})
console.log(obj.age);
obj.age = 20;
console.log(obj.age);
1
2
3
4
5
6
7
8
9
- 同时拥有((value || writable) && (get || set))会产生异常
Object.defineProperty({}, 'age', {
value: 18,
get() {},
set(value) {}
})
1
2
3
4
5
6
7
const arr = [1, 2, 3];
arr.forEach((item, index) => {
Object.defineProperties(arr,{
[`_${index}`]: {
value: item,
writable: true
},
[index]: {
get() {
console.log(`劫持get操作:下标${index}的值是${this['_' + index]}`);
return this[`_${index}`];
},
set(value) {
console.log(`劫持set操作:下标${index}的值变换为${value}`);
this[`_${index}`] = value;
}
}
})
})
console.log(arr[0]);
console.log(arr[1] = 3);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
writable与继承
- 一个对象的writable设置成false时,其继承者只能通过defineProperty定义同名属性
- getter/setter设置时就默认writable是false,且无法更改,所以实例也无法生成自己的同名属性
- 类的constructor函数可以主动为实例对象添加实例属性,可以覆盖同名父类属性
- 一个对象的writable设置成false时,其继承者普通赋值方式无效
class A {}
Object.defineProperty(A.prototype, 'age', {
value: 1
})
class B extends A {}
B.prototype.age = 2;
const b1 = new B();
console.log(b1.age);
b1.age = 3;
console.log(b1.age);
1
2
3
4
5
6
7
8
9
10
11
- 一个对象的writable设置成false时,后续的原型链上也用defineProperty设置属性才有效
class A {}
Object.defineProperty(A.prototype, 'age', {
writable: false,
value: 1
})
class B extends A {}
Object.defineProperty(B.prototype, 'age', {
writable: true
value: 2,
})
const b1 = new B();
console.log(b1.age);
b1.age = 3;
console.log(b1.age);
console.log(b1.hasOwnProperty('age'));
const b2 = new B();
console.log(b2.age);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- getter/setter设置时就默认writable是false,且无法更改,
- 所以后续原型链也无法通过赋值生成自己的同名属性,一直都是调用的原型链上的getter/setter方法
- 重写方式也是defineProperty
const obj1 = {
_age: 1,
get age() {
return obj1._age;
},
set age(value) {
obj1._age = value;
}
};
const obj2 = Object.create(obj1);
const obj3 = Object.create(obj1);
console.log(obj2.age);
obj2.age = 18;
console.log(obj1._age);
console.log(obj3.age);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- 类的构造函数中为类的实例对象添加实例属性,可以覆盖同名父类属性
class A {}
Object.defineProperty(A.prototype, 'age', { value: 1 })
class B extends A { age = 2; }
const b = new B();
console.log(b.age);
console.log(b.hasOwnProperty('age'));
1
2
3
4
5
6
7
defineProperties
- 作用:同时可定义多个属性,作用与defineProperty一样
- 调用:Object.defineProperties(obj, {name: descriptor[, name: descriptor...] })
- 入参:Object,Object
- 返回:Object(第一个参数的应用)
const obj = {};
Object.defineProperties(obj, {
name: {
enumerable: true,
value: 'hdy'
},
_age: {
value: 1
},
age: {
get() { return this._age + '岁'; },
set(value) {this._age = value}
}
})
console.log(obj);
console.log(obj.age);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
getOwnPropertyDescriptor
- 作用:查看对象某个属性的描述符对象
- 调用:Object.getOwnPropertyDescriptor(obj, key)
- 入参:Object, String
- 返回:Object(描述对象)
const desc = {
configurable: false,
enumerable: true,
writable: false,
value: 'hdy'
}
const obj = Object.defineProperty({}, 'name', desc);
const d = Object.getOwnPropertyDescriptor(obj, 'name');
console.log(Object.entries(d).every(([key, value]) => desc[key] === value));
1
2
3
4
5
6
7
8
9
10
11
12
getOwoPropertyDescriptors
getOwnPropertyDescriptors
- 作用:获取所有对象的属性描述符
- 调用:Object.getOwnPropertyDescriptors(obj)
- 入参:Object
- 返回:Object
const desc = {
name: {
writable: true,
value: 'hdy'
},
age: {
writable: false,
value: 18
}
}
const obj = Object.defineProperties({}, desc);
const d = Object.getOwnPropertyDescriptors(obj);
console.log(Object.keys(d).every(item => desc[item]));
1
2
3
4
5
6
7
8
9
10
11
12
13
const obj = {
name: 'hdy',
books: [
'红宝书',
'蝴蝶书'
]
}
const obj2 = Object.defineProperties({}, Object.getOwnPropertyDescriptors(obj));
obj.name = 'coderHdy';
console.log(obj2.name);
obj.books[0] = '深入理解ES6';
console.log(obj2.books[0]);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- 期望:将对象所有属性深拷贝出一个新的对象,且各属性描述符不变
const obj1 = Object.defineProperties({}, {
name: {
writable: false,
value: 'hdy'
},
books: {
values:[
'蝴蝶书',
'你不知道的JS'
]
}
})
const obj2 = deepCloneWithDescriptor(obj1);
obj2.name = 'coderHDY';
console.log(obj2.name);
obj1.books[0] = '深入理解ES6';
console.log(obj2.books[0]);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function deepCloneWithDescriptor(obj) {
const oldDesc = Object.getOwnPropertyDescriptors(obj);
const deepEntries = Object.entries(oldDesc).map(([key, value]) =>
typeof value.value === 'object' && value != null ?
[key, JSON.parse(JSON.stringify(value))] :[key, value]
);
const newDesc = Object.fromEntries(deepEntries);
return Object.defineProperties({}, newDesc);
}
1
2
3
4
5
6
7
8
9
10
11
propertyIsEnumerable
- 作用:查看对象的属性是否可枚举
- 调用:obj.propertyIsEnumerable(key)
- 入参:String
- 返回:Boolean
const obj = Object.defineProperties({}, {
name: {
enumerable: true,
writable: false,
value: 'hdy'
},
age: {
enumerable: false,
value: 18
}
})
console.log(obj.propertyIsEnumerable('name'));
console.log(obj.propertyIsEnumerable('age'));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
- Object.keys() 可以查看所有自身可枚举属性
- Object.getOwnPropertyNames() 可以查看自身所有属性,包括不可枚举
- 过滤取反即可得出所有不可枚举属性
const obj1 = { book: '蝴蝶书' };
const obj2 = Object.create(obj1);
Object.defineProperties(obj2, {
name: {
enumerable: true,
writable: false,
value: 'hdy'
},
age: {
enumerable: false,
value: 18
}
})
const unEnumerable = getUnEnumerable(obj2);
console.log(unEnumerable);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function getUnEnumerable(obj) {
return Object.getOwnPropertyNames(obj).filter(function (item) {
return !this.includes(item);
}, Object.keys(obj))
}
1
2
3
4
5
增加限制
preventExtensions/seal/freeze对比
| 限制_ _ proto _ _不可改(不能修改原型链的指向)、不可以增加属性 | configurable:false(不可删除属性、其他配置不可改) | writable:false |
preventExtensions | * | | |
seal | * | * | |
freeze | * | * | * |
freeze
- 作用:冻结本对象的所有属性,禁止增删改属性,只能查看,且configurable也变成false
- 使用:Object.freeze(obj)
- 入参:Object
- 返回:Object(入参引用)
const obj1 = { name: 'hdy' };
const obj2 = Object.create(obj1);
const obj3 = Object.create(obj2);
Object.freeze(obj2);
obj1.age = 17;
obj2.age = 18;
console.log(obj3.age);
obj2.height = 180;
console.log(obj2.height);
1
2
3
4
5
6
7
8
9
10
11
12
const arr = [1, 2];
Object.freeze(arr);
arr[0] = 3;
console.log(arr);
arr.push(3);
1
2
3
4
5
6
7
- 属性值是数组、对象类的,存储的是一个索引,那么它的内部值是可以改变的,只要不更改索引
const obj1 = {
books: []
}
Object.freeze(obj1);
obj1.books.push(1);
console.log(obj1);
1
2
3
4
5
6
7
const obj = {
name: {
firstName: 'h',
lastName: 'dy'
}
}
Object.freeze(obj);
obj.name.firstName = 'H';
console.log(`${obj.name.firstName} ${obj.name.lastName}`);
1
2
3
4
5
6
7
8
9
10
const obj = {
books: [],
names: {},
age: 1,
friend: {
jack: {
names: 'Jack Chen'
}
}
}
Object.deepFreeze(obj);
obj.names.firstName = 'H';
console.log(obj.names.firstName);
obj.friend.jack.names = 'Jack Sun';
console.log(obj.friend.jack.names);
console.log(obj);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Object.deepFreeze = function (obj) {
Object.freeze(obj);
for (let value of Object.values(obj)) {
if (typeof value === 'object' && obj != null) {
Object.deepFreeze(value);
}
}
}
1
2
3
4
5
6
7
8
isFrozen
- 作用:判断一个对象是否被冻结
- 调用:Object.isFrozen(obj)
- 入参:Object
- 返回:Boolean
判断条件
- 所有属性不可增删改
- 所有属性 configurable: false
- 原型链指向不能改(_ _ proto _ _)
const obj = {};
console.log(Object.isFrozen(obj));
Object.freeze(obj);
console.log(Object.isFrozen(obj));
1
2
3
4
- 空的、不可扩展的对象,也就是所有属性 configurable:false
const obj = Object.preventExtensions({});
console.log(Object.isFrozen(obj));
1
2
seal
- 单词:seal 密封
- 作用:将一个对象密封起来,不可新增属性,当前所有属性的configurable变成false
- 调用:Object.seal(obj)
- 入参:Object
- 返回:Object(入参引用)
对比freeze
- 相同:都是不可扩展、属性都configurable:false
- 不同:freeze冻住的对象属性writable也是false,seal密封的没有限制
- 自身密封前已有属性的configurable变为false(也就不能删除)
- 自身不能新增属性
const obj = { name: 'hdy' };
Object.defineProperty(obj, 'age', {
configurable: true,
enumerable: true,
writable: false,
value: 18
})
Object.seal(obj);
obj.name = 'coderHdy';
obj.age = 20;
obj.book = '红宝书';
delete obj.name;
console.log(obj);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
isSealed
- 作用:判断一个对象有没有被密封
- 调用:Object.isSealed(obj)
- 入参:Object
- 返回:Boolean
const obj = {};
console.log(Object.isSealed(obj));
Object.seal(obj);
console.log(Object.isSealed(obj));
1
2
3
4
preventExtensions
- 作用:使对象不可扩展,此操作不可逆
- 调用:Object.preventExtensions(obj)
- 入参:Object
- 返回:Object(入参)
- tip:不会影响到原型链,但本对象_ _ proto _ _属性也不可变
const obj = { name: 'hdy' };
const obj2 = Object.create(obj);
obj2.age = 18;
Object.preventExtensions(obj2);
obj2.book = '蝴蝶书';
console.log(obj2);
obj2.age = 19;
console.log(obj2.age);
const obj3 = { name: '张三' };
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
isExtensible
- 作用:对象是否可扩展
- 调用:Object.isExtensible(obj)
- 入参:Object
- 返回:Boolean
- 限制可扩展性的有:freeze/seal/preventExtensions
const obj = { name: 'hdy' };
console.log(Object.isExtensible(obj));
1
2
const obj = Object.preventExtensions({ name: 'hdy' });
console.log(Object.isExtensible(obj));
1
2
const obj = Object.freeze({ name: 'hdy' });
console.log(Object.isExtensible(obj));
1
2
const obj = Object.seal({ name: 'hdy' });
console.log(Object.isExtensible(obj));
1
2
其他属性
assign
- 作用:将多个对象的
可枚举属性
集成到第一个对象上,返回第一个对象的引用 - 使用:Object.assign(obj1, obj2[, obj3...])
- 入参:Object, Object[, Object]
- 返回:Object
- tip:浅拷贝
- tip:相同属性以后来的为准
- tip:不支持es6就没有的Symbols
const obj1 = {
name: '张三',
book: '红宝书'
}
const obj2 = {
name: '李四',
age: 19
}
const ans = Object.assign(obj1, obj2);
console.log(ans === obj1);
console.log(obj1);
console.log(obj2);
1
2
3
4
5
6
7
8
9
10
11
12
13
const obj1 = {
name: 'hdy'
}
const obj2 = {
books: ['红宝书']
}
Object.assign(obj1, obj2);
obj2.books[0] = '你不知道的JS';
console.log(obj1);
1
2
3
4
5
6
7
8
9
10
const obj1 = {
name: 'hdy'
}
const obj2 = {
name: '李四',
books: ['红宝书']
}
Object.myAssign(obj1, obj2);
obj2.books[0] = '你不知道的JS';
console.log(obj1);
1
2
3
4
5
6
7
8
9
10
11
Object.prototype.myAssign = (obj1, ...objs) => {
objs.forEach(item =>
Object.entries(item).forEach(([key, value]) =>
obj1[key] = value));
return obj1;
}
1
2
3
4
5
6
7
const obj1 = {
name: 'hdy'
}
const obj2 = {
name: '李四',
books: ['红宝书']
}
Object.myDeepAssign(obj1, obj2);
obj2.books[0] = '你不知道的JS';
console.log(obj1);
1
2
3
4
5
6
7
8
9
10
11
Object.prototype.myDeepAssign = (obj1, ...objs) => {
let newObj = Object.assign({}, ...objs);
newObj = JSON.parse(JSON.stringify(newObj));
for (let [key, value] of Object.entries(newObj)) {
obj1[key] = value;
}
return obj1;
}
1
2
3
4
5
6
7
8
9
10
11
fromEntries
- 作用:将键值对转化成对象,类似于entries反向操作
- 调用:Object.fromEntries(entries)
- 入参:Iterator
- 返回:Object
const map = new Map([['name', 'hdy'],['age', 18]]);
const obj = Object.fromEntries(map);
console.log(obj);
1
2
3
const arr = [['name', 'hdy'], ['age', 18]];
const obj = Object.fromEntries(arr);
console.log(obj);
1
2
3
const obj = {
name: 'hdy',
age: 18,
books: '蝴蝶书'
}
const reverseObj = Object.fromEntries(
Object.entries(obj)
.map(([ key, value ]) => [ value, key ])
)
console.log(reverseObj);
1
2
3
4
5
6
7
8
9
10
11
12
is
- 作用:比较两个参数是否相等
- 调用:Object.is(arg1, arg2)
- 入参:any, any
- 返回:Boolean
const obj1 = {};
const obj2 = {};
console.log(Object.is(obj1, obj2));
console.log(Object.is(+0, -0));
console.log(Object.is(NaN, NaN));
1
2
3
4
5
toString
- 作用:检测对象类型
- 调用:obj.toString()
- 返回:[ object type ]
- tip:只是最基础的toString,继承类也可以自定义自己的toString方法
const obj = {}
console.log(obj.toString());
const func = function () {};
console.log(func.toString());
const arr = [1, 2, 3];
console.log(arr.toString());
1
2
3
4
5
6
7
8
9
- 由于返回值第一个是固定的object,但是第二个是构造器类型,就可以利用call/apply配合做类型检测
const arr = [1, 2, 3];
console.log(Object.prototype.toString.call(arr));
1
2
class A {}
const a = new A();
console.log(Object.prototype.toString.call(a));
1
2
3
const a = '';
console.log(Object.prototype.toString.call(a));
1
2
length
constructor
- 原型对象上的属性,指向构造函数
- 每个对象都能够通过原型链找到构造函数的原型对象,然后原型对象上就有对应的constructor属性,所以每个对象都能拿到自己的构造函数
class A {}
const a = new A();
console.log(a.constructor);
1
2
3
4
function B() {}
const b = new B();
console.log(b.constructor);
1
2
3
4
function Parent() {}
function Child() {}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
1
2
3
4
5
6
7
8