vue for in 遍历 为啥总报错_Vue响应式原理之Object.defineProperty()

a4676b3665d58a27da6f3e91de812740.png

已经用Vue好几年了,也知道Vue 2.x的底层响应式用的是Object.defineProperty()这个方法来实现的,可一直没有去深入了解过这个方法,今天就让我们一起去探个究竟。

一、对象属性的增删改查

在开始之前,我们先简单了解下如何对对象进行增、删、改、查等操作。

为了更加直观,我们直接上个简单例子。

首先创建一个空对象data

let data = {};

然后我们可以对其进行增删改查操作。

1. 新增属性

data.text = '想学习更多前端知识,请关注公众号:前端微站';

2. 删除属性

delete data.text;

3. 修改属性

data.text = '想学习更多前端知识,请关注公众号:前端微站';  // 新增属性data.text = '关注公众号前端微站,学习更多前端知识';  // 修改属性

4. 查询属性

查询有两种,一种是通过属性名(key)查询属性值(value), 另一种则正好相反,是通过属性值(value)来查询属性名(key),不过这两种查询都可以通过for...in来遍历查询。

// 先给对象再新增一个属性data.name = '前端王睿';// 通过 key 查询 valuefor(let key in data){  if(key === 'name'){    console.log(data[key]);  // 前端王睿  }}// 通过 value 查询 keyfor(let key in data){  if(data[key] === '前端王睿'){    console.log(key);  // name  }}

二、对象的属性描述符

上面所讲到的对象操作应该是司空见惯、众所周知的了,可是,你说好不容易找了个对象,说让你改就改,让你删就删,多没面子!有没有什么办法,我创建好了对象之后,就不让再随意改动呢,至少要我自己能够配置呀!

当然有!这时Object.defineProperty()就派上用场了!我们可以通过Object.defineProperty()对对象属性进行描述,也就是告诉我们哪个属性是不能改的,哪个属性是不能删的,等等。这里就要讲到对象的属性描述符,而属性描述符又分为 数据描述符 和 存取描述符。数据描述符是一个具有值的属性,该值可以是可写的,也可以是不可写的。存取描述符是由 getter 函数和 setter 函数所描述的属性。

1. 数据描述符

我们同样按照增、删、改、查的顺序对其进行一一列举,可以看出通过Object.defineProperty()方式新增的对象属性,默认都是不可被删除、修改和枚举的。

① value 

表示该对象属性的值,默认值为undefined。例如:

let data = {};Object.defineProperty(data,'text',{  value: '想学习更多前端知识,请关注公众号:前端微站'});console.log(data);  //  {text: '想学习更多前端知识,请关注公众号:前端微站'}

这看起来跟直接通过data.text = '想学习更多前端知识,请关注公众号:前端微站'新增对象属性的效果一样,具体区别请接着往下看。

② configurable 

默认值为false,表示该对象属性不能被删除,只有为true时才可删除。例如:

let data = {};Object.defineProperty(data,'text',{  value: '想学习更多前端知识,请关注公众号:前端微站'});delete data.text;  // 删除失败console.log(data.text);   // 想学习更多前端知识,请关注公众号:前端微站

删除没效果嘛!这时只需设置configurable值为true即可继续愉快地删除了。

let data = {};Object.defineProperty(data,'text',{  value: '想学习更多前端知识,请关注公众号:前端微站',  configurable: true});delete data.text;  // 删除成功console.log(data.text);   // undefined

③ writable 

默认值为false,表示该对象属性不能被修改,只有为true时才可修改。例如:

let data = {};Object.defineProperty(data,'text',{  value: '想学习更多前端知识,请关注公众号:前端微站'});data.text = '关注公众号前端微站,学习更多前端知识';  // 修改失败console.log(data.text);   // 想学习更多前端知识,请关注公众号:前端微站

修改失败了!这时给它设置writable值为true即可继续愉快地修改了。

let data = {};Object.defineProperty(data,'text',{  value: '想学习更多前端知识,请关注公众号:前端微站',  writable: true});data.text = '关注公众号前端微站,学习更多前端知识';  // 修改成功console.log(data.text);   // 关注公众号前端微站,学习更多前端知识

④ enumerable 

默认值为false,表示该对象属性不能被枚举,只有为true时才可枚举。例如:

let data = {  text: '想学习更多前端知识,请关注公众号:前端微站'};Object.defineProperty(data,'name',{  value: '前端王睿'});console.log(data);  //  {text: "想学习更多前端知识,请关注公众号:前端微站", name: "前端王睿"}for(let key in data){  console.log(data[key]);  // 想学习更多前端知识,请关注公众号:前端微站}

我们发现,data对象中虽然已经有两个属性,可我们发现最终却只能遍历出text这一个属性。这时只需给name属性设置enumerable值为true即可愉快地枚举出来了。

let data = {  text: '想学习更多前端知识,请关注公众号:前端微站'};Object.defineProperty(data,'name',{  value: '前端王睿',  enumerable: true  //  想学习更多前端知识,请关注公众号:前端微站});console.log(data);  //  {text: "想学习更多前端知识,请关注公众号:前端微站", name: "前端王睿"}for(let key in data){  console.log(data[key]);  // 想学习更多前端知识,请关注公众号:前端微站  // 前端王睿}

2. 存取描述符

以下这两个函数就是Vue中用于进行数据监听的Object.defineProperty()中属性描述符的两个核心方法。 

① get 

属性的 getter 函数,默认值为undefined。当访问该属性时,会调用此函数,返回值会被用作该属性的值。例如:

let data = {  text: '想学习更多前端知识,请关注公众号:前端微站'};Object.defineProperty(data,'text',{  get(){    return '关注公众号前端微站,学习更多前端知识'  }});console.log(data);  // {text: "关注公众号前端微站,学习更多前端知识"}

可以看到,此时data其实已经被我们修改了!

② set 

属性的 setter 函数,默认值为undefined。当属性值被修改时,会调用此函数,该方法接受一个参数,也就是被赋予的新值。例如:

let data = {  text: '想学习更多前端知识,请关注公众号:前端微站'};Object.defineProperty(data,'text',{  set(value){    console.log(value);  // 关注公众号前端微站,学习更多前端知识  }});data.text = '关注公众号前端微站,学习更多前端知识';

*注意:valuewritable不能与存取描述符(getset)同时存在,不然会报错!这是因为两者之间可能会存在互斥关系,例如:value值与get返回值不同,writablefalse而使用get却可改变对象属性的值,等等。

三、使用Object.defineProperty()写个简单的响应式渲染

let oText = document.getElementById('text'),    oInput = document.getElementById('input');let data = {  text: ''};Object.defineProperty(data,'text',{  set(value){    oText.innerHTML = value; // 当修改data.text值时,自动更改oText中的文字内容  }});oInput.value = data.text = '前端微站';oInput.addEventListener('keyup', function () {  data.text = this.value;});

446871a97877017070645514a7b3304b.gif


重点总结

① Object.defineProperty()中的 数据描述符 可以用来禁止对象属性的 删除、修改和枚举 操作

② Object.defineProperty()中的 存取描述符 可以用来对 对象赋值 和 获取属性值 进行拦截操作

结束语

看我在这啰嗦了半天,可最终能不能掌握Object.defineProperty()的用法还是应该在实践中多总结,而不是简单地看完了就结束了。最后给大家安排个作业,利用Object.defineProperty()的存取描述符,实现一个超简易的Vue,具体功能就是实现上面这个简单的响应式渲染功能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值