Object.defineProperty()方法被许多现代前端框架(如Vue.js,React.js)用于数据双向绑定的实现,当我们在框架Model层设置data时,框架将会通过Object.defineProperty()方法来绑定所有数据,并在数据变化的同时修改虚拟节点,最终修改页面的Dom结构。

 一、语法

  1. Object.defineProperty(obj, prop, descriptor)
        obj:必须,被定义或修改属性的对象;
 
        prop:必须,要定义或修改的属性名称;
 
        descriptor:必须,属性的描述描述符

返回值

函数将返回传递给他的obj对象本身。

二、描述符(descriptor)说明

        该方法允许开发者精确的对对象属性的定义和修改。通过正常赋值进行属性添加而构建的属性会被枚举器方法(如for…in循环或Object.keys方法)获取,从而导致属性值被外部方法改变或删除。而Object.defineProperty()可以避免以上描述的情况,默认的,通过Object.defineProperty()添加的属性是默认不可改变的。
  属性描述参数(descriptor)主要由两部分构成:数据描述符(data descriptor)和访问器描述符(accessor descriptor)。
  数据描述符就是一个包含属性的值,并说明这个值可读或不可读的对象;访问器描述符就是包含该属性的一对getter-setter方法的对象。一个完整的属性描述(descriptor)必须是这两者之一,并且不可以两者都有。

数据描述符和访问器描述符各自都是对象,有以下键值对:

configurable(必须)

仅当设置的属性的描述符需要被修改或需要通过delete来删除该属性时,configurable属性设置为true。默认为false。

enumerable(必须)

仅当设置的属性需要被枚举器(如for…in)访问时设置为true。默认为false。

value(可选)

 设置属性的值,可以是任何JavaScript值类型(number,object,function等类型)。默认为undefined。

writable(可选)

 仅当属性的值可以被赋值操作修改时设置为true。默认为false。
 

访问器描述符可以包含以下可选键值对:

get

属性的getter方法,若属性没有getter方法则为undefined。该方法的返回为属性的值。默认为undefined

set

属性的setter方法,若属性没有setter方法则为undefined。该方法接收唯一的参数,作为属性的新值。默认为undefined。
请牢记,这些描述符的属性并不是必须的,从原型链继承而来的属性也可填充。为了保证这些描述符属性被填充为默认值,你可能会使用形如预先冻结Object.prototype、明确设置每个描述符属性的值、使用Object.create(null)来获取空对象等方式。
  1. // using __proto__
  2. var obj = {};
  3. var descriptor = Object.create(null); // no inherited properties
  4.  
  5. //所有描述符的属性被设置为默认值
  6. descriptor.value = 'static';
  7. Object.defineProperty(obj, 'key', descriptor);
  8.  
  9. //明确设置每个描述符的属性
  10. Object.defineProperty(obj, 'key', {
  11. enumerable: false,
  12. configurable: false,
  13. writable: false,
  14. value: 'static'
  15. });
  16.  
  17. //重用同一个对象作为描述符
  18. function withValue(value) {
  19. var d = withValue.d || (
  20. withValue.d = {
  21. enumerable: false,
  22. writable: false,
  23. configurable: false,
  24. value: null
  25. }
  26. );
  27. d.value = value;
  28. return d;
  29. }
  30. Object.defineProperty(obj, 'key', withValue('static'));
  31.  
  32. //如果Object.freeze方法可用,则使用它来防止对对象属性的修改
  33. (Object.freeze || Object)(Object.prototype);
 

使用示例

创建一个属性

