Javascript 与 SPA单页Web富应用

书单推荐javascript

# 《单页Web应用:JavaScript从前端到后端》 http://download.csdn.net/detail/epubitbook/8720475

# 《MVC的JavaScript Web富应用开发》
http://download.csdn.net/detail/u012070181/7361155

 

SPA单页Web富应用,顾名思义,就是只有一张Web页面的应用。浏览器一开始会加载必须的HTML、CSS 和 JavaScript,只有全部的操做都在这张页面上完成,这一切都是由JavaScript来控制。所以,单页Web应用必将包含大量的JavaScript代码,复杂度可想而知,模块化开发和框架设计的重要性不言而喻。css

随着单页Web应用的崛起,各类框架也不断的涌现,如Vuejs、ReactJs、Angularjs、Backbone.js、Ember.Js等,还有RequireJS等模块加载器。可是,本书没有讲解这些框架和模块加载器,这也正是我喜欢书本的缘由之一。做者本身不多使用框架,而且框架的限制过多,一旦不符合框架自己的设计哲学,结果可能拔苗助长。———— 事实上全部的框架使用都是如此。
但不论是使用框架仍是按照书的方法开发,书中的思想都是适用的。html

 

第一个SPA应用 前端

<html>
<head>
    <title></title>
    <style type="text/css"> body{ width: 100%; height:100%; overflow:hidden; background: #777; } #spa { position: absolute; top: 8px; left: 8px; right: 8px; bottom: 8px; border-radius: 8px 8px 0 8px; background: #fff; } .spa-slider { position: absolute; bottom: 0; right: 2px; width: 300px; height: 16px; cursor: pointer; border-radius: 8px 0 0 0; background: #f00; } </style>
</head>
<body>
    <div id="spa">
        <div class="spa-slider"></div>
    </div>
</body>
<script src="https://cdn.bootcss.com/jquery/1.9.1/jquery.min.js"></script>
<script type="text/javascript">
var spa = (function ( $ ) { var $chatSlider,toggleSlider,onClickSlider,initModule; var configMap = { extended_height : 434, extended_title : 'Click to retract', retracted_height : 16, retracted_title : 'Click to extend', template_html : '<div class="spa-slider"></div>' }, toggleSlider = function () { var slider_height = $chatSlider.height(); if( slider_height === configMap.retracted_height) { $chatSlider.animate({height: configMap.extended_height}) .attr('title',configMap.extended_title); return true; } else if (slider_height === configMap.extended_height) { $chatSlider.animate({ height : configMap.retracted_height}) .attr('title',configMap.retracted_title); return true; } return false; }; onClickSlider = function ( event ) { toggleSlider(); return false; }; initModule = function ( $container ) { // render HTML
 $container.html(configMap.template_html); $chatSlider = $container.find( '.spa-slider' ); $chatSlider.attr( 'title', configMap.retracted_title) .click( onClickSlider ); return true; }; return { initModule : initModule } }( jQuery )); $(function(){ console.log(spa); spa.initModule($('#spa')); }) </script>
</html>
View Code

  

变量做用域java

要么全局,要么局部,而javascript中惟一能定义变量做用域的语块就只有函数。jquery

换个方式来看,函数就像监狱(prison),在函数中定义的变量就像是囚犯(prisoner).程序员

正如监狱限制囚犯不让他们从监狱逃脱同样,函数也限定了局部变量不让他们逃脱到函数以外:编程

function prison () { var prisoner = 'i am local!' } prison(); console.log(prisoner); //prisoner is not defined

要是真这么简单就行了!!!这里有一个Javascript 做用域的陷阱,能够在函数中声明全局变量。成功帮助囚犯(prisoner)越狱。那就是只要忽略 var 关键字便可:后端

function prison () { prisoner = 'i am local!' // 没有书写 var 关键字 } prison(); console.log(prisoner); // i am local!

这种陷阱常常出如今for循环计数器中。因此请务必要避免这种错误:浏览器

# 错误的作法,没有书写var关键字 function prison () { for( i = 0 ; i < 10 ; i++) { // ...
 } } prison(); console.log(i); // i is 10
 # 正确的作法,养成 var 定义变量的方式 function prison () { for( var i = 0 ; i < 10 ; i++) { // ...
 } } prison(); console.log(i); // i is not defined

 

 

