浅谈JavaScript对象属性的特性和defineProperty方法


本文摘自PHP中文网,作者青灯夜游,侵删。

对象是无序属性的集合,而这些属性在创建是都带有一些特征值(可以理解为属性的属性,天生自带的),这些特征值是为了实现JavaScript引擎用的,因此JavaScript不能直接访问。

JavaScript通过这些特征值来定义属性的行为(属性是否删除,枚举,修改等)。

例如,在全局定义的属性是会挂载到window上的。当想删除window上的这个属性,是不可以的。也就是说window上的属性是不可配置的。delete window.obj //false

Function.prototype当你修改成其他值,其原始值并没有改变。是不可写的。

在比如,我们的for in是可以枚举原型链上属性的,但所有的原型顶端都是Object.prototype.但for in 并没有枚举出来。所以Object.prototype是不可枚举的。

属性分为两种类型:1数据属性 2访问器属性。例如:一般我们自己在对象设置的属性默认是数据属性,而Window上的那么属性是访问器属性。

怎样知道这个属性到底是数据属性还是访问器属性?

使用Object.getOwnPropertyDescriptor(属性所在的对象,属性)方法。 返回一个对象,当时访问器属性时,该对象属性有enumerable,configurable,get,set。当时数据属性,该对象返回的属性有value,writable,enumerable,configurable.

1

2

3

4

5

6

var obj  = {name:'zwq',age:18};

console.log(Object.getOwnPropertyDescriptor(obj,'name'));  //name属性属性

//{value: "zwq", writable: true, enumerable: true, configurable: true}

 

console.log(Object.getOwnPropertyDescriptor(window,'name'));  //window上的name属性时访问器属性

//enumerable: true, configurable: true, get: ?, set: ?}

数据属性

 ● 数据上行包含一个数据值的位置,可以读取和写入值,数据属性有4个描述其行为的属性,由于是这些值不能直接访问,是内部值,所以该规范把他们放在两对括号中。

 ● 属性是否可配置:[[Configurable]]:表能否通过delete删除属性,能够修改属性的特性,能否把属性修改为访问器属性

 ● 属性是否可枚举:[[Enumerable]]:表能否通过for-in循环返回属性

 ● 属性是否可修改:[[Writable]]

 ● 属性的数据值 :[[value]]读取属性的时候,从这个位置读,写入属性的时候,把新值保存到这个位置。

普通定义的属性 默认值前三个都是true,最后一个是undefined。

当我们想到修改属性默认的特性,使用Object.defineProperty(属性所在对象,属性的名字,描述符对象)方法。

当使用Object.defineProperty方法第二个参数属性的名字不存在时,该方法会创建这个属性,并且该属性的特性除了value剩下的特性的默认值都是false。也就是说当你想让这个用Object.defineProperty方法创建的属性跟正常的属性一样可枚举,配置,写入,必须把这个属性值的特性都改为true。否则就是false。

1

2

3

4

5

6

7

8

9

10

11

12

var obj  = {name:'zwq',age:18};

 

        Object.defineProperty(obj,'name',{ 修改name属性的特性,值为haha,并且name属性不能修改值

            value:'haha',

            writable:false  //默认值是true,改为false,不可写。

        })

 

        Object.defineProperty(obj,'sex',{创建一个sex属性,这个属性不可枚举

            value:'woman',

            writable:true,

            configurable:true,

        })

访问器属性

访问器属性不包含writable和value,他包含的是一对getter和setter函数,在读取访问器属性是,会调用getter函数,并返回有效的值,在写入访问器属性时(修改属性)会调用setter函数并传入新值。访问器包含4个特性

 ● 属性是否可配置:[[Configurable]]:表能否通过delete删除属性,能够修改属性的特性,能否把属性修改为访问器属性

 ● 属性是否可枚举:[[Enumerable]]:表能否通过for-in循环返回属性

 ● [[Get]]:在读取属性时调用的函数。默认值是undefined。

 ● [[Set]]:在写入(或修改)属性时调用的函数。默认值undefined。

定义访问器属性,同样也必须商用Object.defineProperty().

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

function Person(){

            this._name = 'zwq',

            this.age =  18

        }

        var person = new Person();

         

         

        Object.defineProperty(person,'name',{

            set(newValue){

                console.log('set');

                this._name = newValue   //设置或修改属性时,会调用set函数,把设置的值通过参数传进去后,用一个变量或属性保存。并且当调用get,return就是返回的这个值

            },

            get(){

                return this._name;   //当读取属性时 返回return的值

            }

             

        })

