JavaScript: The Good Parts 第五章
JS弱类型,不需要类型转换,对象继承关系变得无关紧要。对于一个对象来说重要的是它能做什么,而不是它从哪里来。
伪类 Pseudoclassical
JS原型存在着诸多矛盾,虽然看起来想基于类的语言,但实际上还是原型机制。表现之一就是:通过构造器函数产生对象。
当一个函数对象被创建时,Function
构造器产生的函数对象会运行类似这样的一些代码:
this.prototype = {constructor: this};
constructor
属性没什么用,重要的是prototype
对象。
当采用构造器调用模式,即用new
前缀去调用一个函数时,函数执行的方式会被修改。如果new
运算符是一个方法而不是一个运算符,他可能会像这样执行:
例子:
伪类模式本意是想向面向对象靠拢,但它看起来格格不入。我们可以隐藏一些丑陋的细节:
inherits
和method
都返回this
,这样就允许我们采用级联的方式编程:
危害:没有私有环境,无法访问父类方法。如果忘记了new
,this
就错了,也没有运行时警告。
对象说明符 Object Specifiers
多参数问题,记不住
原型 Prototypal
在一个纯粹的原型模式中,我们会摒弃类,转而专注于对象。基于原型的继承相比于基于类的继承在概念上更为简单:一个新对象可以继承一个旧对象的属性。
这是一种”差异化继承“。通过定制一个新的对象,我们指明它与所给予的基本对象的区别。
函数化 Functional
以上的弱点都是没法保护隐私。更好的方式就是应用模块模式。
我们从构造一个生成对象的函数开始。我们以小写字母开头来命名它,因为它并不需要使用new
前缀。包括4个步骤。
-
创建一个新对象。有很多方法去构造一个对象。它可以构造一个对象字面量,或者它可以和new
前缀连用去调用一个构造函数,或者他可以使用Object.create
方法构造一个已经存在的对象的新实例,或者它可以调用一个会返回一个对象的函数。
-
有选择地定义私有实例变量和方法。这些就是函数中通过var
语句定义的普通变量。
-
给这个新对象扩充方法。这些方法拥有特权去访问参数,以及在第2步中通过var
语句定义的变量。
-
返回那个新对象。
伪代码
var constructor = function(spec, my) {
var that, 其他私有实例变量;
my = my || {};
把共享的变量和函数添加到my中
that = 一个新对象
添加给that的特权方法
return that;
};
spec
对象包含构造器需要构造一个新实例的所有信息。
my
对象是一个为继承链中的构造器提供秘密共享的容器。
扩充that
,我们可以分配一个新函数成为that
的成员方法。或者,更安全地,我们可以先吧函数定义为私有方法,然后再把他们分配给that
:
var methodical = function() {
...
};
that.methodical = methodical;
这样分两步的好处是,如果其他方法要用methodical
,它们可以直接调用methodical()
而不是that.methodical()
。如果该实例被破坏或篡改,甚至that.methodical
被替换掉了,调用methodical
的方法同样会继续工作,因为它们私有的methodical
不收该实例被修改的影响。
函数化模式还给我们提供了一个处理父方法的方法。我们会构造一个superior
方法,它去的一个方法名并返回调用那个方法的函数。该函数会调用原来的方法,尽管属性已经变化了。
函数化模式有很大灵活性。它相比伪类模式逼近带来的工作更少,还让我们得到更好的封装和信息以你藏,以及访问父类方法的能力。
如果一个对象的所有状态都是私有的,那么该对象就成为一个“防伪(tamper-proof)对象”。该对象的属性可以被替换或删除,但该对象的完整性不会收到损害。如果我们用函数话的样式创建一个对象,并且该对象的所有方法都不使用this
或that
,那么该对象就是持久性(durable)的。一个持久性对象就是一个简单功能函数的集合。
一个持久性的对象不会被入侵。访问一个持久性的对象时,除非有方法授权,否则攻击者不能方位对象的内部状态。