下载本文的示例代码:ModulePattern . zip-所有4个HTML文件和万能药. js-1.6 KB
介绍
AngularJS的库中有很多东西,但我只想重点介绍小型的、特定于主题的库,我相信这些库可以更好地介绍Angular。您不需要任何与Angular甚至JavaScript相关的经验来理解这篇文章。希望大家能从这篇文章中看到使用Angular的一些好处,愿意尝试一下。背景
我使用Angular已经有一段时间了,在学习Angular的时候,我也喜欢构建一些样例,所以当我开始深入其中的时候,我并没有过多考虑模块或者JavaScript的设计模式,这有助于保持代码的有条理和有条理。这就是所有的关键点:保持代码的有条理和有条理。因此,现在我回去创建这个极其小的示例,以展示使用模块是多么简单。
(大多数)文章在解释模式方面有问题
大多数时候,人们试图在读者知道一个模式是什么之前解释它,这基本上误导了所有人。在这里,我们应该尽量让这篇文章变得简单。先来看看这个问题。有什么问题?这是一个关于默认情况下将在全局内存空间中创建的所有内容的Javascript问题。
我是这个意思。JavaScript默认全局问题
假设您的HTML中有以下脚本。
script var isDoingWork=false/script作用域?
你知道这个变量的范围吗?是的,它是全球性的。该布尔值实际上被添加到浏览器的全局窗口对象中。
将其设置为“操作”
在这里你可以看到它是如何运作的。
下载本文的代码示例。在浏览器中打开modulePattern.htm。打开浏览器开发工具-F12 (Chrome,IE)或Ctrl-Shift-I (Opera)-(这样就可以看到控制台了)。在浏览器工具控制台下,输入:“正在工作”。然后按回车键,你会看到输出值是假的。现在输入:是工作=真,然后按回车键,下载的值将为真。您已经更改了该值。您可以看到,通过输入doingwindow.isDoingWork=true,然后按enter键,该值已被更改。已添加到全局窗口对象中。这可能会导致一些名称冲突和一些严重的错误。这对你来说可能有点危言耸听,不是吗?但是假设您决定实现一个新的JS库,它可以每分钟创建一次。假设你找到了一个名为pangeline . js的伟大的库,它将解决你所有的问题。
所以你在页面上引用如下:
script src=' http : vanlincy . js '/脚本非常简单,您已经解决了之前遇到的所有问题。然而,因为它是一个巨大的库,并且您只想要解决方案,但是您不会回去深入挖掘这个巨大的(数千行代码)源文件中的每一行代码。但是深埋在panel . js的某个角落,下面真的有这样的代码3360。
var isDoingWork=falsesetInterval(function(){ IsdGoinWork=!正在工作;}, 3000);这个代码真的很酷,你知道吗?
每隔3秒,它将该布尔值设置为一个相对值。啊!
亲自看
如果你想自己验证这个东西,你可以做以下步骤:
下载本文的示例代码。在浏览器中打开modulePattern2.htm。打开浏览器开发工具- F12(Chrome,IE)或Ctrl-Shift-I (Opera)-(这样就可以看到控制台了)。在浏览器开发工具的控制台下,回车: isDoingWork,按回车键重复步骤4几次,你会发现isDoingWork的值每3秒左右就会发生变化。这不是很棒吗?我的第一点是:模块模式非常有用
我需要解释这一点,以便向您展示为什么JavaScript模块模式非常有用。我必须向您展示JavaScript模块模式,这样我就可以告诉您它是如何在AngularJS中使用或实现的。模块模式:封装
所以,事实上,模块模式基本上是封装的。封装听起来很熟悉,如果你有一些面向对象编程的经验——我希望你能有一些经验。封装是面向对象编程的三大原则之一。封装的另一个术语是数据隐藏。在经典的面向对象编程中,——不同于JavaScript所依赖的原型OOP数据隐藏是构建类模板的固有部分。
例如,在C#中,动物类隐藏数据特定值的封装与动物对象相关联。然后,如果有人决定更改这些值,他或她必须显式初始化一个动物对象,并设置该对象的值以实现目标。在JavaScript中,我们可以随意设置全局窗口对象中的值。
公共类Animal { //构造函数允许用户设置commonName公共动物(字符串名称){ this.commonName=name} //使commonName成为私有将它隐藏(封装)在类私有字符串commonName中;//显式公开只读取公共字符串公共名称的值get {return this。common name } }在JavaScript中,已经创建了模块来模拟这种封装行为,这样我们就不会将我们的变量组织到一个全局命名空间中,这导致了一个难以发现和修复的深层隐藏问题。
现在你知道为什么了,让我们看看会是什么样子。
函数被立即调用的表达式
似乎我们每向前迈一步,都要走一点弯路,因为要得到允许我们创建模块模式的JavaScript语法,就要理解一个叫做function被立即调用的表达式语法,也叫life(life发音为‘iffy’)。
最基本的生活是这样的:
(function(){//lines//of//code }());如果你从未见过这样的事情,那你就说不通了。立即被呼叫
首先,名字的第一部分被立即调用的原因是包含这个特殊函数的源文件通常是加载的,那么包含在这个函数中的代码就会运行。更仔细地观察IIFE语法
你可以看到这个语法的中心是一个函数。看着这个代码块,我已经将代码分段并标记了一些行,以便我们可以讨论它。
(//1.function() //2。{//3.///////////////code }() //4。);//5.首先看上面脚本的第二行。这一行通常看起来像是匿名(即未命名)函数声明。那么,第三个到第四个就是这个函数的主语部分。最后,第4行以一对括号结束,括号告诉JavaScript解释器调用这个函数。最终,所有这些都将被包装在不属于任何部分的括号(第1行和第5行)中,这对括号将告诉解释器调用这个外部匿名函数,它包含我们定义的函数。
生命可以携带参数
这种奇怪的语法在取参数后会看起来更加奇怪。它将如下所示
(函数(thing1,thing 2){//line//of//code }(' in string ',382));现在,您可以看到这个函数可以接受两个thing1,thing2参数,这些参数将被内部函数引用。在该示例中,传入的值是“in string”和382。
现在我们理解了IIFE语法,让我们创建另一个代码示例。我们将运行这段代码来看看封装是如何工作的。
(function(){ var is doing work=false;console . log(' IsdIngWork值: ' IsdIngWork);}());亲自看
要了解其工作原理,您可以执行以下步骤:
下载本文的源代码。在浏览器中打开modulePattern3.htm。打开浏览器开发工具- F12(Chrome,IE)或Ctrl-Shift-I (Opera)-(这样就可以看到控制台了)。你可以看到类似下图的东西
当调用该方法时——当代码被加载并被JavaScript解释器支持时,这种情况会立即发生——然后该函数创建isDoingWork变量,并调用console.log()在控制台中输出该变量的值。
现在,让我们使用开发工具中的控制台来尝试我们之前尝试过的步骤:
输入:正在工作,然后按回车键
当你这样做的时候,你会看到浏览器不再相信值isDoingWork已经被定义了。即使您试图从全局窗口对象中获取该值,浏览器也不会认为该值是在该对象中定义的。您看到的错误信息将类似于下图所示。
函数是一个对象:它创建一个范围
这是因为现在你已经在一个函数中创建了变量isDoingWork也就是说,在我们的匿名生活中——这个变量只能通过这个函数来访问。有趣的是,Javascript中的所有函数都是一级对象。这仅仅意味着函数是一个对象,可以通过变量来访问。或者,描述它的另一种方式是存储一个指向该函数的引用,并在以后存储。
在我们的第一个例子中,我们的问题是我们没有保存对匿名函数的引用,所以我们永远无法再次获得isDoingWork的值。这是我们下一个例子的改进。
函数是一个使用这个的对象:
因为每个函数都是一个对象,所以每个函数都会有一个this变量,它为开发人员提供了对当前对象的引用。为了提供对我们的函数及其作用域的外部访问,我们可以返回这个变量——它将提供对当前对象的引用。
然后,除非我们将这个私有的isDoingWork变量添加到函数引用(this)中,否则我们也不能引用这个变量。出于这个原因,我们需要对前面的例子做一个微小的改变。它看起来像下面的:
thing=(function(){ //1。this.isDoingWork=false//2.console . log(' IsdIngWork值: ' IsdIngWork);归还这个;//3.}());
您可以看到,在第一行中,我们添加了一个新的全局变量thing,它包含匿名函数返回的值。从示例代码的开头跳到第三行,您可以看到我们已经返回了这个变量。这意味着我们返回了对匿名函数的引用。
在第二行中,我们还在这个引用中添加了isDoingWork,这样我们就可以使用语法thing.isDoingWork从外部引用这个值。亲自看
为了查看的操作,您可以执行以下步骤:
下载本文的示例代码。在浏览器中打开modulePattern4.htm。打开浏览器开发工具- F12(Chrome,IE)或者Ctrl-Shift-I (Opera)-(然后就可以看到控制台了)。正如您在第一个示例中看到的那样,您将看到isDoingWork的值将输出到控制台。然而,现在你必须输入东西。我正在努力得到这个值。
在最后一个例子中,变量值被成功封装,而其他JavaScript库可以显式引用thing对象来获取这个值。这似乎不太可能,这有助于我们保持全局命名空间的干净,并且似乎是一种更好的代码组织形式。这也使我们的代码维护更容易。最后,我们使用AngularJS
因为使用模块模式是一种最佳实践,所以AngularJS开发人员在库中构建了一个模块系统。普伦克代码
首先,你可以去这个普伦克(http://plnkr.co/edit/js8rbKpIuAuePzLF2DcP?p=预览-在新窗口或标签页中打开)来获得整个AngularJS示例。
我们在这里展示代码,这样我们可以更方便地谈论它。
首先,让我们看看这个HTML。
!DOCTYPE html html ng-app=' mainApp ' head meta charset=' utf-8 '/title angular Module示例/title脚本数据-require='[email protected]' src=' http :https://code . angular js . org/1 . 2 . 20/angular . js ' data-SEM ver=' 1 . 2 . 20 '/脚本src=' http 3360 MainCtrl . js '/脚本src=' http:secondCtrl.js/p olling-repeat=' a in MC。万物' { a } }/Li/ol/div ng-controller='秒ctrl为sc' p hello {{sc。name}}/p olling-repeat=' a in sc。所有的事情“{a}}/。
Angular定义和使用的叫做指令。这些指令基本上都是Angular定义的属性,AngularJS编译器(Angular JavaScript)会把它们转换成其他东西。
我们应用了ng-app指令,并为我们的Angular应用程序定义了一个名为mainApp的名称。
MainApp是我们稍后将看到的模块模式的起点。引入的脚本:每个都是一个模块
现在,请注意这个HTML中引入了三个脚本。
首先是必要的AngularJS库。
而另外两个是作为模块实现的角度控制器。
它们被实现为模块,以保持代码彼此独立,从这个应用的角度来看。
安古拉JS :创造得分
向下看,您将看到两个div:它们以下面的代码开始
Div ng-controller=' main ctrl as MC ' div ng-controller=' second ctrl as sc '这是为每个div设置ng-controller。每个div都有自己的范围。第一个控制器称为主ctrl,第二个控制器称为第二ctrl。
AngularJS编译器将使用这两个名称在您提供的代码中找到相应的函数(导入)。
如果AngularJS编译器找不到这两个名字对应的函数,就会抛出一个错误。
MainCtrl.js :第一个控制器
让我们看看mainCtrl.js文件中有什么。
可以在Plunker页面左侧点击,在Plunker中打开。
当你打开它时,你会看到一些熟悉的代码。嗯,至少你可以看到他们都被包裹在一个生命中。
(function(){ var app=angular . module(' mainApp ',[]);app.controller('MainCtrl ',function(){ console . log(' in MainCtrl . '));//vt=虚this -只是简写vt=thisvt.name=' MainCtrlvt.allThings=['第一','第二','第三'];});})();这是因为我们需要这些代码在加载mainCtrl.js文件时运行。
现在,请注意今生的第一行代码。
var app=angular.module('mainApp ',[]);这一行代码是Angular向其命名空间添加模块的方式。在这里,我们添加了一个模块,用于展示我们的应用程序。这是应用程序的模块,我们将其命名为itmainApp,与HTML页面上ng-app指定的值相同。
我们还创建了一个名为app的局部变量(仅在IIFE中局部可见),这样我们就可以使用它在这个函数内部再次添加一个控制器。
奇怪的角度语法
请再看一遍第一行。您会注意到,这是我们第一次创建mainApp模块,如果这是第一次,我们必须以字符串数组的形式提供我们可能需要的任何依赖项(指示依赖项库的名称)。然而,对于这个简单的例子,我们在这里不需要任何依赖。但是Angular仍然需要我们传入一个空数组,以便它知道我们正在创建一个新模块,而不是试图加载一个已经创建的模块。
Tip :你会看到我们会在secondCtrl.js中加载mainApp模块,上面提到的数组会有更多的功能。
一旦我们创建了主应用程序,我们就需要向其中添加控制器。这些是Angular希望我们在HTML中添加的控制器。将控制器添加到应用模块
添加控制器的代码如下:所示
app.controller('MainCtrl ',function(){ console . log(' in MainCtrl . '));//vt=虚this -只是简写vt=thisvt.name=' MainCtrlvt.allThings=['第一','第二','第三'];});为了添加我们的控制器函数,我们为app.controller()函数提供了一个控制器名称和一个函数。这里我们提供一个匿名函数。
因此,我们控制器的主要代码如下行:
console.log('in MainCtrl . ');//vt=虚this -只是简写vt=thisvt.name=' MainCtrlvt.allThings=['第一','第二','第三'];这里,当我们的控制器运行时,它将向控制台输出一行。然后,我们将这个变量重命名为vt(为了方便起见,我们称之为virtual this),然后我给它添加了一个name属性和一个名为allThings的字符串数组。
控制器和包装
这是由Angular调用控制器时将运行的代码。该控制器将在文件加载时运行,即在开始加载HTML时运行。这意味着控制器将被加载到app模块中,这些属性将被添加到控制器对象(函数)中。因为我们想给这个变量添加属性,所以我们可以在以后获取这些属性,但是它们是封装的,所以不能被每个人随意更改。
现在,让我们跳到在HTML中引用和使用控制器的地方。第一个Div
这是我们的MainCtrl控制器引用和使用的第一个Div。它看起来像下面的:
div ng-controller=' main ctrl as MC ' PMC指的是已添加到angular app模块的main ctrl/p pHello { { MC . name } }!/p olling-repeat=' a in MC。all things“{ a } }/Li/ol/div这个div输出我们网页的以下部分,它看起来像下一张图片中显示的内容。
使用角度指令创建输出
然而,它使用一种特殊的方式来创建输出。它使用两种角度指令:
{{mc.name}} ng-repeat的第一条指令与Div行上MainCtrl的声明和引用相关联。我们告诉Angular,我们想引用我们的MainCtrl函数(对象),名称为mc。这是Angular提供的一个很棒的缩写函数。
现在,因为我们在MainCtrl的这个对象上放了一个属性,我们现在可以通过mc和属性的名称来引用这些东西。我们将那些东西包含在特殊的双大括号{{}}中,这样Angular编译器就知道它是可以运行的代码,您会看到Angular将其转换为HTML:
pHello {{mc.name}}!/p编程如下:
你好MainCtrl!之后,我们建立一个漂亮的非必要列表,并使用ng-repeat指令迭代输出数组中的每一行。
然后Angular覆盖了整个allThings数组,并用下面的HTML替换它
Li ng-repeat=' a in MC。所有的东西“{a}}/Li变成以下输出
1.第一个2。第二个3。第三就是这么简单。这都是关于模块化的,我们的价值再也不会被任何人触及。
第二Ctrl :几乎是一回事
下面是SecondCtrl的代码。代码机会是一样的,只是我们得到的是我原来的app模块,有点不一样。——不是第一次创建了。
(function(){ var app=angular . module(' mainApp ');app.controller('SecondCtrl ',function(){ console . log(' in SeCOnd ctrl . ');//vt=虚this -只是简写vt=thisvt.name=' SecondCtrlvt.allThings=['培根','生菜','番茄'];});})();仔细看看下面的:行
var app=angular . module(' mainApp ');唯一的区别是我们不提供引用数组。
那是因为mainApp已经存在了,我们只想给它添加另一个新模块(SecondCtrl)。总结:最佳实践
所有其他脚本和HTML中的代码基本相同,但这里最重要的是所有代码都被模块化,数据被封装,以便更好地组织我们的代码。这是谷歌软件开发人员遵循的最佳实践,我们也应该遵循。请向他学习,使用它,和他在一起(阿门)。