• Javascript 与 SPA单页Web富应用


    书单推荐

    # 《单页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代码,复杂度可想而知,模块化开发和框架设计的重要性不言而喻。

    随着单页Web应用的崛起,各种框架也不断的涌现,如Vuejs、ReactJs、Angularjs、Backbone.js、Ember.Js等,还有RequireJS等模块加载器。但是,本书没有讲解这些框架和模块加载器,这也正是我喜欢书本的原因之一。作者自己很少使用框架,并且框架的限制过多,一旦不符合框架本身的设计哲学,结果可能适得其反。———— 事实上所有的框架使用都是如此。
    但不管是使用框架还是按照书的方法开发,书中的思想都是适用的。

     

    第一个SPA应用 

    <html>
    <head>
        <title></title>
        <style type="text/css">
            body{
                 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;
                 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

      

    变量作用域

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

    换个方式来看,函数就像监狱(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,来总结一下:

    1、两者都首先创建了对象的模板

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

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

    4、实例化对象,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个原因:

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

    2、它不是对象或者原型上的属性,所以它无法访问或者赋值;

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

    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()来获取。

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

  • 相关阅读:
    通过git命令行从github或服务器上克隆、修改和更新项目
    将项目部署到 github上(部署到码云操作一样,前提是有码云账号)
    tp5中代替tp3.2中的一些方法
    关于ThinkPHP下使用Uploadify插件 仅有火狐提示HTTP Error (302)错误的解决办法
    nginx轮询宕机解决
    nginx实现动静分离
    nginx实现负载均衡
    nginx实现反向代理
    解决表单重复提交
    解决跨域问题
  • 原文地址:https://www.cnblogs.com/CyLee/p/6366325.html
Copyright © 2020-2023  润新知