《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等内容。