深入了解Object.defineProperty

463 阅读5分钟

前言

以下内容是对MDN JS对象属性Object.defineProperty() 的笔记

JS对象属性

如何生成一个对象

  • Object.create(null) 产生一个真正的什么都没有的空对象

  • ES5 的 new Object() 等价于 ES6的 Object.create(Object.prototype) 截屏2021-12-04 下午8.25.50.png

  • var d = {} 字面量{}看起来像(一个对象),那么它就是字面量

对象

以下代码中 a: 3 会覆盖 a: 1

var obj = {
  a: 1,
  b: 2,
  a: 3
}
var a = 1
var b = 2
var object = {
  a: a,
  b: b
}
// 可以改写成
var object = {a, b}

动态属性名

var name = 'a'
var object = { [name]: 1 } // { a: 1 } 

对属性的读写进行自定义的操作

var o = {
  _age: 18,
  get age(){return o._age}, // 读的时候
  set age(value){ if(value<100){o._age = value}else{o._age = 100}} // 写的时候,自定义最大
}

var i = 0 
Object.defineProperty(window, 'a', {
  get(){
    i += 1
    return i
  }
})
a === 1 && a === 2 && a ===3  // true

实现一个浅拷贝

var obj1 = {a:1, b:2, c:3}
var obj2 = {}

for(let key in obj1){
  obj2[key] = obj1[key]
}
// 以上 for 简写成
var obj2 = Object.assign({}, obj1)
// 还可以简写成
var obj3 ={...obj1}

var aa = 1
var aa = 2
var obj4 = {...obj1, ...obj2, ...obj3, aa, bb, get x(){return 'hi'}}

变更原型

a.__proto__ 是浏览器给自己用的,不要在任何时候使用a.__proto__(它不受标准待见), ES6规定需要用Object.getPrototype(a)来拿它的原型

var a = {}
var b = {
  sayHi(){
    console.log('hi')
  }
}

a = Object.ceate(b) // 让对象和对象之间产生关联

a.__proto__ === b  // true, 现在 b 是对象 a 的原型
b.__proto__ === Object.prototype // true

对象字面量表示法与JSON 可以理解为 JS的对象与JSON的对象的区别

Object.defineProperty()

var newObject = {
  get x(){},
  set x(value){}
}

newObject.x  //  在读的时候走 get, 在写的时候走 set

假设有一个旧对象且属性已经写好的

var oldObject = {a: 1, b: 2}

我想给它加一个get set 怎么加?
加不了,定义的时间已经过了,无法重新定义
故只能用 Object.defineProperty() 来加

Object.defineProperty(oldObject, 'x', {
  get(){
  //这个里面 {} 是啥也没做的读的时候是 undefined
    get x(){},
    set x(value){} 
  }
})

undefined

它是不是关键字?
如果一个东西是关键字那么它是无法声明的
截屏2021-12-06 下午11.36.02.png 通过以上测试,可以得出 undefined 不是关键字
undefined 是一个变量
var undefined = 1的时候,那么 undefined打印出来的是几 ?

截屏2021-12-06 下午11.41.28.png 打印出来的是 undefined
不是说 undefined 是一个变量吗???
结论: undefined只读的变量 \

只读变量???

// 常量
const a = 1
a = 2 //直接报错

截屏2021-12-06 下午11.46.01.pngundefined 不是常量
那它就是变量 截屏2021-12-06 下午11.47.36.pngundefined 赋值,值却不变
故推倒出 它是一个 不可改变值的变量 \

如何让一个变量不可改变值?

undefined 其实就是 window.undefined 截屏2021-12-06 下午11.52.12.png 由以上判断出 undefined 其实就是 window 的一个只读属性
如何创建一个只读属性? \

var o = {
  get name(){return 'hone'}
}

截屏2021-12-06 下午11.55.50.png 也可写成以下示例 截屏2021-12-07 上午12.03.31.png

截屏2021-12-07 上午12.08.30.png
如何把不可写的变成可写的 截屏2021-12-07 上午12.18.30.png 如何让writable 不可改呢?
写成

Object.defineProperty(o, 'engin', {writable: false, configurable: false})

截屏2021-12-07 上午12.27.59.png

可枚举

有些属性是可以遍历的(可以被遍历的就是可枚举),有些属性是遍历不到的 截屏2021-12-07 上午12.39.14.png

enumerable

enumerable: false 为不可枚举对象 截屏2021-12-07 上午12.47.00.png 作用:为true时,就是 可遍历的 截屏2021-12-07 上午12.50.58.png 使用以下方法就可以设置不可遍历的key,这样就可以在数组上添加一些属性同时不影响以前的代码 截屏2021-12-07 上午12.57.05.png

Object.definePropertys

var obj = {};
Object.defineProperties(obj, {
  a: {
    value: true,
    writable: true
  },
  b: {
    value: 'Hello',
    writable: false
  }
  // a是可写的,b是不可写的 
})

Vue文档 \

当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setterObject.defineProperty 是 ES5 中一个无法 shim 的特性,这也就是 Vue 不支持 IE8 以及更低版本浏览器的原因。

对象可以用 Symbol 当 key

对象的 key 一般都是字符串, 在使用迭代的时候可能会使用 Symbol 作为 key

var s = Symbol()
var o = {
  [s]: 1
}

截屏2021-12-07 上午1.14.13.png 获取对象是否有 Symbol 的 key 需要用以下方法

截屏2021-12-07 上午1.18.11.png

不可使用__propo__

MDN 🔗

已废弃:  该特性已经从 Web 标准中删除,虽然一些浏览器目前仍然支持它,但也许会在未来的某个时间停止支持,请尽量不要使用该特性。

为了更好的支持,建议只使用 Object.getPrototypeOf()

Object.defineProperty实际使用

let obj = {
  姓: "高",
  名: "圆圆",
  get 姓名() {
    return this.姓 + this.名;
  },
  set 姓名(xxx){
    this.姓 = xxx[0]
    this.名 = xxx.slice(1)
  },
  // 除非直接写age: 18
};

obj.姓名 = '高媛媛'

以上代码当我需要在 obj 上添加 obj.get Age 的时候,没有办法去添加,除非直接写
这个时候Object.defineProperty就有用了
Object.defineProperty 的第一个参数你要定义的对象,第二参数你要定义啥

var _xxx = 0 
Object.defineProperty(obj, 'xxx', {  // 我们给 obj 添加了一个虚拟属性 xxx, 它有 get 和 set
  get(){  // get 后面不需要给名字了,这里就是 get xxx
    return 0  // 每次获取的时候返回 0
  }   
  set(x){  // 每次设置的时候你要给我一个新的 x
       // 然后把这个 x 赋给 ??? xxx不存在的,于是我们需要去一个东西去放 xxx 这个值
  }
})
var _xxx = 0 
Object.defineProperty(obj, 'xxx', {  
  get(){  
    return _xxx  // 如果要获取这个 xxx 就 return _xxx   // 改成 return this.xxx 会死循环
  }   
  set(value){  
    _xxx = value
  }
})

以上代码就是在定义完一个对象之后,你想在它身上在额外的添加新的 get set 的时候,就可以通过Object.defineProperty来做
注意⚠️:一般来说我们定义的 get set 属性,是不存在的,不要去使用我们定义的那个 xxx ,它是不存在的,于是就需要用一个全局/局部 变量来放这个值