• javaScript系列 [40]defineProperty


    本文介绍Object.defineProperty()方法,并基于此简单讨论数据劫持的实现方案。

    defineProperty

    Object.getOwnPropertyDescriptor(target,attrName)方法用于获取对象的属性描述符对象,该方法的第一个参数为目标对象,第二个参数为指定的属性名。

    我们可以利用该方法来查看对象属性的描述符配置项(包括:value值writable可重写enumerable可枚举configurable可配置等)。默认正常的对象属性中,这些配置项的值都是 true

    var o = { name: "文顶顶", age: 18 };
    var des = Object.getOwnPropertyDescriptor(o, "name");
    console.log(des);
    /* { value: '文顶顶',writable: true,enumerable: true,configurable: true } */
    

    Object.defineProperty(target,attrName,options)方法用于定义(设置)对象并对指定的属性描述符对象进行配置。该方法的第一个参数为目标对象,第二个参数为指定的属性名,第三个参数为配置对象。

    /* 备注:给o对象添加address属性,并设置属性值为香悦山 */
    /* 说明:默认新添加的属性,属性描述配置项均为false */
    var o = { name: "文顶顶", age: 18 };
    Object.defineProperty(o, "address", { value: "香悦山" });
    console.log(Object.getOwnPropertyDescriptor(o, "address"));
    /* { value: '香悦山',writable: false,enumerable: false,configurable: false } */
    
    /* 备注:重新定义age属性,设置属性值为20,该属性值可配置但无法重写和枚举 */
    Object.defineProperty(o, "age", { value: 20, enumerable: false, writable: false });
    console.log(Object.getOwnPropertyDescriptor(o, "age"));
    /* { value: 20,writable: false,enumerable: false,configurable: true } */
    o.age = 99;
    console.log(o.age); //20
    for (var key in o) {
        console.log(key, o[key]);
    }
    /* name 文顶顶 */
    /* 在for...in循环中,age键值对 ,以及新添加的address键值对均没有被枚举 */
    

    Object.defineProperties(target,options)方法用于一次性设置(添加)对象的多个属性,与之对应的Object.getOwnPropertyDescriptors(target)方法用于获取对象中所有成员的 详细 配置信息。

    Object.defineProperties(o, {
        "className": {
            value: "H5",
            configurable: true,
        },
        "friends": {
            value: ["胡适", "沈从文", "辜鸿铭"],
            configurable: true,
            writable: true
        }
    });
    console.log("_____");
    console.log(Object.getOwnPropertyDescriptors(o));
    /* 
    {   name:{ value: '文顶顶', writable: true,enumerable: true,configurable: true },
        age:{ value: 20,writable: false,enumerable: false,configurable: true },
        address:{ value: '香悦山',writable: false,enumerable: false,configurable: false },
        className:{ value: 'H5',writable: false,enumerable: false,configurable: true },
        friends:{ value: [ '胡适', '沈从文', '辜鸿铭' ],
                  writable: true, enumerable: false,configurable: true } } */
    

    Object.defineProperty 方法主要用于对象中的某个属性进行访问配置,如果需要对整个对象执行类似的操作则可使用Object.preventExtensions()Object.seal()Object.freeze() 等方法,它们分别对应着禁止扩展密封对象以及要冻结对象。

    Getter and Setter

    对于对象字面量创建的对象而言,我们可以直接通过get attrNameset attrName的方式来对属性的设置和读取操作进行拦截和监听。通过下面的代码,我们可以观察到,对象属性的 Getter 和 Setter 的代码并不复杂但却需要借助一个无关的中间变量_age来实现。

    /* getter 和 setter */
    var o = {
        name: "文顶顶",
        _age: 17,
        get age() {
            console.log("监听到执行了getter方法");
            return this._age;
        },
        set age(val) {
            console.log("监听到执行了setter方法");
            this._age = val;
        }
    }
    console.log(o.age);
    o.age = 100;
    console.log(o.age);
    /* 
    监听到执行了getter方法
    17
    监听到执行了setter方法
    监听到执行了getter方法
    100 
    */
    

    Object.defineProperty()方法的配置对象中也支持对象属性的 GetterSetter操作。

    (function() {
        var o = { name: "文顶顶", age: 17 };
        var temp = 18;
        Object.defineProperty(o, "age", {
            get() {
                console.log("——getter———");
                return temp;
            },
            set(val) {
                console.log("——setter———");
                temp = val;
            }
        });
        console.log(o.age);
        o.age = 100;
        console.log(o.age);
    
        /* 执行情况 */
        // ——getter———
        // 18
        // ——setter———
        // ——getter———
        // 100
    })();
    

    利用`Object.defineProperty()`方法,来监听对象属性的设置和读取操作,可以不必借助于中间属性来实现而改用一个外部变量即可,这样的处理方式为代码的封装提供了可能。

    注意defineProperty 方法内部使用 set 和 get 函数时不能与 value 和 writable 共存。上述的代码演示了监听对象单个属性读写的方案,如果需要为对象中所有的属性都添加 set 和 get 监听,可以考虑对上述代码进行封装。

    let observer = (target) => {
        if (typeof target !== "object" || target == null) return;
        for (var key in target) {
            if (target.hasOwnProperty(key)) defineReactive(target, key, target[key]);
        }
    }
    
    let defineReactive = (target, key, val) => {
        observer(val); /* 递归解决多层对象解构问题 */
        /* val 是外部传入的参数:就是指定属性的默认值 */
        Object.defineProperty(target, key, {
            get() {
                handler("getter")
                return val;
            },
            set(_val) {
                handler("setter")
                val = _val;
            }
        })
    }
    
    let handler = (text) => {
        console.log("监听到" + text);
    }
    
    /* 测试代码 */
    var o = { name: "文顶顶", age: 18, car: { color: "white" } };
    observer(o);
    o.age = 100;
    console.log(o.age);
    o.car.color = "black";
    console.log(o.car.color);
    
    /* 执行情况 */
    // 监听到setter
    // 监听到getter
    // 100
    // 监听到getter
    // 监听到setter
    // 监听到getter
    // 监听到getter
    // black
    
  • 相关阅读:
    Android应用程序组件Content Provider在应用程序之间共享数据的原理分析
    Android录制声音(二)录音输输出格式
    Firemacs:像垄断 Emacs 一样垄断 Firefox
    超棒的 KDE 面板小序次-Kirocker Music Display
    特征化设置你的linux环境
    软响铃 ── 把 beep 换成音乐
    简化 Picasa 图片上传
    Moonlight:Linux 平台上的 Silverlight
    Wine 0.9.39
    本钱与linux
  • 原文地址:https://www.cnblogs.com/wendingding/p/15760934.html
Copyright © 2020-2023  润新知