如果当前对象不存在我们要设置的属性,Object.defineProperty()会根据方法设置为对象创建一个新的属性。如果描述符参数缺失,则会被设置为默认值。所有布尔型描述符属性会被默认设置为false。而value,get,set会被默认设置为undefined。一个未设置get/set/value/writable的属性被称为一个“原生属性(generic)”,并且他的描述符(descriptor)会被“归类”为一个数据描述符(data descriptor)。
  1. var o = {}; //创建一个对象
  2.  
  3. //使用数据描述符来为对象添加属性
  4. Object.defineProperty(o, 'a', {
  5. value: 37,
  6. writable: true,
  7. enumerable: true,
  8. configurable: true
  9. });
  10. //属性”a”被设置到对象o上,并且值为37
  11.  
  12. //使用访问器描述符来为对象添加属性
  13. var bValue = 38;
  14. Object.defineProperty(o, 'b', {
  15. get: function() { return bValue; },
  16. set: function(newValue) { bValue = newValue; },
  17. enumerable: true,
  18. configurable: true
  19. });
  20. o.b; //
  21. //属性”b”被设置到对象o上,并且值为38。
  22. //现在o.b的值指向bValue变量,除非o.b被重新定义
  23.  
  24. //你不能尝试混合数据、访问器两种描述符
  25. Object.defineProperty(o, 'conflict', {
  26. value: 0x9f91102,
  27. get: function() { return 0xdeadbeef; }
  28. });
  29. //抛出一个类型错误: value appears only in data descriptors, get appears only in accessor descriptors(value只出现在数据描述符中,get只出现在访问器描述符中)
 

修改一个属性

当某个属性已经存在了,Object.defineProperty()会根据对象的属性配置(configuration)和新设置的值来尝试修改该属性。如果该属性的configurable被设置为false,则该属性无法被修改(这种情况下有个特殊情况:如果之前的writable设置为true,则我们仍可以将writable设置为false,一旦这么做之后,任何描述符属性将变得不可设置)。如果属性的configurable设置为false,则我们无法将属性的描述符在数据描述符和访问器描述符之间转换。
如果新设置的属性和该属性不同,并且该属性的configurable被设置为false,则一个类型错误(TypeError)会被抛出(除了上一段文字中说的特殊情况)。若新旧属性完全相同,则什么都不会发生。

可写特性-writable

当一个属性的writable被设置为false,这个属性就成为“不可写的(non-writable)”。该属性不可被重新赋值。
  1. var obj = {};
  2. Object.defineProperty(obj,"name",{
  3. value:"张三",
  4. writable:false//当设置为false的时候当前对象的属性值不允许被修改
  5. })
  6.  
  7. obj.name="李四"
  8. console.log(obj.name)//张三
  9.  
  10. var obj = {};
  11. Object.defineProperty(obj,"name",{
  12. value:"张三",
  13. writable:true//当设置为true的时候当前对象的属性值允许被修改
  14. })
  15.  
  16. obj.name="李四"
  17. console.log(obj.name)//李四

可枚举特性-enumerable

属性的enumerable值定义对象的属性是否会出现在枚举器(for…in循环和Object.keys())中。
  1. var obj = {name:"张三",age:"李四"}
  2.  
  3. Object.defineProperty(obj,"name",{
  4. enumerable:false//当设置为false的时候对象的属性不可被枚举
  5. })
  6.  
  7. Object.defineProperty(obj,"age",{
  8. enumerable:false
  9. })
  10.  
  11. console.log(Object.keys(obj))//[]
  12.  
  13. var obj = {name:"张三",age:"李四"}
  14.  
  15. Object.defineProperty(obj,"name",{
  16. enumerable:true//当设置为true的时候对象的属性可被枚举
  17. })
  18.  
  19. Object.defineProperty(obj,"age",{
  20. enumerable:true
  21. })
  22.  
  23. console.log(Object.keys(obj))//["name",age]
 

可配置特性-configurable

属性的configurable值控制一个对象的属性可否被delete删除,同时也控制该属性描述符的配置可否改变(除了前文所述在configurable为false时,若writable为true,则仍可以进行一次修改将writable改变为false)。
  1. var obj = {};
  2. Object.defineProperty(obj,"name",{
  3. value:"张三",
  4. configurable:false//当设置为false的时候对象的属性不允许被删除
  5. })
  6.  
  7. delete obj.name;
  8.  
  9. console.log(obj.name)//张三
  10.  
  11. var obj = {};
  12. Object.defineProperty(obj,"name",{
  13. value:"张三",
  14. configurable:true//当设置为true的时候对象的属性允许被删除
  15. })
  16.  
  17. delete obj.name;
  18.  
  19. console.log(obj.name)//undefined

