JavaScript面向对象程序设计——属性
前言
在前两篇文章中详细介绍了在JavaScript语言下进行面向对象编程的 构建对象 和 继承 的实现步骤。这篇文章将会详细介绍JavaScript面向对象设计中属性定义相关的问题。这篇文章也是《JavaScript面向对象精要》的阅读总结
属性的类型
在JavaScript中属性主要分为数据属性和访问器属性,数据属性就是最常用的,如
var person1 = {
_name: "Jerry"
}
中 _name
就是数据属性。所谓的访问器属性也就是类似于C#中的getter/setter,如
var person1 = {
_name: "Jerry",
get name() {
console.log(this._name)
},
set name(value) {
this._name = value
}
}
中 name
就是person1的访问器属性
数据属性跟访问器属性拥有不同的属性特征,也有共同的通用特征
- 通用特征有
[[Configurable]]
和[[Enumerable]]
- 数据属性特征有
[[Value]]
和[[Writable]]
- 访问器属性特征有
[[Get]]
和[[Set]]
属性的特征
之前我们一般使用构造函数和字面量的方法来定义对象的属性,也有一种不太好的实践是定义对象之后再添加属性,在这里用最常用的数据属性作为例子
//字面量方式定义属性
var person1 = {
_name: "Jerry"
}
//构造函数方式定义属性
var Person = function(name) {
this._name = name
}
var person2 = new Person("Tony")
//定义对象之后添加属性
var person3 = new Object()
person3._name = "Jim"
通过上面方法定义的数据属性都是可枚举(enumerable),可配置(configurable)和可写(writable)的。
可枚举是通用特征,指可以通过Object.keys()
和 in
运算符获取该属性
var person1 = {
_name: "Jerry"
}
var properties = Object.keys(person1)
console.log(properties) // ["_name"]
for(var p in person1){
console.log(p)
}//_name
可配置也是通用特征,指可以重新定义该属性(指可以从数据属性重新定义为访问器属性,或者相反)以及删除该属性
var person1 = {
_name: "Jerry"
}
console.log("_name" in person1) //true
delete person1._name
console.log("_name" in person1) // false
可写是数据属性特征,指属性的值可以改变
var person1 = {
_name: "Jerry"
}
person1._name = "Tommy"
console.log(person1._name) //Tommy
接着是访问器属性的例子
var person1 = {
_name: "Jerry",
get name() {
return this._name
},
set name(value) {
this._name = value
}
}
console.log(person1.name) //Jerry
person1.name = "Tony"
console.log(person1.name) //Tony
上面例子中定义了访问器属性的get和set特征,get和set特征并不强制要求一起定义,当只定义get特征而不定义set特征的是后,当试图写入数据的时候会失败(严格模式下会抛出错误)
// 之定义get而不定义set
var person1 = {
_name: "Jerry",
get name() {
return this._name
},
}
console.log(person1.name) //Jerry
person1.name = "Tony"
console.log(person1.name) //Jerry
当只定义set特征而不定义get特征时,当视图获取数据的时候总是返回 undefined
// 之定义set而不定义get
var person1 = {
_name: "Jerry",
set name(value) {
this._name = value
},
}
console.log(person1.name) //undefined
person1.name = "Tony"
console.log(person1.name) //undefined
定义属性
在前面就介绍了定义属性的方法,但使用前面的方法定义属性的时候并不能对属性的特征进行有效的控制,接下来的定义属性方法将会在定义属性的同时进行特征的定义,主要使用的方法是 Object.defineProperty()
和 Object.defineProperties()
,在使用这两个方法的时候需要注意的是特征在定义的时候没指定的话默认为 false
定义数据属性
直接看代码
var person1 = {}
Object.defineProperty(person1, "_name", {
value: "Jerry",
configurable: true,
enumerable: true,
writable: true
})
console.log(person1._name) //Jerry
person1._name = "Tony"
console.log(person1._name) //Tony
定义访问器属性
var person1 = {
_name: "Jery"
}
Object.defineProperty(person1, "name", {
get: function() {
return this._name
},
set: function(value) {
this._name = value
},
configurable: true,
enumerable: true,
})
console.log(person1.name) //Jerry
person1.name = "Tony"
console.log(person1.name) //Tony
定义多重属性
在实际的编码中如果一个一个的属性进行定义肯定会降低效率,所以在需要定义多个属性的时候可以使用多重属性进行定义
var person1 = {}
Object.defineProperties(person1, {
_name: {
value: "Jerry",
enumerable: true,
configurable: true,
writable: true
},
name:{
get: function() {
return this._name
},
set: function(value) {
this._name = value
},
enumarable: true,
configurable: true
}
})
console.log(person1.name) //Jerry
person1.name = "Tony"
console.log(person1.name) //Tony
在构造函数中定义属性
在构造函数中使用 Object.defineProperty()
定义属性的优点是能够正确的控制属性的特征,在初始化的时候就确定了该属性是否可枚举,是否能够配置,在之后的编码过程中能够减少误删除属性的操作,也可以维护属性枚举的纯净性——在大多数时候我们只希望数据能够被枚举而方法不被枚举。
var Person = function(param) {
Object.defineProperties(this, {
_name: {
value: param,
enumerable: true,
configurable: false,
writable: true
},
name:{
get: function() {
return this._name
},
set: function(value) {
this._name = value
},
enumarable: true,
configurable: false
}
})
}
var person1 = new Person("Jerry")
console.log(person1.name) //Jerry
person1.name = "Tony"
console.log(person1.name) //Tony
私有属性的实现
JavaScript中并没有完备的访问权限体系,就算ES6也没有原生支持 public
, private
或类似的权限控制语法。但控制访问权限在实际编码中又是迫切需要的,例如存储个人信息的对象,开发者肯定不希望用户在Console中轻易使用 user.password
来获取到用户的密码。
模块模式创建私有属性
模块模式使用的是立即执行函数(IIFE)来返回对象,类似于工厂模式创建对象。主要实现方法如下
var person1 = (function(){
var age = 25
return {
name: "Jerry",
getAge: function() {
return age
}
}
}())
console.log(person1.age) //undefined
console.log(person1.getAge()) //25
构造函数中的私有属性
整体的思路跟模块模式差不多,私有属性不添加到 this
中,而在 this
中添加与私有属性相关的操作。
var Person = function(){
var age = 25
this.getAge = function() {
return age
}
this.name = "Jerry"
}
var person1 = new Person()
console.log(person1.age) //undefined
console.log(person1.getAge()) //25
总结
JavaScript面向对象程序设计这系列的文章都是以ES5为主,在ES6逐渐成为主流的情况下,class
关键字已经成为标准,再死磕ES5中各种面向对象的实践似乎已经没有了意义,但是在了解前辈的实践的时候,能够对这门语言有一个更新的认知何尝不是一件好事呢。
参考资料《JavaScript面向对象精要》