资讯专栏INFORMATION COLUMN

JS属性特性(属性描述符)

yeyan1996 / 783人阅读

摘要:概念中定义了一个名叫属性描述符的对象,用于描述了的各种特征。只指定则表示属性为只读属性。使用属性描述符对象只能在或中使用。修改已有的属性会抛出类型错误异常添加属性会抛出类型错误异常不能修属性结语我对属性描述符很不熟悉,主要是因为平时用得少。

概念

ECMAScript 5 中定义了一个名叫“属性描述符”的对象,用于描述了的各种特征。属性描述符对象有4个属性:

configurable:可配置性,控制着其描述的属性的修改,表示能否修改属性的特性,能否把属性修改为访问器属性,或者能否通过delete删除属性从而重新定义属性。默认值为true

enumerable:可枚举性,表示能否通过for-in遍历得到属性。默认值为true

writable:可写性,表示能否修改属性的值。默认值为true

value数据属性,表示属性的值。默认值为undefined

除了上面的属性,还有两个存取器属性,分别是getset,可以代替valuewritable

get:在读取属性时调用的函数。只指定get则表示属性为只读属性。默认值为undefined

set:在写入属性时调用的函数。只指定set则表示属性为只写属性。默认值为undefined

使用

“属性描述符”对象只能在Object.definePropertyObject.defineProperties中使用。

API 用法
Object.defineProperty:https://developer.mozilla.org...
Object.defineProperties: https://developer.mozilla.org...

var hello = {}

Object.defineProperty(hello, "girl", {
    configurable: false,
    enumberable: false,
    writable: true,
    value: "sexy"
})

// 存取器
Object.defineProperty(hello, "woman", {
    configurable: false,
    enumberable: false,
    get: function() {
        return this.girl
    },
    set: function(val) {
        this.girl = val
    }
})

// 定义多个属性
Object.defineProperties(hello, {
    boy: {
        configurable: false,
        enumberable: false,
        writable: false,
        value: "handsome"
    },
    man: {
        configurable: false,
        enumberable: false,
        writable: true,
        get: function() {
            return this.boy
        }
    }
})

当用Object.definePropertyObject.defineProperties操作(新建或者修改)那些不允许创建或修改的属性时,会抛出类型错误异常。

// 此例子运行在前面的例子的基础上
Object.defineProperty(hello, "boy", {
    writable: true
})    // Uncaught TypeError: Cannot redefine property: boy

因为前面boy属性已经被设置为不可配置,所以这里修改writable会抛出类型错误异常。

通过Object.getOwnPropertyDescriptor或者Object.getOwnPropertyDescriptors可以得到属性描述符。

API 用法
Object.getOwnPropertyDescriptor:https://developer.mozilla.org...
Object.getOwnPropertyDescriptors:https://developer.mozilla.org...

规则
var rules = {
    common: "test"
}

如果属性是不可配置的,则不能修改它的可配置性和可枚举性。

Object.defineProperty(rules, "rule1", {
    configurable: false,
    enumberable: false
})

// 修改configurable会抛出类型错误异常
Object.defineProperty(rules, "rule1", {
    configurable: true
})    // Uncaught TypeError: Cannot redefine property: rule1

// 修改enumberable不会抛出异常,但enmuberable没有被修改
Object.defineProperty(rules, "rule1", {
    enumberable: true
})
Object.getOwnPropertyDescriptor(rules, "rule1")    // Object {value: undefined, writable: false, enumerable: false, configurable: false}

如果存取器属性是不可配置的,则不能修改getset方法,也不能将它转换为数据属性。

Object.defineProperty(rules, "rule2", {
    configurable: false,
    enumberable: false,
    get: function() {
        return this.common
    },
    set: function(val) {
        this.common = val
    }
})

// 修改get或者set方法会抛出类型错误异常
Object.defineProperty(rules, "rule2", {
    get: function() {
        return this.common + "rule2"
    }
})    // Uncaught TypeError: Cannot redefine property: rule2

Object.defineProperty(rules, "rule2", {
    set: function(val) {
        this.common = "rule2"
    }
})    // Uncaught TypeError: Cannot redefine property: rule2

// 将它转换为数据属性同样会抛出类型错误异常
Object.defineProperty(rules, "rule2", {
    value: "rule2"
})    // Uncaught TypeError: Cannot redefine property: rule2

如果数据属性是不可配置的,则不能将它转换为存取器属性;同时,也不能将它的可写性从false修改为true,但可以从true修改为false

Object.defineProperty(rules, "rule3", {
    configurable: false,
    writable: false,
    value: "rule3"
})

// 修改writable为true会抛出类型错误异常
Object.defineProperty(rules, "rule3", {
    writable: true
})


