资讯专栏INFORMATION COLUMN

js保存常量,使其只可读,实现方式有哪些

liuhh / 3082人阅读

摘要:保存常量,使其只可读,实现方式有哪些语法中的常量声明符如果声明一个对象会如何改变该对象的属性声明的对象属性仍然可以改变,因为仅仅只是变量指向的那个内存地址不能改动。

保存常量,使其只可读,实现方式有哪些

1 . es6语法中的常量声明符 const

const freeze = "strange"

freeze = "tony" //  => Uncaught TypeError: Assignment to constant variable.

如果const声明一个对象会如何?

const freezeHero = {
    name: "strange", 
    skill: "magic"
    }

freezeHero = {
    name: "no"
}   //  => Uncaught TypeError: Assignment to constant variable.

//  改变该对象的属性
freezeHero.name = "tony"
freezeHero.skill = "equip"
console.log(freezeHero) //  => {name: "tony", skill: "equip"}

const声明的对象属性仍然可以改变,因为仅仅只是变量指向的那个内存地址不能改动。

2 . Object.freeze()

Object.freeze()同样也是es6新增的api

const freezeMan = {
    name: "tony"
}
Object.freeze(freezeMan)
freezeMan.name = "strange"
freezeMan.skill = "magic"
console.log(freezeMan)  //  => {name: "tony"}

可以看到,对象的静态属性变为只读,不可修改,且不可以添加新属性,如果属性本身也是对象会如何?

const freezeMen = {
    members: ["tony", "strange"], 
    level: 2
}
Object.freeze(freezeMen)
freezeMen.level = 4
//  修改对象的members属性
Array.prototype.push.apply(freezeMen.members, ["captain", "hulk"])

console.log(freezeMen)  // => {members: ["tony", "strange", "captain", "hulk"], level: 2}

被锁定的对象,属性值为简单类型时会被freeze,但值为对象时仍然可以修改,这与const声明符的原理一致。下面通过递归的方式,实现对象引用的深层次锁定,对象的任何属性都不可重写,也不可动态添加新属性

const freezeMen = {
    members: ["tony", "strange"], 
    level: 2
}
const deepLock = function(obj){
    Object.freeze(obj)
    Object.keys(obj).map((k, i) => {
        if(typeof obj[k] === "object"){
            deepLock(obj[k])
        }
    })
    return obj
}
deepLock(freezeMen).members = ["captian", "hulk"]
freezeMen.victory = true

console.log(freezeMen)  // => {members: ["tony", "strange"], level: 2} 

//  如果再想通过defineProperty方法来增加新属性,会直接抛出异常
Object.defineProperty(freezeMen, "lastDefine", {
    writable: false,
    value: "it is lastDefine",
    enumerable: true
})
//  => Uncaught TypeError: Cannot define property lastDefine, object is not extensible

3 . Object.defineProperty

用这个方法实现的效果与freeze方法差不多,设置writable属性值为只读,对于简单值类型有效,而属性值本身为对象时仍然是可以修改其值的。同样可以使用递归来实现

var lockProperty = function(data) {
    if(typeof data === "object") {
        Object.keys(data).map(key => {
            defineDisWritable(data, key, data[key])
        })
    }
    return data
}
var defineDisWritable = function(obj, key, val) {
    Object.defineProperty(obj, key, {
        writable: false,
        value: val,
        enumerable: true
    })
    if(typeof val === "object") {
        lockProperty(val)
    }
}
const freezeMen = {
    members: {
        people: {
            name: "default"
        }
    }, 
    level: 2
}
lockProperty(freezeMen)

freezeMen.add = "new key"
freezeMen.level = 10
freezeMen.members = {
    house: "big"
}

freezeMen.members.people.name = "modified"
console.log(freezeMen)  //  => {add: "new key", members: {people: {name: "default"}, level: 2}

// 我们试试使用defineProperty添加新属性
Object.defineProperty(freezeMen, "lastkey", {
    writable: false,
    value: "last",
    enumerable: true
})
console.log(freezeMen) // => {add: "new key", members: {people: {name: "default"}, level: 2, lastkey: "last"}

