JS对象之getter和setter

1,069 阅读4分钟

获取或者修改对象的属性值在我们写代码的时候是必不可少的操作,那么今天就来看看在获取和修改的时候内部发生了什么吧!

[[Get]]

属性访问在实现时候有一个微妙却非常重要的地方,请看下面的例子:

    const obj={
      a:2
    }
    console.log(obj.a);//2

obj.a是一次属性访问,但是这条语句并不仅仅是在obj中查找名字为a的属性,虽然看起来好像是这样。

在语言规范中,obj.a实际上是实现了[[Get]]操作(有点像函数调用),对象默认的内置[[Get]]操作首先会在对象中查找是否具有同名的属性,如果有就会返回这个属性的值,如果没有,按照[[Get]]的算法就会去原型链中查找,如果还没有则返回undefiend。

      const obj = {
        a: 2,
      };

      obj.__proto__ .b= 3
      console.log(obj.b);//3

      console.log(obj.c);//undefined

上面的代码,我们在obj的原型链的某一段加上了一个属性"b"并赋值为3,当我们使用属性访问的时候,在obj上没有找到b属性,就回去obj的原型链上查找b,对于属性c,obj对象和原项链上都没有c这个属性,所以返回undefined。

==留下问题:==对于本身存的就是undefined和因为找不到而返回的undefined我们怎么区分呢?,这个问题博主之后再分享。

      const obj = {
        a: 2,
        e:undefined
      };
      console.log(obj.e);//undefined
      console.log(obj.c);//undefined

Getter

顾名思义就是获取,当我们获取一个对象的属性的时候,就会调用一个对象的getter函数,但首先要给对象设置getter,这里提及一下,当getter和setter(之后讲解)都是隐藏函数,当给一个属性定义了getter和setter时,JavaScript会忽略value和writable属性,取而代之的是关心setter和getter(还有configurable和enumerable)特性。

   const obj = {
        get name() {
          console.log("调用get"); //调用get
          return "丁时一";
        },
      };
      console.log(obj.name); //丁时一

      Object.defineProperty(obj, "age", {
        get: function () {
          console.log("调用get"); //调用get
          return 21;
        },
      });

      console.log(obj.age); //21

当我们给对象属性定义了getter后,我们去访问这个属性就会触发getter函数,并且其返回的值就是我们获取的值。

现在我们尝试去修改属性的值

  const obj = {
        get name() {
          console.log("调用get"); //调用get
          return "丁时一";
        },
      };
      obj.name="大帅哥"
      console.log(obj.name); //丁时一

我们会发现修改并没有成功,因为我们只定义了getter,对属性的赋值操作会忽略,如果我们要修改属性的值,这时候就要另一个函数了setter

setter

      const obj = {
        get name() {
          console.log("调用get");
          return this._name;
        },
        set name(val) {
          console.log("调用set");
          this._name = val;
        },
      };
      //调用set
      obj.name = "丁时一";
      //调用get
      console.log(obj.name); //丁时一

当尝试去修改一个属性的值的时候,会触发set函数,并且传入一个参数val就是想要赋予的值,这时候我们就可以拿到最新的值,进行一系列的处理(当然也可以直接赋值)后再进行操作。

提及

在Vue2.x中,Object.defineProperty可是为Vue的响应式和双向绑定立了大功,在set中监听属性的改变,并调用notify()函数去通知使用到这个属性的结点的值更新为最新的值。下面是部分代码:

 Object.defineProperty(data, key, {
            enumerable: true,
            configurable: true,
            get() {
              if (Dep.target) {
                dep.addSub(Dep.target);
              }
              return val;
            },
            set(newValue) {
              if (val === newValue) {
                return;
              }
              // 数据发生变化 dep通知wather更新
              val = newValue;
              dep.notify();
            },
   });

存在性

在上面有提到一个问题对于本身存的就是undefined和因为找不到而返回的undefined我们怎么区分呢?,我们可以在不访问这儿属性的情况下判断这个属性是否存在于对象或者对象的原型链中。

方法一:使用in操作符:

      const obj = {
        a: undefined,
      };
      obj.__proto__.c=undefined
      console.log(obj.a);//undefined
      console.log(obj.b);//undefined

      console.log("a" in obj);//true
      console.log("b" in obj);//false
      console.log("c" in obj);//true

in操作符会检查属性是否存在于对象及其对象的原型链之中。

方法二:使用hasOwnProperty():

      const obj = {
        a: undefined,
      };
      obj.__proto__.c=5
      console.log(obj.hasOwnProperty("a"));//true
      console.log(obj.hasOwnProperty("b"));//false
      console.log(obj.hasOwnProperty("c"));//false

in操作符不同的一点是,hasOwnProperty()并不会检测原型链。

注意:in操作符检测的是容器内的某个属性名,对于数组来说,4 in [1,2,4]并不是你所期待的true,因为[1,2,4]这个数组中包含的属性名是下标[0,1,2],并没有4

    const array=["I","Love","You"]

    for(let i in array){
      console.log(i);//0,1,2
    }