《JavaScript: The Good Parts》,解释了JavaScript编程的精髓。理解本书能够解释我们实际编程中遇到的很多问题,并且有助于写出高质量的代码。这里总结了一下要点,其主要内容涉及到JavaScript特殊的语法,理解Prototype,正确解析this的指代,函数对象的使用,闭包的使用等。

    1. JavaScript 语法和对象的特别之处

       1) 两种获取对象属性值的方法:[] 或.

      stooge['first-name']
      stooge.nickname

       用[]可以取到不符合变量命名规范的属性(如'first-name')。当你的JSON对象有个key值是这种名字时,就可以用[]取出它的值。

       2) 属性可以动态添加;属性可以是函数(函数属性成为“方法”(method))    

      flight.status = 'overdue';

       当status是flight的属性时,上面的结果是更新其值;当没有此属性时则添加此属性。

       3)灵活使用逻辑运算符||和&&      

      var status  = flight.status || "unknown";
      var model = flight.equipment && flight.equipment.model

       JavaScript的||和&&运算的结果不是逻辑值true或false,而是操作数之一。上面的|| 给status一个默认值"unknown". 上面的&&给model至少一个undefined值而不至于因为flight.equipment没定义而抛出异常。


    2. 理解Prototype

    我发现这本书对Prototype的解释简明扼要,比网上那些长篇大论有效多了。“每一个对象都链接到一个原型对象并从其继承属性”。(这个链接通过每个对象都有的prototype属性来实现)。换言之每个对象都有它的原型对象。所有用object literal(即{...})创建的对象都链接到Object.prototype。这个Prototype可以结合设计模式中的Prototype Pattern去理解。

     1)要创建一个对象并指定另一个对象作为它的原型对象,较好的方式如下:

    if(typeof Object.create !== 'function') {
        Object.create = function(o) {
            var F = function() {};
            F.prototype = o;
            return new F();
        }
    }
    var another_stooge = Object.create(stooge);

    如上another_stooge将以stooge为原型,继承stooge的属性。

    在对象建立后,向其原型增加的属性仍然会被该对象继承。获取对象属性值时对属性的查找策略是:沿着继承链向上查找,遇到就停止。当对象有某属性时就取该对象的该属性,没有时则向其原型对象索取。这样不断递归。“原型链条只对索取属性有效”,这句话可以解释下面的很多问题。

     2) 对象的hasOwnProperty方法只在本对象中查找属性,不访问原型链条。因为那个链条只对索取属性(如obj.prop)有效。

     3) delete的影响  

    obj.prop        // 对象的prop属性
    delete obj.prop
    obj.prop        // undefiend,或者是obj原型的原型的。。。的prop属性

     delete可以用来从一个对象obj中移除某一个属性prop。移除对原型没有影响。这也可以理解为原型链条只对索取属性有效。相反,如果原型中也有prop属性,则obj.prop将会取到原型中的值。

    

    3. 少用全局变量!

    全局变量应该尽可能少用。可以用一个唯一的全局变量作为容器,容纳所有其他的全局变量。像下面这样:

    var MYAPP = {};
    MYAPP.stooge = {};
    MYAPP.flight = {};

    4.函数    

    “函数是JavaScript最好的东西”。函数也是对象。一个重要的推论就是它们也链接着原型:Function.prototype。另外,每个函数对象func创建的时候也会带一个prototype属性,它的值是一个对象,这个对象带个constructor属性,其值是这个函数func...(又绕回去了...)

     函数对象的建立是通过函数字面值(function literal),即function(va...){}   


    5. 函数的调用模式和this指代

    this指代的对象由函数(function)的“调用模式”(invocation pattern)决定。有四种调用模式:

      1)方法调用模式(Method Invocation Pattern)

        前面提到方法是作为属性的函数。方法调用即通过对象调用函数:

      var myObj = {
        value: 0,
        increment: function(inc) {
            this.value += typeof inc === 'number' ? inc : 1;
        }
      };
      myObj.increment(); // 方法调用模式
      myObj.value

       如上,increment为方法,调用之后myObj.value将打出1,因为increment中的this指代myObj。

      2)函数调用模式(Function Invocation Pattern)

        这种指的是当函数不是对象属性时对它的调用模式。此时理论上其函数体中的this指代的是隐藏的全局对象。所以这个this没有什么用,而且不推荐用。这同样适用于内部函数。但我们经常需要在方法的内部函数中访问方法所属的对象,这就要用到“that”技巧:

      myObj.double = function() {
          var that = this;
          var helper = function() {
              that.value = add(that.value, that.value);
          }
          helper();
      }

       如上helper作为double方法的内部函数,只能通过that访问到myObj对象来完成任务。

      3)构造函数调用模式(Constructor Invocation Pattern)

        首先要好好理解一下构造函数。构造函数一定要与new一起使用,不然后果很严重。为了提醒自己,常常把构造函数名首字母大写。但不管怎样new Constructor()的方式不推荐使用。有更好的替代方法来构造对象。

      var Quo = function (string) {
          this.status = string; //!! 注意这里用了this,不用new Quo调用时this将指向全局...
      }
      
      Quo.prototype.get_status = function {
          return this.status;
      }
      var myQuo = new Quo('confused');
      myQuo.get_status()

       “当用new前缀来调用一个函数时,将建立一个新的对象,这个对象带有一个隐式的链接指向这个函数的prototype属性"。(我们前面提到每个函数都有一个prototype属性)。这时的this将指向建立的这个对象。不用new而直接调用时this将指向全局,灾难就在这里。从上面最后也可以看到对象myQuo继承了方法get_status,并且其中的this指向的是myQuo.

      4)apply调用模式

        apply调用可以指定this应该绑定的对象。这个也体现了函数是对象的一个结果:函数也有方法。(即apply方法)。

      var array = [3,4];
      var sum = add.apply(null, array);
      
      var statusObject = {status: 'A-OK'};
      var status = Quo.prototype.get_status().apply(statusObject);

       如上,第二种用法将get_status方法用在statusObject上,虽然statusObject没有这个方法。同时将this指向了statusObject,得到其status属性值。


    这次先分享到这里。下次分享关于闭包Closure和Module Pattern等内容。