变量提高

在JavaScript的函数中,当变量被声明时,声明会被提高到所在函数的顶部。而后被赋予undefined值。这就使得在函数的任意位置声明变量都将存在于整个函数中。尽管它在赋值以前,它的值一直是undefined。

function prison () { console.log(prisoner); // undefined    
    var prisoner = "Lee"; console.log(prisoner); // Lee
}

全局做用域和变量提高的行为结合时,须要注意仍然遵循变量提高的规则

var prisoner = "Mp"; function prison () { console.log(prisoner); // undefined 
    var prisoner = "Lee"; }

 

Javascript 对象 和 原型链

Javascript 对象是基于原型的,而当今其余普遍使用的语言所有是基于类的对象。

 - 在基于类的编程中,使用类来描述对象是什么样子的;

 - 在基于原型的编程中,咱们须要先建立一个对象,而后告诉Javascript引擎,咱们想要更多想这样的对象;

打个比方,若是建筑是基于类的系统,则建筑师会先画出房子的蓝图,而后房子按照该蓝图建造。

若是i建筑师基于原型的,建筑师会先建造一所房子,而后将房子都建成像这种模样的。

咱们使用先前囚犯的示例,对比一下两种编程方式的不一样。建立一名囚犯所要的条件有哪些,囚犯的属性包括名字(name)、囚犯ID、性别(sex)和年龄(age)。

基于类的编程方式,以C#为例

/* step 1 :定义类 */ public class Prisoner { public int sex = 0; public int age = 30; public string name; public string id; /* step 2 : 定义构造函数 */ public Prisoner ( string name, string id) { this.name = name; this.id = id; } } /* step 3 : 实例化对象 */ Prisoner firstPrisoner = new Prisoner( 'Joe', '12A' ); Prisoner secondPrisoner = new Prisoner( 'Sam', '2BC' );

 

基于原型的编程方式,以javascript为例

/* step 1:定义原型 */
var info = { sex : 0, age : 18 }; /* step 2:定义对象的构造函数 */
var Prisoner = function ( name, id ) { this.name = name; this.id = id; } /* step 3:将构造函数关联到原型 */ Prisoner.prototype = info; /* step 4 :实例化对象 */ Prisoner firstPrisoner = new Prisoner( 'Joe', '12A' ); Prisoner secondPrisoner = new Prisoner( 'Sam', '2BC' );

经过两种不一样的编程方式和step步骤的对比,咱们发现它们都遵循相似的顺序,若是你习惯了类,则适应原型应该不难。但魔鬼隐藏在细节中,若是你没有学习基于原型的相关知识,就以其余语言的类编程理解方式一头扎进Javascirpt,很容易被某些看起来简单的东西绊倒。咱们经过上述两个demo,来总结一下:

一、二者都首先建立了对象的模板

二、模板在基于类的编程中叫作,在基于原型的编程中叫作原型对象

三、构造函数。在基于类的语言中,构造函数是在类的内部定义的。当实例化对象时,就十分的清晰明了。在Javascript中,对象的构造函数和原型对象是分开设置的。因此须要额外多一步来将它们链接在一块儿。

四、实例化对象,javascript使用new操做符。这实际上违背了它基于原型的核心思想,多是试图让熟悉基于类继承的开发人员更容易理解。不幸的是,大部分开发人员容易搞混淆了。

 

基于第4个问题,咱们的解决方案是使用 Object.create 做为 new 操做符的代替,使用它来建立Javascript对象时,更接近原型开发的感受:

var info = { sex : 0, age : 18 }; Prisoner firstPrisoner = Object.create( info ); firstPrisoner.name = 'Joe'; firstPrisoner.id = '12A'; Prisoner secondPrisoner = Object.create( info ); secondPrisoner.name = 'Sam'; secondPrisoner.id = '2BC';

但这样手动设置每一个对象的成员变量name和id是痛苦的,由于会有重复的代码显得不整洁。另外一种更优的方案是,使用object.create + 工厂模式,来建立并返回最终的对象:

var info = { sex : 0, age : 18 }; var FactoryPrisoner = function ( name, id ) { var prisoner = object.create( info ); prisoner.name = name; prisoner.id = id; return prisoner; } var firstPrisoner = FactoryPrisoner( 'Joe', '12A' ); var secondPrisoner = FactoryPrisoner( 'Sam', '2BC' );

 