get和set

Get:指读取属性时调用的函数。
Set:指写入属性时调用的函数

  1. var obj = {name:"张三"}
  2.  
  3. Object.defineProperty(obj,"name",{
  4. get(){
  5. console.log("被访问了")//当被访问的时候会触发get()方法
  6.  
  7. },
  8. set(newVal){
  9. console.log("被设置了"+newVal)//当被设置的时候会触发set()方法
  10. }
  11. })
  12. obj.name//输出:被访问了
  13. obj.name="李四";//输出:被设置了李四

添加属性时的默认值

考虑描述符特性的默认值如何被应用是非常重要的。正如下面示例所示,简单的使用"."符号来设置一个属性和使用Object.defineProperty()是有很大区别的。
  1. var o = {};
  2.  
  3. o.a = 1;
  4. //等同于:
  5. Object.defineProperty(o, 'a', {
  6. value: 1,
  7. writable: true,
  8. configurable: true,
  9. enumerable: true
  10. });
  11.  
  12. //另一方面,
  13. Object.defineProperty(o, 'a', { value: 1 });
  14. //等同于:
  15. Object.defineProperty(o, 'a', {
  16. value: 1,
  17. writable: false,
  18. configurable: false,
  19. enumerable: false
  20. });

configurable和writable

一种特殊情况:当configurable为false时,我们唯一仍能改变的属性就是将设置为true的writable设置为false。
  1. var a={};
  2. Object.defineProperty(a,"o",{
  3. configurable:false,
  4. value:10,
  5. writable:true
  6. });
  7.  
  8. console.log(a.o);//10
  9. a.o=12;//不报错
  10. console.log(a.o);//12
  11.  
  12. Object.defineProperty(a,"o",{
  13. configurable:false,
  14. value:14,
  15. writable:true
  16. });
  17. console.log(a.o);//14
  18.  
  19. Object.defineProperty(a,"o",{
  20. configurable:false,
  21. value:14,
  22. writable:false
  23. });
  24. a.o=16;//不报错
  25. console.log(a.o);//14
  26.  
  27. //报错
  28. Object.defineProperty(a,"o",{
  29. configurable:false,
  30. value:16,
  31. writable:false
  32. });
由以上代码可以得出结论,对于描述符(descriptor)为数据描述符(data descriptor)的情况:
1.使用“.”操作符来设置属性的值永远不会报错,仅当writable为false时无效。
2.只要writable为true,不论configurable是否为false,都可以通过Object.defineProperty()来修改value的值。
由此得出结论,各大浏览器运营商实现的Object.defineProperty()和标准描述在configurable的定义上稍有偏差。描述符为数据描述符时值的改变与否仅受writable的控制。

