JavaScript面向对象程序设计——属性

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面向对象精要》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

若即

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值