Typescript之单例模式与类的静态属性,方法应用(学习笔记)

1,219 阅读5分钟

单例模式

单例模式的定义为:如果某个类对外始终只提供一个对象【实例】,并且在该类的内部提供了一个外部访问该对象的方法或该对象属性,就被称为单例模式

在typescript中实现单例模式非常容易,如下:

懒汉式单例

class Notice {
  // 1.设置静态私有属性,存储唯一对象实例
  private static noticeInstance: Notice
  creator:string = '佚名'
  // 2.私有化构造器
  private constructor(creator?:string) {
    this.creator = creator
    console.log('创建实例')
  }
  
  // 3.设置静态方法,只能从该方法中获取对象实例 
  public static getInstance () {
     // 这里是重点,判断是否已经初始化过实例
    if(!this.noticeInstance) {
      this.noticeInstance = new Notice()
    }
      return this.noticeInstance
  }
  
  public warm(content) {
    console.log('警告:'content)
  }
}

let insOne = Notice.getInstance()
let insTwo = Notice.getInstance()
console.log(insOne == insTwo) // true

编译成ES5版本后是这样的

var Notice = /** @class */ (function () {
    function Notice(creator) {
        this.creator = '佚名';
        this.creator = creator;
    }
    Notice.getInstance = function () {
        if (!this.noticeInstance) {
            this.noticeInstance = new Notice();
        }
        return this.noticeInstance;
    };
    Notice.prototype.warm = function () {
        console.log('警告');
    };
    return Notice;
}());
var insOne = Notice.getInstance();
var insTwo = Notice.getInstance();
insOne.warm();
console.log(insOne == insTwo); // true

饿汉式单例

class Notice {
  // 1.设置静态属性,存储唯一对象实例,并直接实例化对象
  public static noticeInstance: Notice = new Notice()
  creator:string = '佚名'
  // 2.私有化构造器
  private constructor(creator?:string) {
    this.creator = creator
    console.log('创建实例')
  }
  
  public warm(content) {
    console.log('警告:'content)
  }
}

// 3直接通过Notice.noticeInstance拿到实例
let insOne = Notice.noticeInstance

编译成ES5版本后是这样的

var Notice = /** @class */ (function () {
    function Notice(creator) {
        this.creator = '佚名';
        this.creator = creator;
    }
    Notice.prototype.warm = function () {
        console.log('警告');
    };
    Notice.noticeInstance = new Notice();
    return Notice;
}());
var insOne = Notice.noticeInstance;

当外部访问某个类的实例时,确保只能访问该类的唯一对象时才能保证逻辑的正确性时,就应该用单例模式。例如vuex,全局弹框,库的实例(jquery的$),等等。

类的静态属性与方法的应用

什么时候应该使用静态方法和静态模式呢?

  1. 上面的单例模式就是静态属性和方法的一种很好的应用。

  2. 当类中某个方法没有任何必要使用任何实例属性时,而且使用了实例属性反而让这个方法的逻辑不正确,那既如此,就应该禁止这个方法访问任何实例属性和其他的实例方法,这时就应该把这个方法定义为静态方法。(我们都知道静态方法其实就是定义在函数对象上的方法,实例方法通常是定义在函数的prototype对象上的方法,而前者无法访问实例上的属性,后者可以访问,应此当一个方法无需借助实例属性时,就可以使用静态方法)。

    例如:一个顾客类的购买方法【 buy 方法】中肯定要允许访问顾客姓名或其他顾客微信这些实例属性,这样的方法我们就需要定义在原型对象属性上,但如果顾客类中的 阅读顾客积分公告方法【 readNotice 方法] 是针对全体顾客的公告方法,就应该定义为静态方法,方法内部就应该禁止出现任何具体的实例属性。如果在这样的方法中使用了顾客的某个属性,比如用了顾客姓名,那么这个方法逻辑就不正确

  3. 当一个类中某个方法只有一个或者 1-2个 对象属性,而且更重要的是,你创建这个类的对象毫无意义,我们只需要使用这个类的一个或者多方法就可以了,那么这个方法就应该定义为静态方法。常见的工具类 中的方法通常都应该定义为静态方法。比如 StringUtil, FileUtil 等,

    补充:什么叫毫无意义呢,比如 fileUtils,用它创建实例,再调用方法和直接使用静态方法看起来都差不多,但是前者没有语义性,创建实例是可以创建多个实例的,但在这里,创建多个实例没有任何意义,哪怕创建10个实例,最终也只是为了使用那几个方法而已,完全没有必要创建多个实例,只会浪费内存,反之,如果我们创建的是学生实例,顾客实例,则是有很强的语义性,用一个学生对象描述一个实体,充分发挥面向对象的优势

// 使用静态方法
class FileUtil{
   // 从指定文件上把数据读出来打印在控制台或页面上的静态方法
   public static readFile(readonly fileName:string){  
      fs.readFile(fileName, (err: any, data: any) => {
          console.log("fs.readFile:", data.toString());
      }
   }
  // 把键盘输入的数据或页面上获取的数据写入到指定文件上的静态方法
   public static writeFile(fileName:string){
       fs.writeFile(fileName, '刘老根4', function (error) {
          if (error) {
            console.log('写文件失败')
          } else {
            console.log('写文件成功')
          }
	})
 
 }
 // 实际应用中,读和写一般都不在一个时间段,可能读功能完成后,过了几分钟,用户才在客户端执行写的方法,
 // 又过了一会,用户又在客户端执行了读的方法。 但我们知道静态方法实际上是一直保存到内存空间,这样反复操作其实节省了大量反复创建 和释放 FileUtil 对象的时间和对应的对象内存空间。
 FileUtil.readFile('./log.txt')
 FileUtil.writeFile('./log5.txt')
// 使用类(定义在原型上的)方法
class FileUtil{
   constructor(public fileName:string){}
  
   // 从指定文件上把数据读出来打印在控制台或页面上的静态方法
   public  readFile(){  
      fs.readFile(fileName, (err: any, data: any) => {
          console.log("fs.readFile:", data.toString());
      }
   }
   
    // 把键盘输入的数据或页面上获取的数据写入到指定文件上的静态方法
    public writeFile(fileName:string){
       fs.writeFile(fileName, '刘老根4', function (error) {
          if (error) {
            console.log('写文件失败')
          } else {
            console.log('写文件成功')
          }
	})
 }
 // 实际应用中,读和写一般都不在一个时间段,可能读功能完成后,过了几分钟,用户才在客户端执行写的方法,
 // 又过了一会,用户又在客户端执行了读的方法。所以每次都要创建 FileUtil 对象,这样反复创建 和释放  FileUtil 对象,就浪费了大量反复创建 和释放 FileUtil 对象的时间和对应的对象内存空间
 new FileUtil('./log.txt').readFile()
 new FileUtil('./log5.txt').writeFile()
 // 像这种哪怕是只创建一个对象都没有必要