美文网首页
如何在ES5的环境下实现const

如何在ES5的环境下实现const

作者: 程序小小黑 | 来源:发表于2020-11-10 15:01 被阅读0次

    Vue双向绑定的核心实现思路就是利用Object.defineProperty对get跟set进行劫持,监听用户对属性进行调用以及赋值时的具体情况,从而实现的双向绑定。
    我们也可以利用Object.defineProperty实现const。

    Object.defineProperty方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。语法如下:
    Object.defineProperty(obj, prop, descriptor)
    其中descriptor代表将被定义或修改的属性描述符。属性描述符有两种主要形式:数据描述符和存取描述符。

    数据描述符有以下选项:
    configurable
    值为 true/false。当前对象元素的属性描述符是否可改,是否可删除。默认为 false。

    enumerable
    值为 true/false。当前对象元素是否可枚举。默认为 false。

    value
    该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。默认为 undefined。

    writable
    值为 true/false。当前对象元素的值是否可修改。默认为 false。

    存取描述符有以下选项:
    get:读取元素属性值时的操作
    set:修改元素属性值时的操作

    当我们将对象属性中的writable设为false的时候,该属性是只读的,就能满足我们对常量的要求了。

    var _const = {};
    Object.defineProperty(_const, "A", {
        value: 1,
        writable: false, //设置属性只读
        configurable: true,
        enumerable: true
    });
    console.log(_const.A);  //1
    _const.A = 2; //在严格模式下会抛错,在非严格模式下静默失败,修改无效。
    

    但此时,我们只要修改属性的数据描述符来修改属性值,依然可以对属性值进行修改:

    var _const = {};
    Object.defineProperty(_const, "A", {
        value: 1,
        writable: false, 
        configurable: true,
        enumerable: true
    });
    
    Object.defineProperty(_const, "A", {
        value: 2,
        writable: true,
        configurable: true,
        enumerable: true
    });
    console.log(_const.A); //2
    _const.A = 3;
    console.log(_const.A); //3
    

    如此我们就需要将configurable设置为false,这样属性就不可配置了。

    var _const = {};
    Object.defineProperty(_const, "A", {
        value: 1,
        writable: false, 
        configurable: false,
        enumerable: true
    });
    console.log(_const.A) //1
    _const.A = 2; //Cannot redefine property: A
    Object.defineProperty(_const, "A", {
        value: 2,
        writable: true,
        configurable: true,
        enumerable: true
    }); //报错!属性不可配置
    

    但是configurable特性表示对象的属性是否可以被删除,以及除writable特性外的其他特性是否可以被修改。所以writable特性依旧可以修改,仅限于由true改为false,不能由false改为true。并且value值的设置也不会应此受到影响,则会出现下述情况:

    var _const = {};
    Object.defineProperty(_const, "A", {
        value: 1,
        writable: true, 
        configurable: false,
        enumerable: true
    });
    console.log(_const.A); //1
    Object.defineProperty(_const, "A", {
        value: 2, //该属性不受configurable的影响
        writable: false, 
        configurable: false,
        enumerable: true
    });
    console.log(_const.A); //2 
    _const.A = 3;
    console.log(_const.A); //2 修改无效
    

    因此,通过Object.defineProperty()方法,使用属性的数据描述符,可以定义一个命名空间,将常量封装在命名空间里面。由于属性描述符默认为false,所以可以这样定义:

    var _const = {};
    Object.defineProperty(_const, "A", {
        value: 1,
        enumerable: true
    });
    Object.defineProperty(_const, "B", {
        value: 2,
        enumerable: true
    });
    

    由于ES5环境没有block的概念,所以是无法百分百实现const,只能是挂载到某个对象下,要么是全局的window,要么就是自定义一个object来当容器

    var __const = function __const (data, value) {
            window.data = value // 把要定义的data挂载到window下,并赋值value
            Object.defineProperty(window, data, { // 利用Object.defineProperty的能力劫持当前对象,并修改其属性描述符
              enumerable: false,
              configurable: false,
              get: function () {
                return value
              },
              set: function (data) {
                if (data !== value) { // 当要对当前属性进行赋值时,则抛出错误!
                  throw new TypeError('Assignment to constant variable.')
                } else {
                  return value
                }
              }
            })
          }
          __const('a', 10)
          console.log(a)
          delete a
          console.log(a)
          for (let item in window) { // 因为const定义的属性在global下也是不存在的,所以用到了enumerable: false来模拟这一功能
            if (item === 'a') { // 因为不可枚举,所以不执行
              console.log(window[item])
            }
          }
          a = 20 // 报错
    

    相关文章

      网友评论

          本文标题:如何在ES5的环境下实现const

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