Object.preventExtensions(对象)
阻止给一个对象添加新属性
const obj = {
name: 1
}
Object.preventExtensions(obj)
obj.age = 2
obj // { name: 1 }
Object.seal(对象)
创建一个封闭对象,这个对象只能修改,不能删除,添加和配置
const obj = {
a: 1
}
Object.seal(obj)
obj.b = 2
delete obj.a
obj // { a: 1 }
手动模式实现Object.seal()
/*
通过Object.preventExtensions来限制对象添加属性,通过configurable:false限制删除
*/
function seal(obj){
Object.preventExtensions(obj)
for(const key in obj){
Object.defineProperty(obj, key, {
writable: true,
configurable: false, // 不可配置
})
}
}
Object.freeze(对象)
冻结一个对象,不能删除,新增和修改,这个对应的属性如果是其他对象,那么不受影响
const obj = {
a: 2,
b: { c: 3 }
}
Object.freeze(obj)
obj.a = 4
obj.b.c = 5
obj // { a: 2, b: { c: 5 }}
模拟实现Object.freeze()
/*
内部通过Object.seal来限制对象的属性添加,删除和配置,
再通过修改每个属性的writable为false来限制修改属性
*/
function freeze(obj){
Object.seal(obj)
for(const key in obj) {
Object.defineProperty(obj, key, {
writable: false
})
}
}
const obj2 = { a:1 }
freeze(obj2)
obj2.a = 2
obj2.b =3
obj2 // { a:1 }
key in obj
检查指定的属性是否在这个对象身上包括它的原型链
const obj = {
a: 1
}
const obj2 = Object.create(obj)
'a' in obj2 // true
obj.hasOwnProperty(key)
检查指定属性是否在这个对象身份,不会检查它的原型链
const obj = {
a: 1
}
const obj2 = Object.create(obj)
obj2.hasOwnProperty('a') // false
for in
遍历数组或对象身上的可枚举的属性,包括它的原型链上的可枚举属性;for in一般用来遍历对象,数组使用for循环
const arrObj = { a: 'a'}
const arr = [1,2,3]
Object.setPrototypeOf(arr, arrObj)
for (const k in arr) {
if (arr.hasOwnProperty(k)) {
console.log(k) // 0 1 2 3
}
console.log(k) // 0 1 2 3 a
}
for of
只能遍历可迭代的对象身上的属性不管是否可枚举,但是不会遍历它原型上的属性,不可迭代的对象会报错,因为内部使用了迭代器进行循环,一般用来遍历数组可以直接获取到其中的值
const arrObj = [5,6]
const arr = [1,2,3]
Object.setPrototypeOf(arr, arrObj)
Object.defineProperty(arr, 3, {
enumerable: false, // 把下标3设置为不可枚举
value:333
})
console.log(arr) // 1 2 3 333
for (const val of arr) {
console.log(val) // 1 2 3 333
}
模拟实现一个for of
function forOf(arr){
const iterator = arr[Symbol.iterator]()
let result = iterator.next()
while(!result.done){
console.log(result.value)
result = iterator.next()
}
}
forOf([1,2,3]) // 1 2 3
Object.keys和Object.values
Object.key获取对象身上可枚举的属性名,不包括原型链;
Object.values获取对象身上可枚举的值,不包括原型链;
Object.getOwnPropertyNames()
获取对象身上自有的所有的属性名,包括可枚举和不可枚举,但是不包括原型链
const obj = {
a: 1
}
const obj2 = Object.create(obj, { b: {
enumerable: false
}})
const name = Object.getOwnPropertyNames(obj2)
name // ['b']
writable属性描述
如果在原型上添加的属性是不可修改的,那么在子对象上就无法通过.的形式创建这个属性,严格模式下会报错
const obj = {
a: 1
}
Object.defineProperty(obj, 'b', { writable: false })
const c = Object.create(obj)
c.b = 2 // 严格模式下会报错
c.a = 3
c // { a: 3 }
解决: 只能通过Object.defineProperty来修改这个属性
set描述符
如果在原型对象的属性上使用了set描述符,那么在子对象身上是无法通过.进行创建这个属性,如果通过.创建了这个属性,那么它就会修改原型上的这个属性的值
const obj = {
a: 1
}
let value = ''
Object.defineProperty(obj, 'b', {
set(val){ // 设置了set
value = val
},
get(){
return value
}
})
const c = Object.create(obj)
c.b = 66 // 给c对象添加属性b值为66
c // {}
c.b // 66 从原型上获取
Object.getPrototype(c) // {a:1,b:66}
解决: 只能通过Object.defineProperty来修改这个属性,并且会修改了原型上的这个属性
Object.getOwnPropertyDescriptor(对象,属性)
获取对象上指定属性的描述符
const obj = {
a: 1
}
Object.defineProperty(obj, 'b', { writable: false })
Object.getOwnPropertyDescriptor(obj, 'b')
/*
configurable: false
enumerable: false
value: undefined
writable: false
*/
Object.getOwnPropertyDescriptors(对象)
获取对象上的所有属性的描述符
const obj = {
a: 1
}
Object.defineProperty(obj, 'b', { writable: false })
Object.getOwnPropertyDescriptors(obj)
/*
结果
{
a:{
configurable: true
enumerable: true
value: 1
writable: true
},
b:{
configurable: false
enumerable: false
value: undefined
writable: false
}
}
*/
组合继承
// 实现一个父类
function Person(name){
this.name = name
}
// 实现一个子类
function Man(){
// 继承属性
Person.apply(this, [...arguments])
}
// 通过原型继承方法
// 方式1:直接继承原型,缺点:如果修改了子类的原型属性,父类也会进行修改
Man.prototype = Person.prototype
// 方式1 的正确继承,通过创建对象的形式
Man.prototype = Object.create(Person.prototype)
// 方式2 通过父类的构造函数继承 缺点:父类中属性也会被子类继承,比如name,如果子类没有这个属性,那么就会找到父类中的name,还有一些副作用(写日志,修改状态等)会影响到子类
Man.prototype = new Person()
// 方式2 正确的写法,通过中介纯函数
function C(){}
C.prototype = Person.prototype
Man.prototype = new C()
//方式3 Object.setPrototypeOf
Object.setPrototypeOf(Man, Person.prototype)
// 修改构造函数的指向
Man.prototype.constructor = Man
instanceof
右侧函数的原型(函数名.prototype)是否在左侧对象的原型链上,它只能判断对象和函数之间的关系,无法判断两个对象之间的关系;
通过中间函数来判断两个对象之间的关系
/*
方法1
a为左侧对象,b为右侧对象
*/
function isInstanceof(a,b){
function F(){}
F.prototype = b
// 不能使用setPrototypeof修改原型,因为setPrototypeof内部使用
// __proto__进行修改,__proto__和prototype不是一个东西,F.__proto__指向
//的是F的构造函数的原型也就是Function.prototype,
// 而F.prototype就是F.prototype
// Object.setPrototypeof(F,b)
return a instanceof F
}
const obj = {}
const c = Object.create(obj)
isInstanceof(c,obj) // true
// 方法2 通过isPrototypeOf
c.prototype.isPrototypeOf(obj) // true
// 方法3 通过getPrototypeof
Object.getPrototypeof(c) === obj // true
// 方法4 通过__proto__
c.__proto__ === obj // true
setPrototypeOf和prototype和__proto__的区别?
- prototype只有构造函数才有这个属性,表示构造函数的原型,而对象不具有这个属性;
- __proto__对象和构造函数都有这个属性,对象的__proto__是指向它的构造函数的prototype,而构造函数䣌__proto__指向它的构造函数(Function)的prototype;
- 因此构造函数身上的__proto__和prototype是两个不同的东西
- Object.setPrototypeOf(a,b)给对象设置原型,内部通过__proto__进行设置的,相当于a._proto_ = b;
function A(){}
const a = new A()
a.__proto__ === A.prototype // true
A.__proto__ === Function.prototype // true
Object.setPrototypeOf(A, {})
// Object.setPrototypeOf(A, {})是修改了构造函数A的原型,而不是修改了A.prototype
模拟_proto_
Object.defineProperty(Object.prototype, '__proto__', {
set(o){
Object.setPrototypeOf( this, o )
return o
},
get(){
return Object.getPrototypeOf( this )
}
})
Object.create(obj,{属性:{描述符}})
创建一个新的对象,并且指定它的原型,并且可以添加新的属性和描述符
const obj = {
a: 1,
}
const obj2 = Object.create(obj, {
b: {
enumerable: true,
}
})
obj2 // { b: undefined }
模拟实现一个create
function create(obj, params){
function F(){}
F.prototype = obj
const f = new F()
if (params) {
const keys = Object.keys(params)
for (const k of keys) {
Object.defineProperty(f, k, {
...keys[k]
})
}
}
return f
}
const obj = {
a: 1,
}
const obj2 = create(obj, {
b: {
enumerable: true,
}
})
obj2 // { b: undefined }