Object.defineProperty(rules, "rule4", {
    configurable: false,
    writable: true,
    value: "rule4"
})

// 可以修改writable为false
Object.defineProperty(rules, "rule4", {
    writable: false
})
Object.getOwnPropertyDescriptor(rules, "rule4")    //   Object {value: "rule4", writable: false, enumerable: false, configurable: false}

如果数据属性是不可配置且不可写的,则不能修改他的值;如果是可配置但不可写,则可以修改他的值(实际上是先将它标记为可写的,然后修改它的值,最后再将它标记回不可写)。

其实这里所说的修改值,是通过Object.definePropertyObject.defineProperties方法修改。通过直接赋值的方法在数据属性不可配置的情况下是不能修改属性值的。

Object.defineProperty(rules, "rule5", {
    configurable: false,
    writable: false,
    value: "rule5"
})

// 修改属性值会抛出类型错误异常
Object.defineProperty(rules, "rule5", {
    value: "rule55"
})    // Uncaught TypeError: Cannot redefine property: rule5

rules.rule5 = "rule55"
// 值没有被修改,也不会抛出异常
rules.rule5            // "rule5"


Object.defineProperty(rules, "rule6", {
    configurable: true,
    writable: false,
    value: "rule6"
})

// 修改属性值
Object.defineProperty(rules, "rule6", {
    value: "rule66"
})
rules.rule6            // "rule66"

rules.rule6 = "rule6"
// 值没有被修改,也不会修改
rules.rule6            // "rule6"

只指定get不能写,如果尝试对该属性赋值,会抛出类型错误异常。(红宝书上说只有在严格模式下才抛出异常)

Object.defineProperty(rules, "rule7", {
    get: function() {
        return this.common
    }
})
rules.rule7 = "rule7"    // Uncaught TypeError: Cannot redefine property: rule7

只指定set不能读,如果尝试读取该属性值,返回undefined。(红宝书上说在严格模式下才抛出异常,但没有)

Object.defineProperty(rules, "rule8", {
    set: function() {
        this.common = "rule8"
    }
})
rules.rule8    // undefined

如果对象是不可扩展的,则可以编辑已有的自有属性,但不能给它添加新属性。

操作对象可扩展性的API有三个:Object.preventExtensionsObject.sealObject.freeze

API 用法
Object.preventExtensions:https://developer.mozilla.org...
Object.seal:https://developer.mozilla.org...
Object.freeze:https://developer.mozilla.org...
Object.isExtensions:https://developer.mozilla.org...
Object.isSealed:https://developer.mozilla.org...
Object.isFrozen:https://developer.mozilla.org...

使用Object.preventExtensions可以将对象转换为不可扩展。
使用Object.isExtensions来判断对象是否可扩展。

var ex = {}
Object.defineProperty(ex, "ex1", {
    configurable: true,
    writable: true,
    value: "ex1"
})
Object.isExtensible(ex)        // true
Object.preventExtensions(ex)
Object.isExtensible(ex)        // false

// 可以修改已有的属性
Object.defineProperty(ex, "ex1", {
    writable: false,
    value: "ex11"
})
Object.getOwnPropertyDescriptor(ex, "ex1")    // Object {value: "ex11", writable: false, enumerable: false, configurable: true}

// 添加属性会抛出类型错误异常
Object.defineProperty(ex, "ex2", {
    value: "ex2"
})    // Uncaught TypeError: Cannot define property:ex2, object is not extensible.

使用Object.seal除了可以将对象转换为不可扩展的,还可以将对象的所有自有属性都转换为不可配置的。即不能给对象添加新属性,而且它已有的属性也不能删除或者配置(这里同样会遵循前面的规则)。
使用Object.isSealed来判断对象是否封闭(sealed)。

var se = {}
Object.defineProperty(se, "se1", {
    configurable: true,
    writable: false,
    value: "se1"
})
Object.isSealed(se)        // false
Object.seal(se)
Object.isSealed(se)        // true

// 修改已有的属性会抛出类型错误异常
Object.defineProperty(se, "se1", {
    writable: true,
    value: "se11"
})    // Uncaught TypeError: Cannot redefine property: se1

// 添加属性会抛出类型错误异常
Object.defineProperty(se, "se2", {
    value: "se2"
})    // Uncaught TypeError: Cannot define property:se2, object is not extensible.

使用Object.freeze除了将对象转换为不可扩展的和将其属性转换为不可配置的之外,还可以将自有属性转换为只读。(如果对象设置了set,存取器属性将不会受影响,仍可以调用set方法,而且不会抛出异常,但如果set方法是改变该对象的属性,则不能修改成功)
使用Object.isFrozen来检测对象是否冻结(frozen)。

