LittleC
技术学习记录
技术交流学习
Javascript中的单例模式和模块化

当脑子里有一个天才疯狂的想法时,重新造轮子通常不是的好方法。有人可能已经和你遇到了相同的问题,并以一种聪明的方式解决了它。这些好方法在规范之后被称为设计模式。 今天了解一下他们的概念,JS中的单例模式和模块。

什么是设计模式

设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。

学习一个公开仓库其中包含的设计模式可能比试图理解一个陌生的设计模式更容易。仓库里的代码还充当了其他开发人员和我们之间的桥梁,使交流更快更容易。

设计模式为我们提供了一种方案,我们可以根据自己的需求做调整。这些模式与特定问题没有关联,这使得它们非常可重用。它们与特定的编程语言无关,但是JavaScript具有比其他语言更流行的设计模式。

单例模式 ( Singleton )

我们从一个叫做单例的设计模式开始。它是最常见的模式之一。在其核心思想中,它将类限制为只有一个实例,并确保它是全局可访问的。当您需要管理整个应用程序中的某些内容时,它可能会派上用场。

数学与逻辑学中,singleton定义为“有且仅有一个元素的集合”。

根据设计, 如果还不存在实例,单例会创建一个新实例并返回。 否则,它会返回一个现有实例。

class Singleton {
  static instance;
  constructor() {
    // 你的东西
  }
  static getInstance() {
    if (Singleton.instance) {
      return Singleton.instance;
    }
    Singleton.instance = new Singleton();
    return Singleton.instance;
  }
}

现在,每次调用  Singleton.getInstance()时,都会获得同一个Object

Singleton.getInstance() === Singleton.getInstance(); // true

上面的代码虽然能用,但是有一些问题。 当直接调用constructor时,他就炸了。这是TypeScript派上用场的时候。

class Singleton {
  private static instance?: Singleton;
  private constructor() {
    // 你的东西
  }
  static getInstance() {
    if (Singleton.instance) {
      return Singleton.instance;
    }
    Singleton.instance = new Singleton();
    return Singleton.instance;
  }
}

将构造函数设为私有后,只能用getInstance函数获取实例

当然,也可以直接从构造函数中返回实例。

class Singleton {
  static instance;
  constructor() {
    if (Singleton.instance) {
      return Singleton.instance;
    }
    Singleton.instance = this;
 
    // 你的东西
  }
}
new Singleton() === new Singleton() // true

上面的代码更难读了一点,因为新手可能没学到constructor函数,所以有人可能读不懂构造函数每次都返回相同的对象。

单例与全局变量有很多相似之处,也有共同的缺点,它们可能会使您的应用程序可读性降低(当然你写一次性代码除外)

模块化

在Javascript应用程序中发现的另一种模式是模块化设计(webpack知道嘛) 将我们的JS代码分割成模块,显著提高代码可读性。

现在,一种流行的方法是将一段代码封装在立即调用函数表达式(IIFE)中。 页面中的JS脚本都共享相同的作用域。

index.html

<!DOCTYPE html>
<html>
    <head>
        <title>Hello world!</title>
        <script src="hello.js"></script>
        <script src="main.js"></script>
    </head>
    <body>
    </body>
</html>
// hello.js
function hello() {
  console.log('Hello world!');
}
hello(); // Hello world!

// main.js
hello(); // Hello world!

在一个JS中定义某些内容会污染整个全局范围,这不是一个理想的情况。解决这个问题的一个常见方法是通过创建一个函数并调用它来引入模块化功能,限制变量作用域。

// hello.js
(function(){
  function hello() {
    console.log('Hello world!');
  }
 
  hello(); // Hello world!
})();

// main.js
hello(); // Uncaught ReferenceError: hello is not defined

上述方法的一个关键点是,如果我们在上面的模块中定义了任何变量,那么它在模块之外是不可用的。

我们还可以通过从立即调用的函数表达式返回一些内容来导出hello函数。

// hello.js
const helloModule = (function(){
  function hello() {
    console.log('Hello world!');
  }
 
  return {
    hello
  }
})();

// main.js
helloModule.hello(); // Hello world!

随着JavaScript语言的发展,上述问题有许多解决方案,比如 ES6 模块, 其中每个模块都是一个文件。现代浏览器(IE菜)已经支持它们,也可以和webpack一起使用。 

Node.js环境还通过实现称为CommonJS的模块系统来提供其解决方案。 这是一段能正常执行的代码:

console.log('Hello');
return;
console.log('world!');

return语句不能出现在函数外部(除非你不按照规范瞎写return)。当我们导入这样的文件时,Node.js大致将其包装为以下函数:

function (exports, require, module, __filename, __dirname) {
  console.log('Hello');
  return;
  console.log('world!');
}

综上所述,该函数有自己的变量作用域,运行也没有出错。

拿Lxns大神的博客来说,上面的写法就非常好地保护了全局变量的整洁,不愧是大佬,太屌了
(其实我见过不少网站都用这种写法的,但是我想不起来有哪些)

技术交流学习

Javascript中的单例模式和模块化
当脑子里有一个天才疯狂的想法时,重新造轮子通常不是的好方法。有人可能已经和你遇到了相同的问题,并以一种聪明的方式解决了它。这些好方法在规范之后被称为设计模式。 今天了解一下他…
扫描二维码继续阅读
2020-01-31