【Vue】-- 数据双向绑定的原理 --Object.defineProperty()的更多相关文章

  1. vue数据双向绑定的原理、虚拟dom的原理

    vue数据双向绑定的原理https://www.cnblogs.com/libin-1/p/6893712.html 虚拟dom的原理https://blog.csdn.net/u010692018/ ...

  2. 西安电话面试:谈谈Vue数据双向绑定原理,看看你的回答能打几分

    最近我参加了一次来自西安的电话面试(第二轮,技术面),是大厂还是小作坊我在这里按下不表,先来说说这次电面给我留下印象较深的几道面试题,这次先来谈谈Vue的数据双向绑定原理. 情景再现: 当我手机铃声响 ...

  3. Vue数据双向绑定原理及简单实现

    嘿,Goodgirl and GoodBoy,点进来了就看完点个赞再go. Vue这个框架就不简单介绍了,它最大的特性就是数据的双向绑定以及虚拟dom.核心就是用数据来驱动视图层的改变.先看一段代码. ...

  4. vue数据双向绑定原理

    vue的数据双向绑定的小例子: .html <!DOCTYPE html> <html> <head> <meta charset=utf-> < ...

  5. Vue数据双向绑定原理(vue2向vue3的过渡)

    众所周知,Vue的两大重要概念: 数据驱动 组件系统 1 2 接下来我们浅析数据双向绑定的原理 一.vue2 1.认识defineProperty vue2中的双向绑定是基于definePropert ...

  6. 【学习笔记】剖析MVVM框架,简单实现Vue数据双向绑定

    前言: 学习前端也有半年多了,个人的学习欲望还比较强烈,很喜欢那种新知识在自己的演练下一点点实现的过程.最近一直在学vue框架,像网上大佬说的,入门容易深究难.不管是跟着开发文档学还是视频教程,按步骤 ...

  7. Vue实现双向绑定的原理以及响应式数据

    一.vue中的响应式属性 Vue中的数据实现响应式绑定 1.对象实现响应式: 是在初始化的时候利用definePrototype的定义set和get过滤器,在进行组件模板编译时实现water的监听搜集 ...

  8. vue双向数据绑定的原理-object.defineProperty() 用法

    有关双向数据绑定的原理 关于数据双向绑定的理解:利用了 Object.defineProperty() 这个方法重新给对象定义了新属性,在操作新属性分别为为获取属性值(调用get方法)和设置属性值(调 ...

  9. 深入理解Proxy 及 使用Proxy实现vue数据双向绑定

    阅读目录 1.什么是Proxy?它的作用是? 2.get(target, propKey, receiver) 3.set(target, propKey, value, receiver) 4.ha ...

随机推荐

  1. 满汉全席[2-SAT]

    题面 对不起我又写了一个板题qvq 和洛谷那道模板题没区别...两样菜至少做一样即可 不过注意define和函数的区别!!! #include <cmath> #include <c ...

  2. shell实战之日志脱敏

    本次实战目标为日志脱敏,将日志目录内的所有文件进行处理,凡是涉及到卡号和密码的信息,一律以“*”号替代,要替代的内容都从对应的标签内获取,本脚本执行目录 drwxr-xr-x 5 root root ...

  3. NOI真题记录

    NOI2001 食物链,拓展域并查集. 炮兵阵地,棋盘状压DP. NOI2002 银河英雄传说,kruskal重构树/带权并查集. 贪吃的九头龙,树形DP. NOI2003 逃学的小孩,树形DP,二次 ...

  4. Wannafly挑战赛23 T2游戏 SG函数

    哎,被卡科技了,想了三个小时,最后还是大佬给我说是\(SG\)函数. \(SG\)函数,用起来很简单,证明呢?(不可能的,这辈子都是不可能的) \(SG\)定理 游戏的\(SG\)函数就是各个子游戏的 ...

  5. 第三十九篇-RecyclerView的使用

    RecyclerView介绍 RecyclerView的出现可以替代ListView,并且比ListView更高级且更具灵活性.如果有数据集合,其中的元素将因用户操作或网络事件而在运行时发生改变,请使 ...

  6. 友盟冲突解决com.umeng.weixin.handler.UmengWXHandler cannot be cast to com.umeng.socialize.handler.UMWXHandler

    删掉一个试试

  7. Java多线程_复习(更新中!!)

    java多线程的常见例子 一.相关知识: Java多线程程序设计到的知识: (一)对同一个数量进行操作 (二)对同一个对象进行操作 (三)回调方法使用 (四)线程同步,死锁问题 (五)线程通信 等等 ...

  8. maven转gradle ,windows错误重定向

    gradle init --type pom --stacktrace > g.log 2>&1

  9. 第二节:重写(new)、覆写(overwrite)、和重载(overload)

    一. 重写 1. 关键字:new 2. 含义:子类继承父类中的普通方法,如果在子类中重写了一个和父类中完全相同的方法,子类中会报警告(问是否显式的隐藏父类的中的方法),如果在子类中的方法前加上new关 ...

  10. npm常用命令学习(npm install -D,semver版本规范, npm进行版本管理的最佳实践用法)

    什么是npm npm有两层含义.一层含义是Node的开放式模块登记和管理系统,网址为npmjs.org.另一层含义是Node默认的模块管理器,是一个命令行下的软件,用来安装和管理Node模块. npm ...