函数 —— 更深刻的窥探

理解函数是理解Javascript的关键之一,是构建专业的单页应用的重要基础。

 

自执行匿名函数

在 javascript 的开发过程当中,咱们常常遇到一个问题:在全局做用域中定义的东西在每一个地方均可用。但有时候咱们不想和全部人共享这些内容,或由于这极可能覆盖对方的内容。好比本身在开发第三方库、建立javascript插件的时候,不但愿使用者使用内部变量。

# demo 1 (function(){ var private_variable = "private"; })(); console.log(private_variable); // private_variable is not defined
 # demo 2
var prison = (function(){ return "Lee is in prison"; })(); console.log(prison); // Lee is in prison
 # demo 3 (function($){
  console.log($); })(jQurey)

 

模块模式

单页应用很是庞大,不能定义在一个文件中。咱们应该将文件分为一个个的模块,每一个模块都有它们本身的私有变量;

咱们仍然使用自执行匿名函数来控制变量的做用域,而且把代码分为多个文件:

var prison = (function(){ var prisoner_name = 'Lee', jail_term = '20 year term'; return { prisoner : prisoner_name, sentence : jail_term }; })() console.log(prison.prisoner_name); // undefined
console.log(prison.prisoner);       // Lee
console.log(prison.sentence);       // 20 year term
 prison.prisoner_name = "Mp"; console.log(prison.prisoner); // Lee

这里出现一个问题, prison.prisoner_name 没有被更新,这里有2个缘由:

一、它是在函数中使用了 var 关键词建立的局部变量。没法被外部访问;

二、它不是对象或者原型上的属性,因此它没法访问或者赋值;

也就是说,这些匿名函数中定义的属性,是没法经过外部直接调用或者赋值的。为了能更新它们,咱们的作法是添加内部方法来访问而且修改内部的变量

var prison = (function(){ var prisoner_name = 'Lee'; return { prisoner : prisoner_name, update_prisoner_name : function(name) { prisoner_name = name; }, out_prisoner_name : function(){ return prisoner_name; } }; })() console.log(prison.prisoner); // Lee
prison.update_prisoner_name("Mp"); console.log(prison.prisoner); // Lee
console.log(prison.out_prisoner_name());  // Mp

 咱们注意到,咱们依然不能在该对象或者原型上直接访问。咱们只能使用匿名函数返回的对象中的方法来访问。这也是利用了【闭包】

 

什么是闭包?

闭包是一种抽象的概念,理解起来可能有些困难,因此在回答什么是闭包以前,先了解一些背景知识。

随着程序的运行,它们会因各类事情而占用计算机的内存,好比保存变量的值。若是程序运行了却从不释放再也不须要的内存,长此以往会致使电脑崩溃。

在一些语言中,如C,内存的管理都是由程序员处理的。而像Java 和 Javascript,实现了自动释放内存的机制,当代码再也不须要时,就自动从电脑的内存中移除它。这种自动化系统叫作垃圾回收器。关于内存是手动释放好仍是自动释放好这里不讨论,只要知道Javascript 有垃圾回收机制就足够了。

那么问题来了,javascript垃圾回收器是怎么知道哪些东西【再也不须要的】呢?万一把我想保留的变量移除了怎么办,有什么方法可让某些变量不被回收?

咱们先来回答第一个问题:javascript垃圾回收器比较一根筋!!

它想:既然函数已经执行完毕了,咱们应该就再也不须要访问该函数环境中的东西了。
因此当函数执行完毕时,函数中全部建立的东西就会从内存中移除。

回答第二个问题:有的,这种方法被称之为闭包!

var menu,
    outer_function;

outer_function = function () {
    var fruit = 'apple';    
    var food = 'cake';
return function () { return { food : food, fruit : fruit } } } menu = outer_function(); // inner_function console.log(menu()); // {food: "cake", fruit: "apple"}

 

从上面这个demo中咱们发现,尽管outer_function()函数执行完毕了。理论上内部的两个变量 fruit 和 food 应该被销毁。但咱们如今却可使用menu()来获取。

这种解决方法就叫闭包。使用闭包的套路其实很简单:一个返回函数的函数!

相关文章
相关标签/搜索