• 使用 defineProperty 劫持数据属性的改变


    使用defineProperty劫持数据属性的变化

    例子一:有一个全局变量a,有一个全局函数b,实现一个`bindData`,执行后,a的任何赋值都会触发b的执行
    // var a = 1;
    a = 1
    // console.log(Object.getOwnPropertyDescriptor(window,'a')); // 自定义的全局变量是可配置的
    // console: { value: 1, writeable: true, enumerable: true, configurable: true }
    function b () {
        alert('a的值发生改变');
    }
    bindData(window, b); // Uncaught TypeError: Cannot redefine property: a;(如果a是通过var声明的)

    function bindData(target, event{
        for (var k in target) { // 给全局对象的任意属性都进行变化检测
            if (target.hasOwnProperty(k)) {
                let descriptor = Object.getOwnPropertyDescriptor(window, k);
                // Object.getOwnPropertyDescriptor(window, "TEMPORARY") => undefined
                // 找出对象可配置的属性
                if (descriptor && descriptor.configurable) { // configurable 为 true, 才可以使用 defineProperty()方法来修改属性描述符

                    (function () {
                        var v = target[k]; // 闭包
                        Object.defineProperty(target, k, {
                            getfunction () {
                                return v;
                            },
                            setfunction (newValue{
                                v = newValue;
                                event.call(this);
                            }
                        })
                    })();

                }
            }
        }
    }

    a = 2// alert: a的值发生改变

    另外,注意

    可配置性决定是否可以使用delete删除属性,以及是否可以修改属性描述符的特性

    1. 设置 configurable:false 后,无法使用 delete 删除属性
    2. 一般地,设置 configurable:false 后,将无法再使用 defineProperty() 方法来修改属性描述符
    // 使用var声明全局变量时,变量的configurable为false
    var a = 1;
    console.log(Object.getOwnPropertyDescriptor(window,'a'));
    // console: { value: 1, writable: true, enumerable: true, configurable: false }

    delete window.a // false // 在严格模式下删除为configurable为false的属性,会提示类型错误TypeError
    Object.defineProperty(window,'a',{
        configurable:true
    });
    // Uncaught TypeError: Cannot redefine property: a

    但有一个例外,设置configurable:false后,只允许writable的状态从true变为false,但不允许将writable的状态从false变为true

    如果在定义属性描述符特性时,未指定configurable,writable,enumerable的值,那么它的值默认是 false

    // configurable, writable 均未设置,默认为false;由于configurable为false,因此不可 redefine property
    var obj = {}
    Object.defineProperty(obj, 'name', {
        value'gg'
    })

    Object.getOwnPropertyDescriptor(obj, 'name')
    // {value: "gg", writable: false, enumerable: false, configurable: false}

    obj.name = 'hello'
    console.log(obj.name) // 'gg'; 由于writable:false生效,对象name属性无法修改值,所以obj.name = 'hello'的赋值语句静默失败

    // 在 configurable: false 的情况下,试图将writable从false改为true,会报错
    Object.defineProperty(obj, 'name', {
        writabletrue
    })
    // Uncaught TypeError: Cannot redefine property: name
    // configurable设置为true, writable 未设置,默认为false;configurable为true,因此可以配置 writable为true
    var obj = {}
    Object.defineProperty(obj, 'name', {
        configurabletrue
        value'gg'
    })

    Object.getOwnPropertyDescriptor(obj, 'name')
    // {value: "gg", writable: false, enumerable: false, configurable: true}

    obj.name = 'hello'
    console.log(obj.name) // 'gg'; 由于writable:false生效,对象name属性无法修改值,所以obj.name = 'hello'的赋值语句静默失败

    // 在 configurable: true 的情况下,可以将writable从false改为true
    Object.defineProperty(obj, 'name', {
        writabletrue
    })

    obj.name = 'hello'
    console.log(obj.name) // 'hello'; // 说明 writable 修改成功
    Object.getOwnPropertyDescriptor(obj, 'name')
    // {value: "hello", writable: true, enumerable: false, configurable: true}
    // writable设置为true, configurable 未设置,默认为false; 不可修改属性描述符的特性,
    // 但有一个例外,设置configurable:false后,允许writable的状态从true变为false !!!
    var obj = {}
    Object.defineProperty(obj, 'name', {
        writabletrue
        value'gg'
    })

    Object.getOwnPropertyDescriptor(obj, 'name')
    // {value: "gg", writable: true, enumerable: false, configurable: false}
    obj.name = 'hello'
    console.log(obj.name) // 'hello'; // writable 为true,可以修改name属性

    // 在configurable为false的情况下,只允许writable的状态从true变为false !!!
    Object.defineProperty(obj, 'name', {
        writablefalse
    })
    obj.name = 'good job'
    console.log(obj.name) // 'hello'; // 由于writable:false修改生效,对象name属性无法修改值,obj.name = 'good job'的赋值语句静默失败

    Object.getOwnPropertyDescriptor(obj, 'name')
    // {value: "hello", writable: false, enumerable: false, configurable: true}
    例子二:快速定位不小心暴露到全局的变量
    // 函数泄漏全局变量的场景
    /*
    function A () {
        // 在一个函数中多次用到了 for 循环, 为了节省变量, 都是用了 i
        for (var i=0; i<5; i++) {
            // ...
        }
        for (i=0; i<5; i++) {
            // ...
        }
        for (i=0; i<5; i++) {
            // ...
        }
    }

    // 在某次分拆函数的时候, 忘记在新函数中对抽取出来的 for 循环中的变量进行重新定义
    // 从而导致该变量成为泄漏的全局变量
    function A () {
        for (var i=0; i<5; i++) {
            // ...
        }
        for (i=0; i<5; i++) {
            // ...
        }
    }

    function B () {
        for (i=0; i<5; i++) { // 这里 i 成了泄漏的全局变量
            console.log(i);
        }
    }
    */


    // 通过数据劫持对泄漏的全局变量进行检测
    (function () {
        var value = window["i"];
        Object.defineProperty(window"i", {
            get () {
                return value;
            },
            set (newValue) {
                debugger;
                value = newValue;
            },
            enumerabletrue,
            configurabletrue,
        });
    })();

    // 更快的解决方式
    // window.__defineSetter__('i', function(){ debugger })

    // 'use strict' 使用严格模式可以避免出现未定义的变量
    function B () {
        for (i=0; i<5; i++) {
            console.log(i);
        }
    }

    B(); // 运行 B 函数, i 变为全局变量; 可以被我们的 debugger 调用栈检测到

    /*
    // 如果在函数里对 i 进行声明, 那么就不会被检测
    function B () {
        for (var i=0; i<5; i++) { // 这里 i 被声明了
            console.log(i);
        }
    }
    */

  • 相关阅读:
    去除inline-block元素间间隙的几种方法
    数组去重的几种方法
    CSS实现水平居中的几种方法
    CSS实现垂直居中的几种方法
    实现一个jQuery的API
    jQuery从入门到放弃
    JavaScript中的DOM与BOM
    JavaScript中的原型与原型链
    爬取某东娃娃评价,生成词云
    vm提示:如果该虚拟机未在使用,请按“获取所有权(T)”按钮获取它的所有权。否则,请按“取消(C)”按钮以防损坏。
  • 原文地址:https://www.cnblogs.com/rencoo/p/11984595.html
Copyright © 2020-2023  润新知