红茶的个人站点

  • 首页
  • 专栏
  • 开发工具
  • 其它
  • 隐私政策
Awalon
Talk is cheap,show me the code.
  1. 首页
  2. 前端学习笔记
  3. 正文

JavaScript 学习笔记 4:属性

2026年4月7日 3点热度 0人点赞 0条评论

属性描述符

对象属性除了拥有值以外,还有其他特性,比如:

let user = {
    name: 'Tom',
    age: 18,
}
let nameDesc = Object.getOwnPropertyDescriptor(user, 'name');
console.log(nameDesc);
// { value: 'Tom', writable: true, enumerable: true, configurable: true }

这里的Object.getOwnPropertyDescriptor方法可以获取对象指定属性的所有可配置项:

  • value:值

  • writable:是否可写

  • enumerable:是否在迭代属性时被读取

  • configurable:是否可以删除属性并修改这些配置

使用Object.defineProperty方法可以修改属性的配置项:

Object.defineProperty(user, 'name', {
    value: 'Jerry',
    writable: false,
    enumerable: false,
    configurable: false,
});
​
nameDesc = Object.getOwnPropertyDescriptor(user, 'name');
console.log(nameDesc);
// {
//   value: 'Jerry',
//   writable: false,
//   enumerable: false,
//   configurable: false
// }

如果属性不存在,会被创建:

Object.defineProperty(user, 'sex', {
    value: 'male'
});
console.log(user.sex);
let sexDesc = Object.getOwnPropertyDescriptor(user, 'sex');
console.log(sexDesc);
// male
// {
//   value: 'male',
//   writable: false,
//   enumerable: false,
//   configurable: false
// }

此时特殊配置项默认都是false。

writable

可以将属性设置为只读:

let user2 = {
    name: 'Tom',
}
​
Object.defineProperty(user2, 'name', {
    writable: false
})
​
user2.name = 'Jerry';
console.log(user2.name);
// Tom

这里并没有报错,只不过重新修改属性的代码没有生效。原因是 JS 只会在严格模式下在此情况下报错,否则不会报错,但重新指定属性内容的代码无效。

enumerable

有时候我们可能不希望某个对象属性被遍历,比如:

let user3 = {
    name: 'Tom',
    toString () {
        return `${this.name}`;
    }
}
​
for(let key in user3){
    console.log(key);
}
​
// name
// toString

可以通过属性配置实现这一点:

let user4 = {
    name: 'Tom',
    toString () {
        return `${this.name}`;
    }
}
​
Object.defineProperty(user4, 'toString', {
    enumerable: false
})
​
for(let key in user4){
    console.log(key);
}
​
// name

configurable

如果configurable被设置为false,属性就不能重复被Object.defineProperty修改配置,且无法被delete删除:

let user5 = {
    name: 'Tom',
}
​
Object.defineProperty(user5, 'name', {
    configurable: false,
    writable: false,
})
​
delete user5.name;
console.log(user5.name);
// Tom
​
Object.defineProperty(user5, 'name', {
    writable: true,
})
// TypeError: Cannot redefine property: name

可以利用这一点设置一些只读属性:

let MyMath = {
    pi: 3.14
}
​
Object.defineProperty(MyMath, 'pi', {
    writable: false,
    configurable: false,
})
​
MyMath.pi = 3.15;
console.log(MyMath.pi);
// 3.14

defineProperties

可以使用defineProperties一次定义多个属性的可配置项:

let user6 = { 
}
​
Object.defineProperties(user6,{
    name: {
        value: 'Tom',
        writable: false,
        enumerable: true,
        configurable: true,
    },
    age: {
        value: 18,
        writable: true,
        enumerable: true,
        configurable: true,
    }
})

getOwnPropertyDescriptors

用普通的方式复制属性时,只能复制值,不会复制属性的配置项:

