JavaScript对象属性分为两种类型:数据属性和存取器属性。
数据属性包含一个数据值的位置,可以读取和写入。
const obj = {
name: "东巴文",
age: 1
};
// name和age都是数据属性
存取器属性不包含数据值,而是包含getter和setter函数。
const obj = {
firstName: "东",
lastName: "巴文",
get fullName() {
return `${this.firstName}${this.lastName}`;
},
set fullName(value) {
[this.firstName, this.lastName] = value.split("");
}
};
console.log(obj.fullName); // "东巴文"
obj.fullName = "JavaScript";
console.log(obj.firstName); // "J"
数据属性有四个特性(attribute)。
| 特性 | 说明 | 默认值 |
|---|---|---|
| [[Value]] | 属性的数据值 | undefined |
| [[Writable]] | 是否可写 | false |
| [[Enumerable]] | 是否可枚举 | false |
| [[Configurable]] | 是否可配置 | false |
const obj = { name: "东巴文" };
const descriptor = Object.getOwnPropertyDescriptor(obj, "name");
console.log(descriptor);
// {
// value: "东巴文",
// writable: true,
// enumerable: true,
// configurable: true
// }
const obj = {
name: "东巴文"
};
// 字面量创建的属性,特性默认都是true
Object.getOwnPropertyDescriptor(obj, "name");
// { value: "东巴文", writable: true, enumerable: true, configurable: true }
存取器属性有四个特性,但没有[[Value]]和[[Writable]]。
| 特性 | 说明 | 默认值 |
|---|---|---|
| [[Get]] | getter函数 | undefined |
| [[Set]] | setter函数 | undefined |
| [[Enumerable]] | 是否可枚举 | false |
| [[Configurable]] | 是否可配置 | false |
const obj = {
_age: 0,
get age() {
console.log("获取age");
return this._age;
},
set age(value) {
console.log("设置age");
if (value >= 0) {
this._age = value;
}
}
};
obj.age = 10; // 设置age
console.log(obj.age); // 获取age 10
const obj = {
_name: "东巴文",
get name() {
return this._name;
}
// 没有setter,只读
};
obj.name = "JavaScript"; // 无效(严格模式报错)
属性描述符是一个对象,描述属性的特性。
const obj = { name: "东巴文" };
// 获取单个属性描述符
Object.getOwnPropertyDescriptor(obj, "name");
// 获取所有属性描述符
Object.getOwnPropertyDescriptors(obj);
{
value: "东巴文",
writable: true,
enumerable: true,
configurable: true
}
{
get: function() { return this._name; },
set: function(value) { this._name = value; },
enumerable: true,
configurable: true
}
Object.defineProperty用于定义或修改属性。
const obj = {};
Object.defineProperty(obj, "name", {
value: "东巴文",
writable: true,
enumerable: true,
configurable: true
});
console.log(obj.name); // "东巴文"
const obj = {};
Object.defineProperty(obj, "name", {
value: "东巴文",
writable: false, // 不可写
enumerable: true,
configurable: false // 不可配置
});
obj.name = "JavaScript"; // 无效(严格模式报错)
console.log(obj.name); // "东巴文"
const obj = {};
let _age = 0;
Object.defineProperty(obj, "age", {
get() {
return _age;
},
set(value) {
if (value >= 0) {
_age = value;
}
},
enumerable: true,
configurable: true
});
obj.age = 10;
console.log(obj.age); // 10
const obj = {};
Object.defineProperty(obj, "PI", {
value: 3.14159,
writable: false,
enumerable: true,
configurable: false
});
obj.PI = 3.14; // 无效
console.log(obj.PI); // 3.14159
const obj = {
name: "东巴文"
};
Object.defineProperty(obj, "secret", {
value: "秘密",
enumerable: false
});
console.log(Object.keys(obj)); // ["name"]
console.log(obj.secret); // "秘密"(可以访问)
const obj = {};
Object.defineProperty(obj, "name", {
value: "东巴文",
configurable: false
});
// 无法删除
delete obj.name; // false
console.log(obj.name); // "东巴文"
// 无法重新配置
Object.defineProperty(obj, "name", {
writable: false
}); // TypeError
Object.defineProperties可以一次定义多个属性。
const obj = {};
Object.defineProperties(obj, {
name: {
value: "东巴文",
writable: true,
enumerable: true,
configurable: true
},
age: {
value: 1,
writable: true,
enumerable: true,
configurable: true
},
fullName: {
get() {
return `${this.name}(${this.age})`;
},
enumerable: true,
configurable: true
}
});
console.log(obj.fullName); // "东巴文(1)"
function createUser(name, age) {
const obj = {};
Object.defineProperties(obj, {
name: {
value: name,
writable: false,
enumerable: true,
configurable: false
},
age: {
value: age,
writable: true,
enumerable: true,
configurable: true
}
});
return obj;
}
const user = createUser("东巴文", 1);
getter和setter提供了对属性访问的控制。
const obj = {
_price: 0,
get price() {
console.log("获取价格");
return this._price;
},
set price(value) {
console.log("设置价格");
if (value >= 0) {
this._price = value;
} else {
console.log("价格不能为负数");
}
}
};
obj.price = 100; // 设置价格
console.log(obj.price); // 获取价格 100
class Circle {
constructor(radius) {
this._radius = radius;
}
get radius() {
return this._radius;
}
set radius(value) {
if (value > 0) {
this._radius = value;
}
}
get area() {
return Math.PI * this._radius ** 2;
}
get circumference() {
return 2 * Math.PI * this._radius;
}
}
const circle = new Circle(5);
console.log(circle.area); // 78.54...
console.log(circle.circumference); // 31.41...
const rectangle = {
width: 10,
height: 5,
get area() {
return this.width * this.height;
},
get perimeter() {
return 2 * (this.width + this.height);
}
};
console.log(rectangle.area); // 50
console.log(rectangle.perimeter); // 30
const user = {
_email: "",
get email() {
return this._email;
},
set email(value) {
if (value.includes("@")) {
this._email = value;
} else {
throw new Error("无效的邮箱格式");
}
}
};
user.email = "test@example.com"; // OK
user.email = "invalid"; // Error
const obj = {
_data: null,
_loaded: false,
get data() {
if (!this._loaded) {
console.log("加载数据...");
this._data = this.loadData();
this._loaded = true;
}
return this._data;
},
loadData() {
return [1, 2, 3, 4, 5];
}
};
// 第一次访问时才加载
console.log(obj.data); // 加载数据... [1, 2, 3, 4, 5]
console.log(obj.data); // [1, 2, 3, 4, 5](已缓存)
const obj = {
name: "东巴文",
age: 1,
[Symbol("id")]: 123
};
Object.defineProperty(obj, "secret", {
value: "秘密",
enumerable: false
});
// 可枚举的字符串属性
Object.keys(obj); // ["name", "age"]
// 所有字符串属性(包括不可枚举)
Object.getOwnPropertyNames(obj); // ["name", "age", "secret"]
// 所有Symbol属性
Object.getOwnPropertySymbols(obj); // [Symbol(id)]
// 所有属性(字符串+Symbol)
Reflect.ownKeys(obj); // ["name", "age", "secret", Symbol(id)]
const obj = {
name: "东巴文",
age: 1
};
// 获取所有值
Object.values(obj); // ["东巴文", 1]
// 获取键值对
Object.entries(obj); // [["name", "东巴文"], ["age", 1]]
const obj = {
name: "东巴文",
age: undefined
};
// in操作符(检查原型链)
"name" in obj; // true
"toString" in obj; // true(继承)
// hasOwnProperty(只检查自身)
obj.hasOwnProperty("name"); // true
obj.hasOwnProperty("toString"); // false
// Object.hasOwn(推荐)
Object.hasOwn(obj, "name"); // true
Object.hasOwn(obj, "toString"); // false
// 属性值检查
obj.name !== undefined; // true
obj.age !== undefined; // false(注意:age存在但值为undefined)
const obj = {
name: "东巴文",
age: 1
};
delete obj.age;
console.log(obj); // { name: "东巴文" }
// 删除不可配置属性返回false
Object.defineProperty(obj, "id", {
value: 1,
configurable: false
});
delete obj.id; // false
ES6规定了属性枚举的顺序。
const obj = {
[Symbol("first")]: "symbol",
2: "two",
1: "one",
"b": "letter b",
"a": "letter a",
[Symbol("second")]: "symbol2"
};
// 顺序:
// 1. 数字键,按数值升序
// 2. 字符串键,按创建顺序
// 3. Symbol键,按创建顺序
console.log(Object.keys(obj));
// ["1", "2", "b", "a"]
console.log(Object.getOwnPropertySymbols(obj));
// [Symbol(first), Symbol(second)]
console.log(Reflect.ownKeys(obj));
// ["1", "2", "b", "a", Symbol(first), Symbol(second)]
掌握了对象属性后,让我们继续学习:
东巴文(db-w.cn) - 让编程学习更简单
🔑 东巴文寄语:深入理解对象属性是掌握JavaScript对象的关键,理解数据属性和存取器属性的区别,掌握Object.defineProperty的使用,能让你更好地控制对象行为。在 db-w.cn,我们帮你深入每一个细节!