关于defineProperty的几点疑惑

    function defineReactive(obj, key, val) {
      Object.defineProperty(obj, key, {
        get: function () {
          return val;
        },
        set: function (newVal) {
          val = newVal;
          console.log('值已被修改,现在为 ' + newVal);
        }
      })
    }
    var p1 = {
      name: 'z',
      age: 21,
      sex: 'male'
    };
    
    defineReactive(p1, 'age', p1.age);

执行以后
clipboard.png

几点疑惑
1、这个返回值是哪里来的?是set函数默认添加的吗

clipboard.png

2、JS中函数都是值传递,如果传的是一个基本值类型而不是一个引用类型的话,函数内部形参的改变是不会影响到函数外面的实参的。为什么执行了p1.age = 99 以后,再执行p1.age 能正确取到99呢,这个99存在哪里。。。想了好久都没能理解.... 而且定义get的时候,return val 应该就是 return 21了吧,
我的理解就是一下这两段代码是等价的,因为val参数是一个值类型,所以就是相当于return 21

    function defineReactive(obj, key, val) {
      Object.defineProperty(obj, key, {
        get: function () {
          return val;
        },
        set: function (newVal) {
          val = newVal;
          console.log('值已被修改,现在为 ' + newVal);
        }
      })
    }
    我的理解上面这段代码和下面这段代码等价
    function defineReactive(obj, key, val) {
      Object.defineProperty(obj, key, {
        get: function () {
          return 21;
        },
        set: function (newVal) {
          val = newVal;
          console.log('值已被修改,现在为 ' + newVal);
        }
      })
    }

但实际是为什么set以后,get会改变呢,这个形参val到底经历了什么?

 1   function defineReactive(obj, key, val) {
 2     Object.defineProperty(obj, key, {
 3       get: function () {
 4         return val; // ②
 5       },
 6       set: function (newVal) {
 7         val = newVal; // ①
 8         console.log('值已被修改,现在为 ' + newVal);
 9       }
      })
    }
    var p1 = {
      name: 'z',
      age: 21,
      sex: 'male'
    };
    
    defineReactive(p1, 'age', p1.age);

可能说的不是很明白,刚学这一块,希望得到满意的解答,谢谢~~

阅读 3.5k
4 个回答

理解有点偏差,主要利用的是闭包来保存值,如果不理解闭包,建议先去了解一下。
由于get和set属性都是一个函数,利用闭包存储到了val的值,可以理解为得到了一块内存指向,值就是val的值,所以经过set函数之后,val的值被改变了,所以你通过get通过val找到的值就是被改变之后的值。
defineProperty之后,p1的age属性就已经被重写,之所以跟21相关,也只是设置的时候传入p1.age初始值21,所以不应该执着于21

1.这个返回值是赋值语句的返回值。
这里要理解赋值语句本身是有返回值的

console.log( (a = 3) ) // 3

2.这里形成了闭包

function defineReactive(obj, key, val) {
  //在此作用域内存在一个val变量,由于闭包的存在它不会被销毁,会一直保存在内存里面
  Object.defineProperty(obj, key, {
    get: function () {
      return val; //返回的就是上面说的val变量,因此在set函数了改变了val,这里返回的也会变,都是同一个变量
    },
    set: function (newVal) {
      val = newVal; //改变了val
      console.log('值已被修改,现在为 ' + newVal);
    }
  })
}

一般来说,此局部变量 val 在 convert 执行结束之后应该销毁才是。但是,由于在 convert 中又定义了函数getter 和
setter(函数套函数),且使用了变量 val。因此,形成闭包,使得 val 始终保留在内存中。换句话说,每一次执行 convert
函数,就会多一个 val 变量存储在内存中,且这些 val 的值各不相同。因此,当你想设置某个 key 的值得时候,只需要通过 setter
把 newVal 赋值给对应的 val;当你想读取 key 的值得时候,通过 getter 返回对应的 val 变量就是 data.key
的实际值了。

如果还不明白,看下面的例子

// 这个函数 f1 像极我们的 convert 函数
function f1() {
    var n = 999;   // 类似于我们定义的 val
    nAdd = function () {   // 类似于我们的 setter,只不过这里固定地加1而已
        n += 1
    }
    function f2() {     // 类似于我们的 getter
        alert(n);
    }

    return f2;
}
var result = f1();    // 类似于调用 convert 方法,对 data 的 key 进行定义,data.key 初始值为 999
result(); // 999     // 类似于执行 data.key ,调用 getter,得到 n 的值,为 999
nAdd();  // 类似于执行 data.key = data.key + 1,调用 setter,使得 n++。此时,变量 n 的值为 1000
result(); // 1000    // 再次访问 data.key,返回当前 n 的值,也就是 1000

// 神奇的地方来了,现在我们需要定义 key2 了
var result2 = f1();
result2();   // 999    
// 请注意,这里输出的 n 是999,并非是上面已经加 1 的 1000 了;
// 因为此处的 n 和 上面的 n 本质上是同时存在的两个不同变量!!
  1. 这里其实控制台输出的是代码执行之后的返回值。
  2. 这里其实是内部val被保存下来,然后在get中被返回。

哈哈哈哈

这个 999 是前一句表达式的值啊。

试试 a = 999 控制台也会输出 999 的。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题