美文网首页实用前端技术干货
强大的JS方法Object.defineProperty详解及V

强大的JS方法Object.defineProperty详解及V

作者: 张培跃 | 来源:发表于2018-09-18 19:41 被阅读12次

    Object.defineProperty是一个很了不起的方法。vue.js之所以能够实现双向绑定便是拜它所赐!defineProperty直接翻译过来即是“定义属性”,不过该方法可不仅仅是定义属性这么简单,咱们还可以通过它来对属性进行拦截设置!

    我们知道对象是由多个键/值对组成的无序集合。对象当中的属性可以是任意类型的值。我们可以通过构造函数以及字面量的形式来定义对象。

    var obj={};//或obj=new Object;
    // 添加属性(描述)
    obj.userName="laotie";//或 obj["userName"]="laotie"
    // 添加方法(行为)
    obj.run=function(){};//或 obj["run"]=function(){};
    

    为对象增加属性的方法除了上面的方式外,咱们还可以通过Object.defineProperty来定义新属性,或者对原属性进行修改。

    Object.defineProperty()
    语法:
    • Object.defineProperty(obj, prop, descriptor)
    参数说明:
    • obj:必需。目标对象
    • prop:必需。需定义或修改的属性的名字
    • descriptor:必需。目标属性所拥有的特性
      前两个参数不多说了,看代码就明白了,我们主要看第三个参数descriptor,看看它是个什么鬼!

    1、 value

    通过value可以为对象设置属性,对应的值可以是任意类型,默认为undefined
    var obj={};
    console.log(obj.userName);// undefined
    Object.defineProperty(obj,"userName",{
        value:"laozhang"
    });
    console.log(obj.userName);// laozhang
    
    可以对原有值进行修改:
    var obj={};
    obj.userName="laoli"
    console.log(obj.userName);// laoli
    Object.defineProperty(obj,"userName",{
        value:"laozhang"
    });
    console.log(obj.userName);// laozhang
    
    返回的值为传入函数的对象,即第一个参数obj:
    var obj={};
    var obj2=Object.defineProperty(obj,"userName",{
        value:"laozhang"
    });
    console.log(obj==obj2);// true
    

    2、writable

    用于设置属性的值是否允许被重写。true为允许,false不允许被重写,默认为false

    设置为false不允许被重写,并没有错误抛出
    var obj={};
    Object.defineProperty(obj,"userName",{
        value:"laozhang",
        writable:false
    });
    obj.userName="laoliu";
    console.log(obj.userName);// laozhang
    
    如果在严格描述下会报错:
    "use strict";
    var obj={};
    Object.defineProperty(obj,"userName",{
        value:"laozhang",
        writable:false
    });
    obj.userName="laoliu";
    //报错:TypeError: Cannot assign to read only property 'userName' of object
    
    默认为false,值不允许被修改
    var obj={};
    Object.defineProperty(obj,"userName",{
        value:"laozhang"
    });
    obj.userName="laoliu";
    console.log(obj.userName);// laozhang
    
    设置为true,允许被修改
    var obj={};
    Object.defineProperty(obj,"userName",{
        value:"laozhang",
        writable:true
    });
    obj.userName="laoliu";
    console.log(obj.userName);// laoliu
    

    3、enumerable

    该描述决定着指定的属性是否允许被枚举(使用for...in或Object.keys())。设置为true可以被枚举;设置为false,不能被枚举。默认为false。

    设置为false不允许被枚举

    var obj={
        age:18
    };
    Object.defineProperty(obj,"userName",{
        value:"laozhang",
        writable:false,
        enumerable:false
    });
    for(var key in obj){
        console.log(key,obj[key])// age 18
    }
    console.log(Object.keys(obj));//[ 'age' ]
    
    默认值为false, 不允许被枚举
    var obj={
        age:18
    };
    Object.defineProperty(obj,"userName",{
        value:"laozhang",
        writable:false
    });
    for(var key in obj){
        console.log(key,obj[key])// age 18
    }
    console.log(Object.keys(obj));//[ 'age' ]
    
    设置为true,允许被枚举
    var obj={
        age:18
    };
    Object.defineProperty(obj,"userName",{
        value:"laozhang",
        writable:false,
        enumerable:true
    });
    for(var key in obj){
        /*  age 18 
            userName laozhang*/ 
        console.log(key,obj[key]);
    }
    console.log(Object.keys(obj));//[ 'age', 'userName' ]
    

    4、configurable

    configurable是一个总开关,一旦你将它设置为false,就不能删除指定的属性也不能再设置他的(value,writable,configurable),设置为true,允许被删除,也允许被设置。

    为false不允许被删除,不会报错
    var obj={};
    Object.defineProperty(obj,"userName",{
        value:"laozhang",
        configurable:false
    });
    delete obj.userName;
    console.log(obj.userName);//laozhang
    
    为false不允许被重新设置,会报错
    var obj={};
    Object.defineProperty(obj,"userName",{
        value:"laozhang",
        configurable:false
    });
    Object.defineProperty(obj,"userName",{
        value:"laoli"
    });
    //报错:TypeError: Cannot redefine property: userName
    
    为true时,允许被删除
    var obj={};
    Object.defineProperty(obj,"userName",{
        value:"laozhang",
        configurable:true
    });
    delete obj.userName;
    console.log(obj.userName);//undefined
    
    为true时,允许被重新设置
    var obj={};
    Object.defineProperty(obj,"userName",{
        value:"laozhang",
        configurable:true
    });
    Object.defineProperty(obj,"userName",{
        value:"laoli"
    });
    console.log(obj.userName);//laoli
    
    5、get/set存取器描述

    当你需要设置或获取对象的某个属性值的时候,可以使用该方法。

    var obj={};
    var initValue="xixi";
    Object.defineProperty(obj,"userName",{
        get(){
            // 当读取userName时会有输出
            console.log("执行了get");
            return initValue;
        },
        set(newValue){
            // newValue为写入的值
            console.log("执行了set");
            initValue= newValue+"吧!"
        }
    });
    
    /*  执行了get
        xixi */
    console.log(obj.userName);
    
    obj.userName="爱我";// 执行了set
    
    /*  执行了get
        爱我吧! */
    console.log(obj.userName);
    
    get或set不是必须成对出现,任写其一就可以。
    var obj={};
    Object.defineProperty(obj,"userName",{
        get(){
            return "lala"
        }
    });
    console.log(obj.userName);//lala
    
    当使用了get或set方法,不允许使用writable和value这两个属性
    var obj={};
    Object.defineProperty(obj,"userName",{
        value:"xixi",
        get(){
            return "lala"
        }
    });
    console.log(obj.userName);
    //报错:Invalid property descriptor. Cannot both specify accessors and a value or writable attribute
    

    接下来看个实例:

    var obj={};
    var userName="";
    var userArr=[];
    Object.defineProperty(obj,"userName",{
        get(){
            return userName;
        },
        set(value){
            userArr.push(value);
            userName=value;
        }
    });
    obj.userName="张三";
    obj.userName="李四";
    obj.userName="王五";
    console.log(userArr);// [ '张三', '李四', '王五' ]
    

    以上实例通过存取器成功将userName曾经拥有过的值进行了存储。是不是很神奇,很简单?

    接下来,咱们可以通过defineProperty模拟下VUE.JS的双向绑定:
    <body>
    <input type="text" id="myInp"/>
    <div id="myDiv"></div>
    </body>
    <script>
           var myInp=document.querySelector("#myInp");
           var myDiv=document.querySelector("#myDiv");
           var obj={
               v:"haha"
           }
           myInp.value=obj.v;
           myDiv.innerHTML=obj.v;
    
           Object.defineProperty(obj,"v",{
               set:function(v){
                   myDiv.innerHTML=myInp.value=v;
               }
           })
           myInp.onkeyup=function(e){
               console.log(e.target.value);
               obj.v=e.target.value;
           }
    </script>
    

    好了,马上就结束了。可能有的小伙伴会想,既然这个Object.defineProperty如此强大,每次只能设置一个属性吗?那么这玩意儿用起来也挺费劲的!那么现在大咖上场:Object.defineProperties()。你可以通过该大咖同时设置多个对象描述。

    var obj = new Object();
    Object.defineProperties(obj, {
        name: {
            value: '张三',
            configurable: false,
            writable: true,
            enumerable: true
        },
        age: {
            value: 18,
            configurable: true
        }
    })
    console.log(obj.name, obj.age) // 张三, 18
    

    相关文章

      网友评论

      本文标题:强大的JS方法Object.defineProperty详解及V

      本文链接:https://www.haomeiwen.com/subject/ndgxnftx.html