本文来自这里,翻译过程中加入了译者的理解并作了适当的筛减。
在过去的几年中,JavaScript应用程序在规模和复杂性方面都越来越大。市场上出现了越来越多的单页面应用,并且对这种体验的需求已经达到了促使Google最终决定在爬行页面的过程中考虑支持JavaScript。
对单页面应用的需求,也促使JavaScript架构变得越来越重要。而对于作为动态语言的JavaScript来说,为了保证代码的可维护性、避免代码混乱,你需要做很多额外的工作。
过去很长一段时间中,在一个单一的文件中使用jQuery选择器以及事件处理并没有什么不好。然而,这种方式并不是一种可持续的模式。在23世纪中面临的现代Web,我们需要更多的思考,并且需要通过架构的方式来解决这样的问题。
架构模式
架构模式不是坐下来就能写出来的。不可能坐在那里就能想出如何写出一个很好的模式。模式是在解决问题的过程中发现的。许多问题可以使用同样的方式解决,而模式是从这些解决方案中抽取出来的。
构造器
首先,让我们先看看已存在多年的JavaScript内置的功能——构造器模式
function Starship() {}
在JavaScript声明的所有函数都可以用作构造函数来创建一个实例。这样,就可以创建可重用的函数了。
var enterprise = new Starship(), IKSBuruk = new Starship();
可以修改上面这个函数,给函数传一些参数从而实现更好地描述实例。可以将参数赋值到this的属性上,this将指向由这个构造函数创建的实例。
function Starship(owner, operator, type) { this.owner = owner; this.operator = operator; this.type = type; } var enterprise = new Starship('Federation', 'Star Fleet', 'Class 1 Heavy Cruiser'), birdOfprey = new Starship('Klingon Empire', 'Klingon Imperial Fleet', 'Klingon Warship');
除此之外,还可以使用JavaScript构造函数的一个内置特性——prototype。简单地说,prototype是一个对象。在这个对象上,可以定义一些属性和方法,而这些属性和方法会添加到由该构造函数创建出来的每个实例上。
function Starship(owner, operator, type, weapons) { /* ... */ this.weapons = weapons; } Starship.prototype.fire = function(weapon) { this.weapons[weapon].launch(); }; function PhotonTorpedoSystem() {} PhotonTorpedoSystem.prototype.launch = function() { console.log('launching torpedos'); }; var torpedos = new PhotonTorpedoSystem(), var weaponSystem = { torpedos: torpedos; }; var enterprise = new Starship( 'Federation', 'Star Fleet', 'Class 1 Heavy Cruiser', weaponSystem ); enterprise.fire('torpedos') // launching torpedos;
JavaScript的prototype体系真正重要的是它可以帮助你创建一个继承体系。JavaScript中的继承与其他语言有些许的不同,但通过实践其也不难理解。
function ConstitutionClass(captain, firstOfficer, missionDuration) { this.captain = captain; this.firstOfficer = firstOfficer; this.missionDuration = missionDuration; Starship.apply(this, ['Federation', 'Star Fleet', 'Class 1 Heavy Cruiser', weaponSystem]); } ConstitutionClass.prototype = Object.create(Starship.prototype); ConstitutionClass.prototype.constructor = ConstitutionClass; ConstitutionClass.prototype.warp = function(speed) { console.log('warping at: ' + speed); }; var enterprise = new ConstitutionClass('Kirk', 'Spock', 5); enterprise.fire('torpedos'); // Launching Torpedos enterprise.warp(14.1); // warping at: 14.1
这里,我们创建了ConstitutionClass构造函数。该构造函数接收三个参数captain,firstOfficer和missionDuration。在函数中调用了Starship.apply方法并且传了一个数组参数。这确保了父级构造函数将被调用。
JavaScript中每个函数都有一个call和apply方法,这两个方法允许你通过传入一个上下文来修改函数执行时的上下文,而使用apply方法的话,还需要同时传入一个数组,这个数组包含函数执行时所需的参数。
接下来,为了正确地让ConstitutionClass继承自Starship,我们简单地使用了object.create来赋值给ConstitutionClass的prototype。
这里所做的是告诉ConstitutionClass,将Starship的prototype中所有的属性和方法加到ConstitutionClass的prototype上。
这就是原型链继承。
IIFE避免全局泄漏
需要注意的一点是,上面的代码中存在几个全局泄漏的问题。当你在一个JavaScript文件中简单地声明函数或变量时,这些函数或变量会被添加到全局命名空间上(在浏览器中指的是window)。
下面的代码会创建好几个全局变量:
function PhotonTorpedoSystem() {} PhotonTorpedoSystem.prototype.launch = function() { console.log('launching torpedos'); }; var torpedos = new PhotonTorpedoSystem(), var weaponSystem = { torpedos: torpedos; };
执行这段代码后,会创建window.PhotonTorpedoSystem,window.torpedos,以及window.weaponSystem。这是不好的情形。
为了避免这个问题,一个重要的模式是使用立即执行函数表达式(IIFE,Immediately Invoked Function Expression):
(function(global) { function PhotonTorpedoSystem() {} PhotonTorpedoSystem.prototype.launch = function() { console.log('launching torpedos'); }; var torpedos = new PhotonTorpedoSystem(), var weaponSystem = { torpedos: torpedos; }; /* Create instance of enterprise etc */ global.PhotonTorpedoSystem = PhotonTorpedoSystem; // Export just this to the window }(window));
这里可以看到前面的(function(global) {和后面的}(window));。这就创建了一个匿名的函数并且该函数将被立即执行,同时将global的值设为window。这些都是有效的,因为IIFE创建了一个闭包。在IIFE中定义的一切只包含在那个函数中,除非它被输出到我们定义的global上。
现在,可以显性地在window上设置内容以便在其他的JavaScript文件中也可以使用。
这种方式已经在模式的方向上迈出了正确的一步,但另外一个更好的模式是使用命名空间的模式:
// weaponSystems.js (function(global, NS) { NS.Weapons = NS.Weapons || {}; function PhotonTorpedoSystem() {} PhotonTorpedoSystem.prototype.launch = function() { console.log('launching torpedos'); }; /* ... */ NS.Weapons.PhotonTorpedoSystem = PhotonTorpedoSystem; // Export just this to the NS.Weapons namespace }(window, window.NS = NS || {});
注意到这里我们将前面IIFE的最后一行的代码改成了}(window, window.NS = NS || {});。这样就会给函数传入window和NS对象。这里稍微有点怪的写法是说,“如果NS存在则直接传进入,如果NS还不存在则为它创建一个新的对象”。
你也可以这样使用这种语法,如NS.Weapons = NS.Weapons || {};
它跟下面的if语句是一样的,只不过这种写法更加简洁:
if (!NS.Weapons) { NS.Weapons = {}; }
你可以向命名空间上添加任何对象和函数。这种方式使你避免了添加太多的全局变量到window上。
(function(global, NS) { NS.Ships = NS.Ships || {}; function Starship(owner, operator, type, weapons) { /* ... */ } function ConstitutionClass(captain, firstOfficer, missionDuration) { /* ... */ } /* ... */ NS.Ships.Starship = Starship; NS.Ships.ConstitutionClass = ConstitutionClass; }(window, window.NS = NS || {});
相关推荐
Lambda演算是数学家Alonzo Church在20世纪30年代提出的一种抽象计算理论,它是函数编程的基础,并对现代计算机科学产生了深远影响。Lambda演算的核心概念是λ(lambda)表达式,它允许我们定义匿名函数并进行函数的...
JavaScript的历史始于20世纪90年代,随着互联网的发展,静态网页已无法满足日益增长的用户交互需求。1996年,网景公司(Netscape)推出了Mocha语言,后更名为LiveScript,并最终采用JavaScript作为正式名称。这一...
1. **历史背景**:Flash 在20世纪末至21世纪初广泛应用于网页动画、在线游戏和视频播放,但因其封闭性、性能问题及对移动设备支持不足,逐渐被 HTML5 技术取代。 2. **ActionScript**:Flash 的编程语言,用于创建...
JavaScript的诞生源于20世纪90年代,网景公司(Netscape)为了解决网页静态性的局限,开发了Mocha,后来演变为LiveScript,最终因市场策略更名为JavaScript,以借用Java的知名度。尽管名字相似,但JavaScript与Java...
JavaScript的历史可以追溯到20世纪90年代中期。当时,由于大多数互联网用户通过低速调制解调器连接到网络,因此Web页面的功能非常有限。为了提高用户体验,开发人员开始寻找方法来简化用户与Web页面之间的交互过程。...
JavaScript语言的历史可以追溯到20世纪90年代中期,当时浏览器主要用于浏览网页,而JavaScript的前身是Netscape公司和Sun公司合作开发的。JavaScript的第一个版本被整合到Netscape Navigator 2.0中,它的出现极大...
`getYear()`返回年份(不包含世纪数),所以要加上1得到实际的年份。需要注意的是,月份是从0开始的,因此1月1日需要用`new Date(year, 0, 1)`来表示。 最后一个作业是对Math对象的一些基本数学函数进行测试。`Math...
此外,"共享世纪.url"可能是一个链接,指向更多相关的学习资源或者开发者社区,供进一步探索和交流JavaScript特效的实现和优化。 学习这个CHM文件不仅能够提升JavaScript编程技能,还能让你了解如何将这些特效应用...
世纪高等学校计算机规划教材JavaScript正则表达式PPT学习教案.pptx
【上古世纪官网jQuery焦点图代码】是一种前端网页设计中的特效技术,用于在网页上创建动态的图片轮播效果。这种焦点图代码基于JavaScript库jQuery,适用于包括IE 7、8、9在内的多种主流浏览器,提供了平滑的图片切换...
例如,遍历月份和日期,检查是否为闰年(每四年有一次,但世纪年必须能被400整除),以及确定每个月的第一天和最后一天。 6. **样式与布局**: 虽然JavaScript主要负责逻辑,但也会与CSS配合,通过修改元素的样式...
JavaScript最初由网景公司(Netscape)开发于二十世纪九十年代,最初被设计成一种装饰性语言,用于增加网页的可交互性。随着互联网技术的发展,尤其是Web2.0时代和AJAX技术的兴起,JavaScript迅速成为主流的Web前端...
数独是一种逻辑推理游戏,起源于18世纪的瑞士,流行于20世纪末的日本,并在全球范围内广受欢迎。游戏的目标是在9x9的格子中填入数字,使得每一行、每一列以及每个3x3的小宫格内的数字都从1到9不重复。这个JavaScript...
效果展示: ... 现在,虽然仍在game文件夹中,但在同一位置打开另一个终端会话。键入并运行npm run dev:node.... 如果要调试,请键入npm run ndb....强烈推荐一个更轻松的时间。...更多详情、使用方法,请下载后阅读README.md...
甘特图起源于20世纪初,由亨利·甘特发明,它通过条形图来表示项目任务的开始和结束日期,以及它们之间的依赖关系。在现代,尤其是在IT领域,甘特图被用于项目管理和软件开发中,帮助团队成员清晰地了解任务的状态和...
格里高利历,是目前国际上通用的日历,也是我们通常所说的公历,它的闰年规则更为复杂,每四年一闰,但是世纪年如果不是400的倍数则不闰。 **JavaScript日期操作**: 在JavaScript中,你可以使用`getDate()`获取...
- **二十世纪90年代中期的背景**:当时的网络用户主要通过 28.8kbit/s Modem 接入互联网。随着网页功能需求的增长,HTML 文档变得越来越复杂和庞大,导致用户体验下降。特别是表单验证这类任务需要客户端与服务器...
- **JavaScript的诞生**:在20世纪90年代初期,互联网上的网页主要是静态的,用户与网页之间的互动非常有限。为了增强网页的交互性,1996年,网景(Netscape)公司研发了一种新的脚本语言——Mocha,并将其集成到...
- **背景**:20世纪90年代中期,随着互联网用户的增加和技术的进步,简单的HTML文档已无法满足需求,表单验证等操作需要频繁地与服务器进行交互,用户体验较差。 - **起源**:1995年,Netscape公司和Sun ...