• 编写高质量代码:Web前端开发修炼之道(四)


    这一节是继上一节高质量的Javascript

    7)编程实用技巧

    1:弹性

    从 一个标签区和内容区的实例(就是点击不同的标签菜单显示不同的内容块)来说明不需要每个tabmenu都设置onclick事件,为了让程序更有弹性,可 以将所有的点击时间封装成一个函数,变化的标签作为参数传入实现点击不同的标签显示对应的内容块,这样标签的数量可自适应,可增可减,而js代码可以不用 变动,只需要修改html的标签就可以。-------------这点我想做过项目的伙伴们都能深深的体会到。

    琐碎知识点:

     js 一个非常经典的问题:在遍历数组时对DOM监听事件(如上例中的菜单单击事件tabMenus[i].onclick),索引值始终等于遍历结束后的值。 解决这一问题可以用闭包,或者给每个DOM节点添加Index属性用来标示区别。---------我遍历一般用jquery的each函数,挺好用的, 没用过Dom,但是还是一个值得注意的地方。

    2:可复用性

    因为一个html页面的id只能出现一次,所以,如果你的程序需要被多次复用,就一定不能用id来标识,不能用id作为js获得DOM节点的挂钩,更好的应该是使用class.  在做项目过程中,我们可以通过class取得一组Dom标签,但由于id的唯一性,只适合取某个固定的标签。

    组件需要指定根节点,以保持每个组件之间的独立性。--------具体的可能要看书上的代码示例来体会。(就是尽管有相同的class来标识同类结构(一个组件),但是不同的组件与组件之间还需要指定一个父节点来区别对待)。


    3:通过参数实现定制

    如果每个tab标签需要不同的样式,这个时候就只能根据每个tabMenu的唯一标示作为参数传到处理函数,在函数中根据不同的标识来选择不同的样式

    如果一个函数内某个因素很不稳定,我们可以将它从函数内部分离出来,以参数的形式传入,从而将不稳定的因素和函数解耦。---与第一点弹性有相似处

    4:this关键字的指向

    在js全局域中  this指向window.

    A:JavaScript伪协议和内联事件对于this的指向不同:

      伪协议:a标签的href="javascript:alert(this==window)"     结果:弹出true       伪协议中this指向window

      内联事件:onclick="alert(this.tagName)"                          结果:弹出a          内联事件的this指向当前的Dom元素  

    B:setTimeout和setInterval也会改变this的指向,在这两个函数中  this指向window

    C:DomNode.on***事件也会改变this的指向,this会指向当前调用时间的Dom结点。

    使用匿名函数可将B,C中this的指向改变。

    例:

    复制代码
     1 <script type="text/javascript">
     2     var name = "global-name";
     3     var btn = document.getElementById("btn");
     4     var test = {
     5         name: "test-name",
     6         say: function () {
     7             alert(this.name);
     8         }
     9     }
    10     test.say();                                              //输出test-name
    11     setTimeout(function () { test.say() }, 1000);            //输出test-name    匿名函数会改变指向,谁调用就指向谁
    12     /*
    13     setTimeout和setInterval两个函数的调用方式,第一个参数应该是函数指针,或字符串
    14     */
    15     setTimeout('test.say()', 1000);                     //作为字符串,还是由test调用,输出test-name  
    16     setTimeout(test.say, 1000);                         //作为函数指针,输出global-name   setTimeout的直接调用指向方式,this指向window
    17 
    18     setInterval(function () { test.say() }, 1000);           //输出test-name
    19     setInterval('test.say()', 1000);                         //输出test-name ,由test主调
    20     setInterval(test.say, 1000);                             //输出global-name
    21     setTimeout(function () { alert(this == window) }, 1000); //输出true, this为全局window对象
    22 
    23     $(function () {
    24         var btn2 = document.getElementById("btn");
    25         btn2.onclick = function () { test.say() };            //输出test-name
    26         btn2.onclick = function () { alert(this == btn2) };   //输出true, this为DOM元素对象
    27     });
    28 </script>
    29 <input type="button" name="btn-name" id="btn" value="test-btn" />
    复制代码

    总结:1:如果setTimeout和setInterval调用的处理函数,该处理函数是“直接调用的函数”,那么this指向window

       2:DomNode.on***关联的处理函数,该处理函数是“直接调用的函数”,this指向Dom结点

       3:如果将上面的处理函数用匿名函数封装起来,那么,处理函数的调用方式:由直接调用变为间接调用;这样就不会受到外面调用函数的影响,this该指向谁就指向谁

    自己的话:一般来说,this----该动作是谁调用的,那么this就指向调用者,只有碰到setTimeout、setInterval、btn.on***等会改变指向的函数,才不会遵守基本规则,但是也可以通过匿名函数的间接调用来解决,这样又变回了一般状态。


    另外还可以通过call和apply函数来改变处理函数的this指向,test.say.call(btn); test.say.apply(btn);  this 直接指定的。

     5:预留回调接口

    我所理解的就是,在函数中预留出一个参数handler,该参数的实参是一个函数。 判断回调函数是否有用,if(handler){ handler(参数);//调用回调函数;}

    添加回调的接口可以参加代码的可扩展性。

    6:编程中的DRY规则

    DRY----don't repeat yourself,强调在程序中不要将相同的代码重复编写多次,更好的做法是只写一次,然后多次引用。提高重用率,减少代码量

    7:用hash传参

    比较: 普通传参方式:参数量大,而且参数的顺序很重要,

        hash传参方式:hash是一个key-value的集合,可包含任意类型的数据,用hash对象传参,可以提高函数调用的灵活性,没有顺序的控制

    如: test函数有6个参数,调用时有些为空,有些不为空,普通传参: test(null,null,null,null,null,"hello"); 用hash传参: test( {str:"hello"} ) 显然第二种简单直观.

    8)面向对象编程

    1:面向对象

    将数据和处理函数定义到了一个对象的内部,作为这个对象的属性和行为存在,在对象的内部,属性和行为通过this关键字来访问,在对象的外部,用对象的属性和对象的行为来调用。   额~,抽象的概念,理解起来就是别扭。

    它的思维过程是定义一个对象,对象有自己的属性和行为。属性和行为都从属于对象,于是有对象内,和对象外的概念。

    整个程序可以由一堆对象组成,对象和对象之间可能会有通讯,为了能相互访问,于是就有了私有和公有的概念。

    2:js的面向对象

    A:类的概念 

     一般的类定义,都要class关键字,js中的类没有class关键字,它是用函数来充当类的函数在js中既可以用作普通函数,也可以当类来使用,当充当类时,又担负着构造函数的作用。

    fuction test(){//code...}

      调用方式: 函数充当普通函数,直接使用()进行调用。 如:test()

            函数充当类时,使用new来实例化。如:var c=new test();

    B:原型(prototype)

    原型在js中是一个很重要的概念,因为JS是基于原型的语言,通过new实例化出来的对象,其属性和行为来自于两部分:1:构造函数(既函数本身),2:原型

    原型的概念及由来:我们只需要知道“在声明一个类的时候,同时就生成了一个对应的类的原型。”------------我理解为同样的生命周期。

    通过test.prototype就可以指向这个原型, 而原型也可以通过它的constructor属性指向test类,具体指的是test类的构造函数。

    只有当函数作为类使用,new出来一个对象时,原型才具有它存在的价值(个人观点)

    原型是个hash对象,也可以分开定义

    如:test.prototype={                    分开定义:

      name:"***",                       test.prototype.name="***";

      type:"***",                         test.prototype.type="***";

      say:function(){.....}                   test.prototype.say=function(){...};

    }

    C :优先级

     当构造函数和原型中都定义了同名的属性和行为,则构造函数中的属性和行为优先级要高,它会覆盖原型中的属性和行为。

    this关键字无论是出现在构造函数还是原型中它指代的都是实例对象。能改变this指向的函数就另当别论。

     D:公有和私有

    js中没有public,protect,private等关键字,js中的公有还是私有是通过作用域来实现的。

    用this.***定义的属性都是公有的(原因:在构造和原型中的this 都是同一个实例对象,在原型中可以访问得到),而用var ***定义的属性都是私有的(在原型中访问不到)。方法的公有私有也一样用this区别开来。

    习惯: 定义类时,一般我们会把属性(变量)放在构造函数里------方便构造函数接收参数,而行为(方法)放在原型里。

    原因:因为在内存中一个类的原型只有一个,写在原型中的行为可以被所有实例共享,实例化时不会在实例的内存中复制一份;而写在类中的行为,会每个实例都会复制一份。--------------为了减少内存消耗。

     自己的话:实例化时,原型中的属性和行为是引用,构造函数中的属性和行为是复制。

     3:继承

    琐碎知识点:在js中,fuction作为普通函数存在时,直接使用()进行调用,函数内的this是指向window;

              function作为类存在时,通过new实例化,类里面的this指向实例对象。

    A:构造函数中属性和行为的继承

    为了实现构造函数中属性和行为的继承,可以通过call/apply方法来实现。

    如:

    当实例化B的对象时就能访问到构造函数里的属性和方法。

    B:原型中属性和行为的继承

    琐碎知识点:js中的传值和传址。

    在js中,赋值语句会用传值和传址两种不同的方式进行复制,如果是数值型,布尔型,字符型等基本数据类型,将复制一份数据进行赋值。---传值

    如果是"数值,hash对象等复杂类型"(数值,hash对象可包含简单类型数据),在进行赋值时会直接使用内存地址赋值。-----传址

    原型中的属性和行为的继承,我们可以直接将A的原型赋值给B的原型,如B.prototype=A.prototype

      这样B的实例及B.prototype 是可以访问A类原型中的say方法,但是prototype本质上是一个hash对象,它的赋值是传址的方式,当我们在B的原型中在添加一个AddFunctionForB()方法时,由于传址方式,在A的原型中也会添加B的AddFunctionForB()方法. 

    如:

     为了解决这个问题,我们用另一种方法实现prototype的传值-new somefunction() ----new出基类的对象,然后重新定向B类的构造。

    在上面的代码中我们定义了A类及A类的原型、B类;我们只需要将B类的原型中指向A类的实例对象(这时,B类会继承A类的构造及原型中的属性和方法),这样就可以访问A类原型中的方法。但由于这种赋值会使得B.prototype.constructor指向了A类的构造,所以我们要将它纠正,重新指向B类。

    这样就解决了上面的问题,既能从原型继承方法,又不会引起不必要的混乱。

    9)prototype和内置类

    从第8点知道了prototype和类的关系,现在看看prototype和js自带的内置类的关系。

    Js的内置类包括Array,String,Function等 如Array提供length属性,push.pop方法,String提供length属性,replace.split方法 ,Fuction提供Call.apply方法.

    一般内置类我们一般不用New实例化,习惯更简单的方式,如 var a="sssss";  而不是 var a=new String("sssss");

    只要是类就会有原型,So 我们可以对内置类的原型进行修改,以重写或扩展它的功能。

    如:常见的有 Array.prototype.each=function(){....}        Array.prototype.map=function(){.....}

    里面可能会涉及到this指针的指向,我们只需要记住,在类的构造函数和原型中的this ,都是指向该类实例化的对象。

     注意:内置类的方法可以重写,比如toString()方法,但是 他的属性不能重写,如length;

     

    10)标签的自定义属性

    在html语言中的标签一般有自己的属性,如 id,class href等,我们有时候会用到自定义的属性
    为了从兼容性来考虑,用JS来读取属性时,笔者建议对于常规属性,统一使用node.***的方式读取,对于自定义属性,统一使用node.getAttribute("***")读取。

    自定义属性一个非常有用的技巧:----将普通字符串转换为hash对象或数组

    字符串的反序列化-----------通过eval函数来实现。

    如在a标签里自定义一个属性<a id="a" userinfo="name:'alice' , age:22 , pwd:'123456' " ></a> 

    取到a的userinfo属性值 var info = document.getElementbyId("a").getAttribute("userinfo");

                alert(typeof info) //string 类型      访问info.name  info.age都访问不到

                info=eval("("+info+")"); 转化为对象类型; 访问info.name  输出alice ;  访问 info.age   输出22;

    11) 标签的内联事件和event对象

    在IE下,event是window对象的一个属性,是全局作用域下的; 在FF中,event对象是作为事件的参数存在。

    在标签的内联事件中,FF下,使用arguments[0]可以访问到event对象。不是内联事件,可以给处理函数传参来访问  funtion(e){...}

    关于event对象前面的章节中也有讲过,这里就不再描述了。

    12)一些规则

    基本在前面的内容中均已描述过,包括css命名规则、注释规则、html规范、css规范、js规范。


    到这全书就结束了。将学习笔记记录下来希望对看到该文的朋友有一定的帮助。O(∩_∩)O~ 

    转载请注明出处

    原文地址:http://www.cnblogs.com/Joans/archive/2012/09/14/2685110.html

  • 相关阅读:
    二叉排序树
    C# 大端与小端
    【转】C#socket通信
    【转】Github 搜索技巧,快速找到好资源
    web api 跨域请求,ajax跨域调用webapi
    【转】Linux简介及最常用命令
    【转】带你吃透RTMP
    09-vuex基本应用之计数demo
    08-配置vue路由的步骤
    02-原型与原型链
  • 原文地址:https://www.cnblogs.com/lxzltg/p/5104040.html
Copyright © 2020-2023  润新知