上述方法也可以实现对象深层嵌套的属性冻结,与Object.freeze()的唯一区别是,传递的顶层对象仍然可以添加新的属性(不管是通过动态添加还是Object.defineProperty)。

还可以通过劫持setter来锁定通过defineProperty方法添加的属性。

var lockProperty = function(data) {
    if(typeof data === "object") {
        Object.keys(data).map(key => {
            defineDisWritable(data, key, data[key])
        })
    }
    return data
}
var defineDisWritable = function(obj, key, val) {
    Object.defineProperty(obj, key, {
        set: function(newVal) {
            //  不赋新值
            // val = newVal
        },
        get: function() {
            return val
        },
        enumerable: true
    })
    if(typeof val === "object") {
        lockProperty(val)
    }
}
const freezeMen = {
    members: {
        people: {
            name: "default"
        }
    }, 
    level: 2
}
lockProperty(freezeMen)

freezeMen.add = "new key"
freezeMen.level = 10
freezeMen.members = {
    house: "big"
}

freezeMen.members.people.name = "modified"
console.log(freezeMen)  //  => {add: "new key", members: {people: {name: "default"}, level: 2}

_比较Object.defineProperty()Object.freeze()两种方法的递归方案,对于复杂的数据对象,可以实现两种情况:

1.要存储一个完全不可写的数据,使用Object.freeze();
2.要存储一个不可修改但可拓展的数据,使用Object.defineProperty()

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/89672.html

相关文章

  • Thunk深入解析

    摘要:捕抓信息,并且出错时,传递给回调函数回调函数应该只调用一次。总结在学习一个概念或者一个模块时,测试代码加深你对知识的理解和掌握。 一步步打造thunkify 本文的思路: 学习thunk相关知识,主要参考阮一峰的介绍 一步步实现thunkify模块,并且使用测试用例来完善我们的代码,打造出一个健壮的模块 1. 诞生背景 Thunk函数的诞生是源于一个编译器设计的问题:求值策略,即函...

    xi4oh4o 评论0 收藏0
  • 关于const的声明

    摘要:声明变量常量常量,常量的值非复合型数据不可以改变报错已声明为一个常量,常量一旦声明只可读,不允许改变,去修改这个常量就会抛出错误。 const声明变量(常量): 1、常量,常量的值(非复合型数据)不可以改变 const temp = 10; temp = 5;//报错: Assignment to constant variable. //temp已声明为一个常量,常量一旦声明只可读,...

    Profeel 评论0 收藏0
  • 0基础C语言保姆教学——第五节 数组

    摘要:关注我,订阅专栏基础语言保姆教学,就可以持续读到我的文章啦本文为万字长文,满满干货。那么,上面的代码所运行的结果就是一维数组的使用使用即可以访问并可以修改,即可读可写。 大家好~~~我是开心学编程,学到无极限的@jxwd? 写在前面: 各位小伙伴还在为C语言的学习而苦恼嘛? 还在为...

    RobinQu 评论0 收藏0
  • PHP Q&A

    摘要:有哪些作用域函数作用域类作用域函数作用域是什么函数体是一个局部作用域函数体中无法直接访问外部全局变量,必须通过才能访问外部全局变量无法访问通知错误正确访问外部全局变量无法访问函数体中可以直接访问常量输出函数体中可以直接访问类静态属性静态方法 PHP 有哪些作用域? 函数作用域 类作用域 PHP 函数作用域是什么? 函数体是一个局部作用域 函数体中无法直接访问外部全局变量,必须通...

    mikasa 评论0 收藏0
  • JS 一定要放在 Body 的最底部么?聊聊浏览器的渲染机制

    摘要:所以,抛开这些歧义和陷阱,我的问题变成了标签的位置会影响首屏时间么然而答案并不是那么显而易见,这得从浏览器的渲染机制说起。 说明: 本文提到的浏览器均是指Chrome。 script标签指的都是普通的不带其他属性的外联javascript。 web性能优化的手段并不是非黑即白的,有些手段过头了反而降低性能,所以在讨论条件和结论的时候,虽然很多条件本身会带来其他细微的负面或正面影响,为...

    VincentFF 评论0 收藏0

发表评论

0条评论

liuhh

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<