var fr = {}
Object.defineProperty(fr, "fr1", {
    configurable: true,
    writable: false,
    value: "fr1"
})
Object.isFrozen(fr)        // false
Object.freeze(fr)
Object.isFrozen(fr)        // true

// 修改已有的属性会抛出类型错误异常
Object.defineProperty(fr, "fr1", {
    writable: true,
    value: "fr11"
})    // Uncaught TypeError: Cannot redefine property: fr1

// 添加属性会抛出类型错误异常
Object.defineProperty(fr, "fr2", {
    value: "fr2"
})    // Uncaught TypeError: Cannot define property:fr2, object is not extensible.

fr.fr1 = "fr11"
// 不能修fr1属性
fr.fr1            // "fr1"
var set = {}
Object.defineProperty(set, "set1", {
    configurable: true,
    value: "set1"
})
Object.defineProperty(set, "set2", {
    configurable: true,
    set: function(val) {
        this.set1 = val
    }
})
Object.isFrozen(set)        // false
Object.freeze(set)
Object.isFrozen(set)        // true

set.set2 = "set2"
set.set1                    // "set1"
结语

我对属性描述符很不熟悉,主要是因为平时用得少。不过最近,开始学写一些小的库(虽然很挫),就感觉属性描述符有使用的场景了。我暂时能想到的就是将库对象的一些属性设置为只读,以防止对象的一些属性被用户重写覆盖了。还有一个用法是在知乎和学vue的时候知道的,就是通过gettersetter实现“监听”对象属性的数据更新(在这里挖一个坑。后面学习一下这种方法,再写一篇“监听”对象属性的数据更新的文章)。
最后,如果大家知道更多属性描述符的使用后场景,希望大家能在评论区留下你们的高见。

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

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

相关文章

  • 浅谈 JS 对象添加 getter与 setter 的5种方法以及如何让对象属性不可配置或枚举

    摘要:需被定义或修改的属性名。当且仅当该属性出现在相应的对象枚举属性中。可以是任何有效的值数值,对象,函数等。方法将返回用作属性的值。该方法将收到作为唯一参数的新值分配给属性。属性用来声明欲修饰属性是否可以被枚举。 定义 getter 与 setter 通过对象初始化器定义 使用 Object.create 方法 使用 Object.defineProperty 方法 使用 Objec...

    Dean 评论0 收藏0
  • JavaScript对象详解

    摘要:属性描述符升级打怪必备技能对象有自己的属性和方法,对于我们对象的属性来讲,属性还有自己的属性,又称为属性描述符。这个方法接受三个参数,第一个是指定的对象,第二个是指定的对象参数,第三个当然是要修改的属性描述符了。 对象的声明有俩种: 字面量 通过new一个构造函数Object 两者唯一的区别就是,字面量形式,可以一次赋值多个,通过new Object就得一个一个赋值 数据类型 ...

    fancyLuo 评论0 收藏0
  • 重温JS基础--JS中的对象属性

    摘要:数据属性有个描述其行为的特性表示能否通过删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。总结一波关于这些内容主要讲的就是在的对象属性可以分为两类,一类就是数据属性,一类就是访问器属性。 前言 面向对象的语言都有一个类的概念,通过类可以创建任意多个具有相同属性和方法的对象。 JavaScript中把对象定义为无序属性的集合,属性可以包含基本值,对象或者函数...

    jerryloveemily 评论0 收藏0
  • JavaScript中的Object.defineProperty()和defineProperti

    摘要:语法将要被添加属性或修改属性的对象该对象的一个或多个键值对定义了将要为对象添加或修改的属性的具体配置张三张三功能该方法返回指定对象上一个自有属性对应的属性描述符。 文章同步到github ECMAS-262第5版在定义只有内部采用的特性时,提供了描述了属性特征的几种属性。ECMAScript对象中目前存在的属性描述符主要有两种,数据描述符(数据属性)和存取描述符(访问器属性),数据描述...

    sanyang 评论0 收藏0
  • Javascript基础:js属性描述详解

    摘要:之后,所有的属性都存在属性描述符这个属性。获取属性描述符属性的方法是以下面的代码为例返回该方法返回的是一个对象,该对象除了包含为的属性外,还包含了属性的三个特性。 今天俺跟大家一起来学习一下一个很小的知识点,那就是属性描述符,需要注意的是该功能是在ES5之后才出现的,也就是说ES5之前伟大的JS创造者并没有开放出该功能~大家现在知道了吧,其实我们学习的语言并不是什么天生的大神创造的,本...

    Clect 评论0 收藏0

发表评论

0条评论

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