• 前端分享学习博客,探究前端相关技术,推动天朝前端发展,有任何问题都可以留言一起探究
  • 由于站内自链接问题,部分pre中的代码首字母使用大写以过滤筛选
  • 欢迎友链互换,还有,如果有大神请不要黑我的站点(o´・ェ・`o)
  • 如果你觉得博客还不错,请Ctrl+D收藏( *︾▽︾)

JavaScript笔记之modules

jQuery/JavaScript 薛 陈磊 531次浏览 0个评论 扫描二维码

前端开发者都知道,在深入的过程中(Javascript),我们总有"module bundlers vs. module loaders"、"Webpack vs. Browserify"和"AMD vs. CommonJS"这样的问题;在前端领域,模块化(modules)已经是不是什么新奇的编程/设计理念了,JavaScript中模块系统刚开始可能是有点唬人的(毕竟JavaScript中初始是没有类和命名空间的概念,在ES2015/ES6中引入了class关键字,但是只是语法糖,JavaScript 仍然是基于原型的),但理解它对于Web开发人员却非常重要;

问题:那到底什么是modules?

用一句话来描述modules的存在比较合适:Good authors divide their books into chapters and sections; good programmers divide their programs into modules;原始的JavaScript开发可能就是一些变量/function/event 监听的堆积集合,随着开发量级的急剧提升,各种问题就暴露了,显而易见这种粗放的集合不适应日益变化的JavaScript,以至于后面出现了种种设计模式,来解决这个JavaScript坑;说了那么多,其实也就是modules拥有可以规避以上问题/提高开发整体规格的优点特性:

  • 可维护性:根据定义,模块是独立的。一个精心设计的模块旨在尽可能减少对代码库部分的依赖,从而可以独立增长和改进。当模块与其他代码段分离时,更新单个模块会更容易;

  • 命名空间:在JavaScript中,顶级函数范围之外的变量是全局变量(意味着每个人都可以访问它)。因此,通常有“命名空间/全局变量污染”,不相关的变量/代码被全局共享造成一系列麻烦;

  • 可复用:我们可以把我们以前写过的代码复制到一个新页面或另一个新的项目中,这没问题,但是如果你找到一个更好的方法来编写代码的一部分,你必须找到所有你改过的地方,显然这是个非常不好的方式,而modules就是封装/引用的工作方式(就算不是modules工作方式,这也是必须…)避免这些问题;

Module pattern

模块模式用于模拟类的概念(因为JavaScript本身不支持类),因此我们可以将公共和私有方法和变量存储在单个对象内 – 类似于在其他编程语言中使用类,如Java或Python。这允许我们为我们想要向外部公开的方法创建一个面向公众的API,同时将封闭范围中的私有变量和方法封装起来;有几种方法来完成模块模式。在第一个例子中,使用一个匿名闭包(anonymous closure)。这将通过将所有代码放在匿名函数中来帮助我们实现我们的目标;

Example 1: Anonymous closure:

(function () {
  // We keep these variables private inside this closure scope
  
  var myGrades = [93, 95, 88, 0, 55, 91];
  
  var average = function() {
    var total = myGrades.reduce(function(accumulator, item) {
      return accumulator + item}, 0);
    
      return 'Your average grade is ' + total / myGrades.length + '.';
  }

  var failing = function(){
    var failingGrades = myGrades.filter(function(item) {
      return item < 70;});
      
    return 'You failed ' + failingGrades.length + ' times.';
  }

  console.log(failing());

}());

// ‘You failed 2 times.’

使用这个结构,我们的匿名函数有自己的运行环境或“闭包”,然后我们立即运行。这让我们可以从全局命名空间隐藏变量,这种方法有什么好处在于,可以在此函数中使用本地变量,而不会覆盖现有的全局变量,但仍然访问全局变量,如:

var global = 'Hello, I am a global variable :)';

(function () {
  // We keep these variables private inside this closure scope
  
  var myGrades = [93, 95, 88, 0, 55, 91];
  
  var average = function() {
    var total = myGrades.reduce(function(accumulator, item) {
      return accumulator + item}, 0);
    
    return 'Your average grade is ' + total / myGrades.length + '.';
  }

  var failing = function(){
    var failingGrades = myGrades.filter(function(item) {
      return item < 70;});
      
    return 'You failed ' + failingGrades.length + ' times.';
  }

  console.log(failing());
  console.log(global);
}());

// 'You failed 2 times.'
// 'Hello, I am a global variable :)'

需要注意的是,匿名函数周围的括号是必需的,因为以关键字函数开头的语句总是被认为是函数声明(记住,不能在JavaScript中使用未命名的函数声明)and why click here?

Example 2: Global import

另一种流行方法是全局导入(Global import),像jQuery这样的库就是这么做的。它类似于我们刚刚看到的匿名闭包,不同的是传入全局便令作为参数:

(function (globalVariable) {

  // Keep this variables private inside this closure scope
  var privateFunction = function() {
    console.log('Shhhh, this is private!');
  }

  // Expose the below methods via the globalVariable interface while
  // hiding the implementation of the method within the 
  // function() block

  globalVariable.each = function(collection, iterator) {
    if (Array.isArray(collection)) {
      for (var i = 0; i < collection.length; i++) {
        iterator(collection[i], i, collection);
      }
    } else {
      for (var key in collection) {
        iterator(collection[key], key, collection);
      }
    }
  };

  globalVariable.filter = function(collection, test) {
    var filtered = [];
    globalVariable.each(collection, function(item) {
      if (test(item)) {
        filtered.push(item);
      }
    });
    return filtered;
  };

  globalVariable.map = function(collection, iterator) {
    var mapped = [];
    globalUtils.each(collection, function(value, key, collection) {
      mapped.push(iterator(value));
    });
    return mapped;
  };

  globalVariable.reduce = function(collection, iterator, accumulator) {
    var startingValueMissing = accumulator === undefined;

    globalVariable.each(collection, function(item) {
      if(startingValueMissing) {
        accumulator = item;
        startingValueMissing = false;
      } else {
        accumulator = iterator(accumulator, item);
      }
    });

    return accumulator;

  };

 }(globalVariable));

Example 3: Object interface

另一种方法是使用独立的对象接口创建模块:

var myGradesCalculate = (function () {
    
  // Keep this variable private inside this closure scope
  var myGrades = [93, 95, 88, 0, 55, 91];

  // Expose these functions via an interface while hiding
  // the implementation of the module within the function() block

  return {
    average: function() {
      var total = myGrades.reduce(function(accumulator, item) {
        return accumulator + item;
        }, 0);
        
      return'Your average grade is ' + total / myGrades.length + '.';
    },

    failing: function() {
      var failingGrades = myGrades.filter(function(item) {
          return item < 70;
        });

      return 'You failed ' + failingGrades.length + ' times.';
    }
  }
})();

myGradesCalculate.failing(); // 'You failed 2 times.' 
myGradesCalculate.average(); // 'Your average grade is 70.33333333333333.'

如上,这种方法可以让我们声明想要保留私有的变量/方法(例如myGrades)以及我们想要将它们放在返回语句中的变量/方法(average & failing);

Example 4: Revealing module pattern

这与上述方法非常相似,除了它确保所有方法和变量保持私有,直到最后明确要暴露出去被使用:

var myGradesCalculate = (function () {
    
  // Keep this variable private inside this closure scope
  var myGrades = [93, 95, 88, 0, 55, 91];
  
  var average = function() {
    var total = myGrades.reduce(function(accumulator, item) {
      return accumulator + item;
      }, 0);
      
    return'Your average grade is ' + total / myGrades.length + '.';
  };

  var failing = function() {
    var failingGrades = myGrades.filter(function(item) {
        return item < 70;
      });

    return 'You failed ' + failingGrades.length + ' times.';
  };

  // Explicitly reveal public pointers to the private functions 
  // that we want to reveal publicly

  return {
    average: average,
    failing: failing
  }
})();

myGradesCalculate.failing(); // 'You failed 2 times.' 
myGradesCalculate.average(); // 'Your average grade is 70.33333333333333.'


还有一些写法掺入了兼容CMD/Common.js的写法(库中常用),后面写到这一块的时候再写吧,以上如有疏漏欢迎提示;

纸上读来终觉浅,撸码致知树生花。


思考,下面一种写法属于哪个方式:

( function( window, undefined ) {
  
  // normally variables & functions start with a lowercase letter but with modules, that is not the case.
  // The general tradition is to start them with a capital letter instead.
  function MyModule() {
  	
  	var ss="12345600";
  	
  	this.rePassword=function rePassword(){
  		var commonstring="123456";
  		return commonstring;
  	}
  	
    
    // `this` refers to the instance of `MyModule` when created
    this.myMethod = function myMethod() {
      alert( 'my method' );
    };
    
    // note that we still use a function declaration even when using a function expression.
    // for more information on why, check out: http://kangax.github.io/nfe/
    this.myOtherMethod = function myOtherMethod() {
      alert( 'my other method' );
    };
    
  }
  
  // expose access to the constructor
  window.MyModule = MyModule;
  
} )( window );

var m=new MyModule();
m.myMethod();


Reference:

Learning JavaScript Design Patterns

Adequately Good by Ben Cherry

The Module Pattern


薛陈磊的博客 , 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明JavaScript笔记之modules
喜欢 (0)
[905044086@qq.com]
分享 (0)
作者薛陈磊
关于作者:
非著名前端Coder,中二非文艺闷骚少年,喜欢动漫、历史、暗荣三国志和游山玩水,关注互联网发展,期待遇到更多小伙伴一起吹水玩耍;
说点什么...
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址