不一定非要同时指定getter和setter,只指定getter意味着属性是不能写。

vue的双向数据劫持绑定(主要应用于表单中)的原理就是利用Object.defineProperty来检测数据的变化。

双向劫持绑定时当视图(页面的某一元素)发生改变时,数据跟着改变,当数据改变时,视图也跟着改变。例如下面的输入框里面的内容改变时,数据(对象或数组)改变。检测数据改变。底下的p文本根据数据的改变而改变。

上面我们介绍到,当数据改变时会触发set方法。由此我们就可以检测数据的变化。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

//检测对象的变化。

     var input = document.getElementById('Oinput');

        var view = document.getElementById('view');

 

        var data = {

            valueObj :{

                value:'zwq'

            }

        }

        //当输入框数据发生改变时,数据跟着改变

        input.oninput = function(){

            data.valueObj.value = this.value;

        }

         

        // 更新视图

        function upData(){  

            view.innerText = data.valueObj.value;

        }

         

        upData(data);

        obServe(data);

 

        // 监控某个对象是否发生改变

        function obServe(data){

           //判断当前传的是否是对象,如果不是,直接return

            if(!data || !(data instanceof Object)){return data}

       //获取所有属性名。使用keys方法可以获取所有属性名(包括原型上的)并保存带数组中

            var arrProperty = Object.keys(data);

       //遍历数组,调用defindRective检测每一个属性值的改变 

            arrProperty.forEach(function(key){

          defindRective(data,key,data[key]); //传入3个参数,当前对象,当前属性,当前属性值

            })

        }

 

        function defindRective(obj,key,val){

           obServe(val);   //使用递归,当想上面的数组,对象套对象的形式,由于里面的对象是一个引用值,无法检测里面的数据变化,所以使用递归。

            Object.defineProperty(obj,key,{  //核心:使用Object,definPropert的set检测数据的改变。

                set(newValue){

                    console.log(5);

                    if(newValue == val) return val;

                    val = newValue;

                    upData();   //当数据变化,跟新视图

                },

                get(){

                    return  val;

                }

            })

        }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

// 监测数组,将数组原型重写

// 当操作数组的arr push pop unshift slice...才会检测

        let {push} = Array.prototype;

        var arr = [1,2,3];

        function upData(){

            console.log('更新');

        }

        Object.defineProperty(Array.prototype,'push',{

            value:(function(){

                return (...arg) => {

                    upData();

                    push.apply(arr,arg);

                }

            })()

        })

 arr.push(8,9);

由于使用Object.defineProperty检测数组和对象变化要分开实现。而且,当添加数据时,不会检测到的。

所以ES6中添加了Proxy来实现。

Proxy&reflect简介:植入代理的模式,以简洁易懂的方式控制对外部对象的访问,利用set,get方法控制属性的读写功能,还有其余的has,desProperty。。等方法。但proxy兼容性不好,使用时要注意。

proxy是一个构造函数,通过代理的方式将你想到代理的对象传给构造函数,并且需要传入参数对象对读和写进行控制。使用new方法实例化代理的对象,

此后,当修改,或添加属性都使用代理的对象。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

let data = {

    value:'zwq',

}

// let data = [1,2];

let oProxyData = new Proxy(data,{

    set(target,key,value,receiver){  //传入4个参数 对象 属性 属性值 代理的对象

        // target[key] = value;

        Reflect.set(target,key,value); //等同于上一步

        upData();

  },

    get(target,key,receiver){

        // console.log(target,key);

        Reflect.get(target, key);

    },

    has(target,key){ //当使用in时触发当前函数。

        return key in target;   //in --检测对象能否访问该属性,能访问返回true,不能false,无论是在实例还是原型中。

    }

});

 

console.log('valu' in oProxyData);

 

function upData(){

    console.log("更新啦");

}

 

oProxyData.value = 20;

本文来自 js教程 栏目,欢迎学习!

以上就是浅谈JavaScript对象属性的特性和defineProperty方法的详细内容,更多文章请关注木庄网络博客

相关阅读 >>

javascript介绍ajax加载单张图片展示进度的方法

javascript之undefined与null的区别(详解)

html标签伪元素绑定事件的三种方式

javascript和java有什么区别

storage event如何实现页面间通信

javascript怎么去除小数点后的数

nodejs的npm常用命令集合

javascript怎么实现秒转时间

使用rxjs管理react应用状态的介绍

javascript中添加注释正确的是什么

更多相关阅读请进入《javascript》频道 >>




打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

分享从这里开始,精彩与您同在

评论

管理员已关闭评论功能...