let user7 = {
    name: 'Tom',
    age: 18,
    sex: 'male',
    toString () {
        return `${this.name} ${this.age} ${this.sex}`;
    }
};
Object.defineProperties(user7, {
    name: {
        writable: false,
        enumerable: true,
        configurable: true,
    },
    toString: {
        enumerable: false,
    }
});
​
let user8 = {};
for(let key in user7){
    user8[key] = user7[key];
}
let user8propertyDesc = Object.getOwnPropertyDescriptors(user8);
console.log(user8propertyDesc);
// {
//   name: {
//     value: 'Tom',
//     writable: true,
//     enumerable: true,
//     configurable: true
//   },
//   age: { value: 18, writable: true, enumerable: true, configurable: true },
//   sex: {
//     value: 'male',
//     writable: true,
//     enumerable: true,
//     configurable: true
//   }
// }

不仅仅是无法复制属性配置,如果属性被设置为enumerable:false,无法被遍历,值也不会被复制。

可以借助getOwnPropertyDescriptors实现属性复制,该方法会返回对象所有属性的配置项:

let user9 = {};
Object.defineProperties(user9, Object.getOwnPropertyDescriptors(user7));
let user9propertyDesc = Object.getOwnPropertyDescriptors(user9);
console.log(user9propertyDesc);

Getter/Setter

JS 中属性被分为普通的值属性以及访问器属性,利用访问器属性可以对现有代码进行重构,以“虚构”出一个新的属性,比如:

let name = {
    firstName: 'Tom',
    lastName: 'Jerry',
    get fullName () {
        return `${this.firstName} ${this.lastName}`;
    },
    set fullName (value) {
        let names = value.split(' ');
        this.firstName = names[0];
        this.lastName = names[1];
    }
}
console.log(name.fullName);
// Tom Jerry
name.fullName = 'Jeck Chen';
console.log(name.firstName);
console.log(name.lastName);
// Jeck
// Chen

这个示例中并没有一个fullName这样的值属性,而是通过一个 Getter 方法和一个 Setter 方法利用已有的两个值属性实现了一个新的访问器属性fullName,这个访问器属性可以像普通属性那样读写。

访问器属性描述符

访问器属性同样有属性描述符,可以配置的内容:

  • get,Getter 方法

  • set,Setter 方法

  • enumerable

  • configurable

通过属性描述符的方式添加访问器属性:

let name2 = {
    firstName: 'Tom',
    lastName: 'Jerry',
};
Object.defineProperty(name2, 'fullName', {
    get () {
        return `${this.firstName} ${this.lastName}`;
    },
    set (value) {
        let names = value.split(' ');
        this.firstName = names[0];
        this.lastName = names[1];
    }
});
console.log(name2.fullName);
// Tom Jerry

需要注意的是,一个属性不能即是值属性又是访问器属性:

Object.defineProperty(name2, 'attr', {
    get() {
        return this.firstName;
    },
    value: 'Tom',
});
// TypeError: Invalid property descriptor. Cannot both specify accessors and a value or writable attribute, #<Object>     

包装器

除了利用 Getter/Setter 添加新属性外,对已有属性进行重构,并为其添加新的特性也是常见用途:

let person = {
    name: undefined
};
person.name = 'Tom';
console.log(person.name);   

这里的name属性读写没有任何限制,如果我们希望写入name时进行检查,过短的姓名不应该被写入:

let person2 = { 
    _name: undefined,
    get name () {
        return this._name;
    },
    set name (value) {
        if(!value){
            throw new Error('Name cannot be empty');
        }
        if(value.trim().length <= 4){
            throw new Error('Name is too short');
        }
        this._name = value;
    } 
};
person2.name = 'Tom';
// Error: Name is too short

这里将原属性名前添加_,表示这是一个私有属性,不应该直接被访问,原属性名name用于定义访问器属性,读取该访问器属性的时候检查参数是否满足要求。

The End.

本文的完整示例可以从这里获取。

参考资料

  • 现代 JavaScript 教程

本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: 暂无
最后更新:2026年4月7日

魔芋红茶

加一点PHP,加一点Go,加一点Python......

点赞
< 上一篇

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复

COPYRIGHT © 2021 icexmoon.cn. ALL RIGHTS RESERVED.
本网站由提供CDN加速/云存储服务

Theme Kratos Made By Seaton Jiang

宁ICP备2021001508号

宁公网安备64040202000141号