Object.defineProperty ,一个神奇的函数

vue.js是通过这 Object.defineProperty来实现双向数据绑定的,神奇的方法

为JavaScript对象新增或者修改属性,有两种不同方式:直接使用=赋值或者使用Object.defineProperty()定义。如下:

// 示例1
var obj = {};

// 直接使用
obj.a = 123

// 使用Object.defineProperty定义
Object.defineProperty(obj, "b",
    {
        value: 2
    });

console.log(obj) // 打印"{a: 1, b: 2}"

这么看两种方法都一样对吧,没啥区别,但是用Object.getOwnPropertyDescriptor()查看obj.a与obj.b的属性的描述描述符(property descriptor)时,会发现=与Object.defineProperty并不一样:

// 示例2
var obj = {};

obj.a = 1;

Object.defineProperty(obj, "b",
{
    value: 2
});

console.log(Object.getOwnPropertyDescriptor(obj, "a")); // 打印"{value: 1, writable: true, enumerable: true, configurable: true}"

console.log(Object.getOwnPropertyDescriptor(obj, "b")); // 打印"{value: 2, writable: false, enumerable: false, configurable: false}"

 

Object.defineProperty 很简单,对吧,接收三个值,且都是必填的
    第一个参数:目标对象

    第二个参数: 需要定义的属性或者方法的名称

    第三个参数:目标属性所拥有的特性(descriptor)描述符

前两个参数比较好理解,来看看第三个参数descriptor, 看下有哪些取值

  1. value: 属性的值
  2. writable:如果为false,属性的值就不能被重写,只能为只读了(有道面试题ES5去写const 可以用这个思路)

  3. configurable:总开关,一旦为false,就不能再设置他的(value,writable,configurable)

  4.  enumerable:是否能在for...in循环中遍历出来或在Object.keys中列举出来。

  5. get  (get 和set )不能与 value 或者是 writable 同时存在

  6. set  (get 和set )不能与 value 或者是 writable 同时存在

configurable

var a= {}
Object.defineProperty(a,"b",{
  value:123
})
console.log(a.b);//123

上面这个简单的例子,我们可以只设置了value,并没有别的什么设置,但是它会默认帮我们添加上writable,configurable,enumerable,默认值是false,也就是上面的代码等同于下面的

var a= {}
Object.defineProperty(a,"b",{
  value:123,
  writable:false,
  enumerable:false,
  configurable:false
})
console.log(a.b);//123

首先看下总开关configurable, 第一次什么也没设置默认就是false,然后进行第二次设置会发生什么呢,没错,会报错。

var a= {}
Object.defineProperty(a,"b",{
  configurable:false
})
Object.defineProperty(a,"b",{
  configurable:true
})
//error: Uncaught TypeError: Cannot redefine property: b

所以这里的configurable如果后面需要用到,在第一次的时候就要设置好,不设置会默认帮你加上false。一旦设置成false ,就不能设置他的(value,writable,configurable)了。

writable

设置成false,就变成只读了。

var a = {}; 

Object.defineProperty(a, "b", { 
    value : 123,
    writable : false });

console.log(a.b); // 打印 37

a.b = 25; // 没有错误抛出(在严格模式下会抛出,即使之前已经有相同的值)

console.log(a.b); // 打印 37, 赋值不起作用。

前面提到了一个面试题,在ES5 下实现 const,可以用到writable代码有点长,不想看可以跳过的

var __const = function __const(data, value) {
  window.data = value // 把要定义的data挂载到window下,并赋值value

  Object.defineProperty(window, data, { // 利用Object.defineProperty的能力劫持当前对象,并修改其属性描述符
    enumerable: false,
    configurable: false,
    get: function () {
      return value
    },
    set: function (data) {
      if (data !== value) { // 当要对当前属性进行赋值时,则抛出错误!
        throw new TypeError('Assignment to constant variable.')
      } else {
        return value
      }
    }
  })
}
__const('a', 10)
console.log(a)
delete a
console.log(a)
// enumerable 属性下面有详细讲解
for (let item in window) { // 因为const定义的属性在global下也是不存在的,所以用到了enumerable: false来模拟这一功能
  if (item === 'a') { // 因为不可枚举,所以不执行
    console.log(window[item])
  }
}
a = 20 // 报错

enumerable

属性特性 enumerable 定义了对象的属性是否可以在 for...in 循环和 Object.keys() 中被枚举。

var a= {}
Object.defineProperty(a,"b",{
  value:3445,
  enumerable:true
})
console.log(Object.keys(a));// 打印["b"]

将enumerable改为false,

var a= {}
Object.defineProperty(a,"b",{
  value:3445,
  enumerable:false // 就这里改了,注意看呀~
})
console.log(Object.keys(a)); // 打印[]

set 和 get

在 descriptor 中不能 同时设置访问器 (get 和 set) 和 wriable 或 value,否则会错,就是说想用(get 和 set),就不能用(wriable 或 value中的任何一个)

set 是赋值用的,get 是取值用的

var a= {}
Object.defineProperty(a,"b",{
  set:function(newValue){
    console.log("你也可以赋值, 新值是"+newValue)
    },
  get:function(){
    console.log("你取我的值")
    return 2 // 注意这里,我硬编码返回2
   }
})
a.b =1 // 打印 你要赋值给我,我的新值是1
console.log(a.b)    //打印 你取我的值
                    //打印 2    注意这里,和我的硬编码相同的

简单来说,, 这个 “b” 赋值 或者 取值的时候会分别触发 set 和 get 对应的函数

这就是实现 observe的关键啊。。

先写这些拜了个拜~

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值