D19 - 今晚我想来点 唯独派 getter 唯写派 setter

前言

JavaScript 内的物件都有内建的两个属性,可以实现对物件的存取,称为:

  • getter 取值器
  • setter 设值器

他们是什麽

不同於一般物件内的属性,由 getter 和 setter 定义的属性有个专有名词称为 存取器属性 accessor property

写在 getter 或 setter 的属性皆为方法,但在查找时,不需加上() 呼叫,背後会自动进行函式呼叫。

两者的差异?

getter 取值器,主要目的是读取值,不能进行赋值修改
setter 设值器,可以设定属性的值,透过赋值的方式传入参数

属性依照是否设置 setter 与 getter 分为三种特性:

  • 可读写特性:同时设置 getter、setter
  • 唯独特性 read-only:只设置 getter
  • 唯写特性 write-only:只设置 setter

如何设定取值器和设值器

在 JS 中有三种方法:

  1. 物件实值 Object literal
  2. class 类别定义
  3. Object.defineProperty

1. 物件实质 Object literal 设定

//物件实质中的写法
let obj = {
        get name(){
    ....
    },
        set name(value){
        ...
    }
}

setter、getter 可用来定义可计算属性 computed property,其值是每次存取时计算出来的,像是物件中若有属性 firstName 与 lastName,可以设置一个存取器属性 fullName ,透过 setter 设定将 firstName 与 lastName 属性相加印出。

❓ 我有疑问
其实可以在物件内建立一个方法就可以,为什麽要设置为 setter 呢?
根据忍者 2 的讲解,若某个值只依赖自物件的内部状态(如:firstName、lastName),比起以方法形式呈现,以属性的方式做读取其实更合理!

实际操作如下:

let aurther = {
    firstName : 'Sherry',
    lastName : 'Ho',
    get fullName(){
    consle.log(this.firstName + this.lastName)
    },
    set fullName(name){
        let _name_ = name.split(' ')
        this.firstName =  _name_[0]
        this.lastName = _name_[1]
    }
}

aurther.fullName   // SherryHo,呼叫 getter 取值
aurther.fullName = 'Sam Smith' // 重新指派值会呼叫 setter 将新值当参数传入
aurther.firstName  // Sam,变为 setter 新赋的值
aurther.fullName  // Sam Smith

在 DevTools 印出可以看到属於存取器属性前会加上 setter 与 getter 字样。

2. Class 中设定 setter、getter

class 内加上 setget 关键字,就可以定义存取器属性。

// class 内写法
class Name{
    construcor(){}
    set name(){}
    get name(value){}
}

举例:
定义一个 class 名称为 Cake,设置一个可读写的属性 producer,这个存取器属性会存入 prototype 物件内,以 Cake 创建的实例可以透过原型链取得。

class Cake {
    constructor(flavor){
        this.flavor = flavor;
        this._producer = 'Hoo'
    }
    set producer(name){
        this._producer = name;
    }
    get producer(){
        return this._producer;
    }
}

let cake1 = new Cake('Tiramisu')
cake1.producer  // Hoo,实例可透过原型链取得 producer 属性

在 DevTools 中印出可以看到,存取器属性 producer 存在於 prototype 内

3. Object.defineProperty

透过 Object.defineProperty 可以定义或修改物件中的属性。

Object.defineProperty(目标物件,'特性名称', {setter, getter 描述})
let cake = {};
Object.defineProperty(cake, 'producer', {
    get: function (){
        return this.producer
    },
    set: function (name){
        this.producer = name;
    }
})

可以透过 Object.defineProperty 来控制存取私有变数,与物件实质和 class 不同的是,透过 Object.defineProperty 会和私有变数建立在相同的范围中!

function Cake() {
    let _cakeRating = 0;
Object.defineProperty(this, 'cakeRating', {
    get: ()=>{
        return _cakeRating;
    },
    set: (value)=>{
        _cakeRating = value;
    }
})
}

let cake1 = new Cake();
cake1.cakeRating = 10; // 透过 getter 指派值给 _cakeRating 变数
console.log( cake1.cakeRating ) // 10,只能透过 getter 取到 _cakeRating 这个私有变数

严格模式禁止修改 getter

若对 getter 属性重新赋值,在一般模式下不会产生效果,引擎会自动忽略这项动作,但在严格模式下禁止这项动作,会抛出错误提醒不能对唯读属性修改。

'use strict'

class Cake {
    constructor(flavor){
        this.flavor = flavor;
        this._productionDate = 'Oct.3, 2021'
    }
    set productionDate(date){
        this._productionDate = date;
    }
    get producer(){
        return 'this.Hoo';
    
    }}

let cake1 = new Cake('cream')
a.producer = 'sherry' // error! Cannot set property producer of #<Cake> which has only a getter

Reference

008重新认识 JavaScipt
忍者 JavaScript 开发技巧探秘第二版 by John Resig, Bear Bibeault, Josip Maras
MDN


<<:  JavaScript Call, Bind, Apply

>>:  【day19】聊天室(下) X Realtime database

ASP.NET C# - GridView -ButtonFiled & CommandName

环境 VS2013 ASP.NET 4.0 GridView怎麽做删除钮与编辑钮. 要做之前,我们要...

饭店的奇闻轶事

今天心情郁闷只好来写一些特别的东西,来跟大家聊聊空服的外站人生。 还记得那是一个很冷很冷的冬天,历经...

Day27 切版笔记 - 破格式设计

今天来练习切这个版面 运用到的观念 flexbox box model 区块大小的计算 margin...

Day1 写程序的前置工作!

安装环境第一步骤 App Store 搜寻Xcode并下载 下载後并创立一个专案 进入後系统帮我们预...