广州之行

广州小蛮腰

从广州回来已经快一个月了,我才想起要总结一下的。

是的,很需要总结一下。不必说我们团队一起度过的无眠的夜晚,也不比说广州的现场环境的千奇百怪的复杂,单单只说上线晚上我紧急发布的补丁个数,就与别的省份的情况不可同日而语了。当然,我只去过广州,别的省份没有切身的体会,但从各种道听途说的消息来判断,也可见一斑。

十月八号到达广州,而月底是我们的上线时间,一开始我们不说信心满满,至少心里有底,因为我们在公司这边已经进行了三轮测试,虽然还存在Bug,但都不是什么严重问题。然而,事情并不是我想象的那样简单。首先的就是数据迁移这座大山挡在了我们面前——广东的数据在所有省份中是最多的,也是最为复杂的。经过千辛万苦,ESA的数据迁移过来了,然而LDAP的数据却成了难题,尤其是authorizations那个节点下的授权关系数据,UDI迁移工具跑了一个晚上,到第二天一不小心由于网络断开,一晚上的时间就白搭上了。就这样一次次的虐心,一次次失败。测试部的人因为没有测试环境只能在那尴尬的闲着,而我们开发的同样如此,那段时间真是让人“难忘”。于是第一周,我们的进度几乎为零。然而万幸的是最后终于在LDAP上添加了vlv索引,并修改UDI同步工具,总算把第一个测试环境的数据整了出来。

本以为测试的数据整出来我们可以轻松一把,没想到他们的WAS打包的那么不靠谱。测试的人测完了第一轮,晚上,我检查测试环境的更新情况时却发现之前给的补丁包并没有打上去。于是,第一轮的测试以“白测”而告终。不得已,我直接接手了他们的测试环境,什么事情还是自己来才放心。这得多亏了我以前对于WAS以及Linux的学习和了解,这下终于派上了用场,在输入命令轻敲回车的瞬间,心中充满了惬意。渐渐的,测试环境终于在我手中变的非常顺滑,各种问题可以很快定位原因并解决。

第二轮测试的时候,时间已飞快的来到了月底。测试紧张的进行,Bug数很多,但是测出的Bug质量却不高,很多重复的,很多边缘但并不严重的。看着那些Bug单,然后回想主线代码的杂乱无章,各种功能代码的交叉提交,心中无限忐忑。前路总是艰难,迷茫,既然走在了这条路上,我们唯有勇往直前,脚踏实地的走下去。改Bug,上传代码,打补丁包,更新,测试,回归,一步一个脚印。有句话叫你为什么要爬那座山,因为山在那里!而我那时最想说的是,为什么要熬夜去改Bug,因为问题在那里!

上线前几天,经过几次的上线演练,我们信心倍增,特别是我,对于UDI工具的使用,Radis的配置,已经达到了得心应手的程度。而亚坤他们的升级脚本,ESA数据以及刷数据工具,都已成熟起来,没有什么问题了。把几次演练的过程,汇聚起来,我写了份升级手册,在十一月四号晚上终于迎来了上线的时间。晚上九点,我们开始了升级。执行数据库脚本,同步ESA数据,运行UDI迁移工具,执行刷数据脚本...这些如期的顺利执行完后,开始测试。当晚即测出多个问题,有的甚至非常严重几乎要导致回退的地步,比如casp导致的单点登录失败,iam因工单窗口无反应而无法进行操作授权等,上线当晚即提交了7个补丁,不得不感叹计划不如变化快。在不知不觉中,天亮了,当最后看到领导发出的邮件了,那个不算很大的红色的上线成功四个字时,即使熬夜心中悠然

JavaScript 规范编程笔记:继承

JavaScript是一门弱类型的语言,从不需要类型转换。对象的起源是无关紧要的。对于一个对象来说重要的事它能做什么,而不是它从哪里来。JavaScript提供了一套更为丰富的代码重用模式。它可以模拟那些基于类的模式,同时它也可以支持其他更具表现力的模式。在JavaScript中可能的继承模式有很多。下面将介绍几种最为直接的模式。

在基于类的语言中,对象是类的实例,并且类可以从另一个类继承。JavaScript是一门基于原型的语言,这意味着对象直接从其他对象继承。

1. 伪类

当一个函数对象被创建时,Function构造器产生的函数对象会运行类似这样的一些代码:

this.prototype = {constructor: this};

该prototype对象是存放继承特征的地方。因为JavaScript语言没有提供一种方法去确定哪个函数是打算用来作构造器的,所以每个函数都会得到一个prototype对象。

当采用构造器调用模式,即使用new前缀去调用一个函数时,这将修改函数执行的方式。如果new运算符是一个方法而不是一个运算符,它将可能会像这样执行:

Function.method('new', function(){

    // 创建一个新对象,它继承自构造器函数的原型对象
    var that = Object.beget(this.prototype);

    // 调用构造器函数,绑定 this 到新对象上
    var other = this.apply(that, arguments);

    // 如果它的返回值不是一个对象,就返回该新对象
    return (typeof other === 'object' && other) || that;
});

以下是一个示例:

var Mammal = function(name){
    this.name = name;
};

Mammal.prototype.get_name = function(){
    return this.name;
};

Mammal.prototype.says = function(){
    return this.saying || '';
};

var myMammal = new Mammal('Herb the Mammal');
var name = myMammal.get_name();

以下我们可以构造另一个伪类来继承Mammal,通过定义它的构造函数并替换它的prototype为一个Mammal的实例来实现:

var Cat = function(name){
    this.name = name;
    this.saying = 'meow';
};

Cat.prototype = new Mammal();

Cat.prototype.purr = function(n){
    var i, s = '';
    for(i = 0; i < n; i += 1){
        if(s){
            s += '-';
        }
        s += 'r';
    }
    return s;
};

Cat.prototype.get_name = function(){
    return this.says() + ' ' + this.name + ' ' + this.says();
};  

var myCat = new Cat('Henrietta');
var says = myCat.says();
var purr = myCat.purr(5);
var name = myCat.get_name();

document.writeln('says : ' + says + '; purr : ' + purr + '; name : ' + name);
// says : meow; purr : r-r-r-r-r; name : meow Henrietta meow

伪类模式本意是想向面向对象靠拢,但是它看起来格格不入。我们甚至可以自行定义一个inherits方法来隐藏内部的细节:

Function.method('inherits', function(Parent){
    this.prototype = new Parent();
    return this;
});

伪类形式可以给不熟悉JavaScript的程序员提供便利,但是它也隐藏了该语言的真实本质。借鉴类的表示法可能误导程序员去编写过于深入复杂的层次结构。许多复杂的类层次结构产生的原因就是静态类型检查的约束。JavaScript完全摆脱了那些约束。在基于类的语言中,类的继承是代码重用的唯一方式。显然,JavaScript有着更多且更好的选择。

2. 对象说明符

var myObject = maker({
    first: f,
    last: l,
    state: s,
    city: c
});

当与JSON一起工作时,这还可以有一个间接的好处。JSON文本只能描述数据,但有时数据表示的是一个对象,如果构造器取得一个对象说明符,可以容易的做到,因为我们可以简单的传递该JSON对象给构造器,而它将返回一个构造完全的对象。

3. 原型

基于原型的继承相比于基于类的继承在概念上更为简单:一个新对象可以继承一个旧对象的属性。通过构造一个有用的对象开始,接着可以构造更多和那个对象类似的对象。可以完全避免把一个应用拆解成一系列嵌套抽象类的分类过程。

var myMa = {
    name : 'Herb the Mammal',
    get_name : function(){
        return this.name;
    },
    says : function(){
        return this.saying || '';
    }
}; 

// 差异化继承
var myCatMa = Object.beget(myMa);

myCatMa.name = 'Henrietta';
myCatMa.saying = 'meow';
myCatMa.purr = function(n){
    var i, s = '';
    for(i = 0; i < n; i += 1){
        if(s){
            s += '-';
        }
        s += 'r';
    }
    return s;
};
myCatMa.get_name = function(){
    return this.says() + ' ' + this.name + ' ' + this.says();
};

document.writeln(myCatMa.get_name());

4. 函数化

上述几种继承方式的一个弱点是我们没法保护隐私,对象的所有属性都是可见的。幸运的是,我们有一个更好的选择,那就是模块模式的应用。以下是构造一个产生对象的函数的步骤:

  1. 创建一个新对象,可以使用多种方式;
  2. 选择性的定义私有实例变量和方法;
  3. 给这个新对象扩充方法,这些方法将拥有特权去访问参数,以及新定义的变量;
  4. 返回新对象。

以下是上述步骤的伪码模板:

var constructor = function (spec, my){
    var that, ……;// 其他的私有实例变量
    my = my || {};

    // 把共享的变量和函数添加到my中

    that = // 一个新对象

    // 添加给 that 的特权方法

    return that;
};

spec对象包含构造器需要构造一个新实例的所有信息。spec的内容可能会被复制到私有变量中,或者被其他函数改变,或者方法可以在需要的时候访问spec的信息。my对象是一个为继承链中的构造器提供共享的容器。my 对象可以选择性的使用。

让我们将这个步骤应用到mammal例子中,此处不需要my,所以我们先抛开它,但将使用一个spec对象:

var mammal = function(spec){
    var that = {};
    that.get_name = function(){
        return spec.name;
    };

    that.says = function(){
        return spec.saying || '';
    };

    return that;
};

var myMammal = mammal({name: 'Herb'});

document.writeln(myMammal.get_name());

var cat = function(spec){
    spec.saying = spec.saying || 'meow';
    var that = mammal(spec);
    that.purr = function(n){
        var i, s = '';
        for(i = 0; i < n; i += 1){
            if(s){
                s += '-';
            }
            s += 'r';
        }
        return s;
    };
    that.get_name = function(){
        return that.says() + ' ' + spec.name + ' ' + that.says();
    };

    return that;
};

var myCats = cat({name: 'Cate',saying: 'Ni Ma B'});

document.writeln(myCats.get_name());

函数化模式还给我们提供了一个处理父类方法的方法:

Object.method('superior',function(name){
    var that = this,
        method = that[name];
    return function(){
        return method.apply(that, arguments);
    };
});

以下是在coolcat上的实验,它将返回父对象cat中的方法执行结果:

var coolcat = function(spec){
    var that = cat(spec),
        super_get_name = that.superior('get_name');
    that.get_name = function(n){
        return 'like ' + super_get_name() + ' bady';
    };
    return that;
};

var myCoolCat = coolcat({name: 'Bix'});
// like meow Bix meow baby

函数化模式有很大的灵活性。它不仅不像伪类模式那样需要很多功夫,还让我们得到更好的封装和信息隐藏,以及访问父类方法的能力。

如果使用函数化的样式创建一个对象,并且该对象的所有方法都不使用this 或 that,那么该对象就是持久性的。一个持久性的对象就是一个简单功能函数的集合。

JavaScript 规范编程笔记:函数(二)

1. 给类型增添方法

JavaScript 允许给语言的基本类型增加方法。以下是各种例子:

// 通过给 Function.prototype 增加方法使得该方法对所有函数可用
Function.prototype.method = function(name, func){
    this.prototype[name] = func;
    return this;    
};

// 给数字类型添加一个提取数字中的整数部分的方法
Number.method('integer', function(){
    return Math[this < 0 ? 'ceiling' : 'floor'](this);
});

document.writeln((-10 / 3).integer());  // -3

// 给字符串添加一个 trim 方法
String.method('trim', function(){
    return this.replace(/^\s+|\s+$/g, '');
});

document.writeln('"' + " neat ".trim() + '"');

基本类型的原型是公共的结构,所以在类库混用时需要判断,只在确定没有该方法时才添加它。

// 有条件的增加一个方法
Function.prototype.method = function(name, func){
    if(!this.prototype[name]){
        this.prototype[name] = func;
    }
    return this;
};

2. 变量作用域

JavaScript只有函数作用域,而不支持会计作用域。那意味着定义在函数中德参数和变量在函数外部是不可见的,而且在一个函数中的任何位置定义的变量在该函数中的任何地方都可见。

3. 闭包

作用域的好处是内部函数可以访问定义它们的外部函数的参数和变量。一个更有趣的情形是内部函数拥有比它的额外部函数更长的生命周期。

// 此函数定义了一个value变量,该变量对increment和getValue方法总是可用的,
// 但函数的作用域使得它对其他的程序来说是不可见的

// 该函数将返回一个包含两个方法的对象。
var myObject = function(){
    var value = 0;
    return {
        increment: function(inc) {
            value += typeof inc === 'number' ? inc : 1;
        },
        getValue: function(){
            return value;
        }
    };
}();

下面是一个更有意义的函数。

// 创建一个名为quo的构造函数
// 它构造出带有get_status方法和status私有属性的一个对象。
var quo = function(status){
    return {
        get_status: function(){
            return status;
        }
    };
};

var myQuo = quo('amazed');
document.writeln(myQuo.get_status());
当我们调用quo时,它返回包含get_status方法的一个新对象。该对象的一个引用保存在myQuo中。即使quo已经返回了,但get_status方法仍然享有访问quo对象的status属性的特权。get_status方法并不是访问该参数的一个拷贝,它访问的就是该参数本身。这使可能的,因为函数可以访问它被创建时所处的上下文环境,这被称为**闭包**。
// 另一个更有用的例子
var fade = function(node){
    var level = 1;
    var step = function(){
        var hex = level.toString(16);
        node.style.backgroundColor = '#FFFF' + hex + hex;
        if(level < 15){
            level += 1;
            setTimeout(step, 100);
        }
    };

    setTimeout(step, 100);
}

fade(document.body);

理解内部函数能访问外部函数的实际变量而无需复制是很重要的:

// 糟糕的例子

// 当点击一个节点时,按照预想应该弹出一个对话框显示节点的序号
// 但它总是会显示节点的数目
var add_the_handlers = function(nodes){
    var i;
    for (i = 0; i < nodes.length; i += 1) {
        nodes[i].onclick = function(){
            alert(i);
        }
    };
};

add_the_handlers 函数目的是给每个事件处理器一个唯一值(i)。它未能达到目的是因为事件处理器函数绑定了变量i,而不是函数在构造时的变量i的值。

// 更好的例子
var add_the_handlers_bb = function(nodes){
    var i;
    for (i = 0; i < nodes.length; i += 1) {
        nodes[i].onclick = function(b){
            return function(){
                alert(b);
            };
        }(i);
    };
};

现在,我们定义了一个函数并立即传递i进去执行,而不是把一个函数赋值给onclick。那个函数将返回一个事件处理器函数。这个事件处理器函数绑定的是传递进去的i的值,而不是定义在add_the_handlers函数里的i的值。那个被返回的函数将被赋值给onclick。

4. 模块

我们可以使用函数和闭包来构造模块。模块是一个提供接口却隐藏状态与实现的函数或对象。

以下是一个模块的例子,它的任务是寻找字符串中的HTML字符实体并替换为它们对应的字符。它保存字符实体的名字和它们对应的字符放入一个闭包中。

String.method('deentityify', function(){
    // 字符实体表,它映射字符实体的名字到对应的字符。
    var entity = {
        quot: '"',
        lt: '<',
        gt: '>'
    };

    return function(){
        // 这才是 deentityify方法
        return this.replace(/&([^&;]+);/g, 
                function(a,b){
                    var r = entity[b];
                    return typeof r === 'string' ? r : a;
                }
            );
    };

}());

document.writeln('&lt;&quot;&gt;'.deentityify()); // <">

请注意最后一行,我们用()运算符立即调用我们刚刚构造出来的函数。这个调用所创建并返回的函数才是deentityify方法。在这个例子中,只有deentityify方法有权访问字符实体表这个数据对象。

模块模式的一般形式是:一个定义了私有变量和函数的函数;利用闭包创建可以访问私有变量和函数的特权函数;最后返回这个特权函数,或者把它们保存到一个可访问到的地方。

由此,我们可以使用模块模式产生安全的对象,类似JavaBean的形式:

var serial_maker = function(){

    var prefix = '';
    var seq = 0;
    return {
        set_prefix: function(p){
            prefix = String(p);
        },
        set_seq: function(s){
            seq = s;
        },
        gensym: function(){
            var result = prefix + seq;
            seq += 1;
            return result;
        }
    };
};

var seqer = serial_maker();
seqer.set_prefix('Q');
seqer.set_seq(1000);
var unique = seqer.gensym();

JavaScript 规范编程笔记:函数(一)

一般来说,所谓编程就是将一组需求分解成一组函数与数据结构的技能。

JavaScript中最好的特性就是它对函数的实现,它几乎无所不能。函数包含子组语句,它们是JavaScript的基础模块单元,用于代码复用、信息隐藏和组合调用。函数指定对象的行为。

1. 函数对象

在JavaScrpit中函数也是对象。对象字面量产生的对象连接到Object.prototype上,而函数对象连接到Function.prototype上(该原型对象本身连接到Object.prototype)。每个函数在创建时附有两个附加的隐藏属性:函数的上下文和实现函数行为的代码(JavaScript创建一个函数对象时,会给该对象设置一个“调用”属性)。

每个函数对象在创建时也随带有一个prototype属性。它的值是一个拥有constructor属性且值即为该函数的对象。

因为函数是对象,所以它们可以像任何其他的值一样被使用。函数可以存放在变量、对象和数组中,函数可以被当作参数传递给其他函数,函数也可以再返回函数。而且,因为函数是对象,所以函数可以拥有方法。

2. 函数字面量

// 创建一个名为 add 的变量,并用来把两个数字相加的函数赋值给它
var add = function (a, b){
    return a + b;   
};

函数字面量可以出现在任何允许表达式出现的地方。函数也可以被定义在其他函数中。一个内部函数自然可以访问自己的参数和变量,同时它也能方便的访问它被嵌套在其中的那个函数的参数与变量。通过函数字面量创建的函数对象包含一个连到外部上下文的连接。这被称为闭包

3. 调用

在JavaScript中一共有四种调用模式:方法调用模式、函数调用模式、构造器调用模式和apply调用模式。调用函数时,除了声明时定义的形式参数,每个函数接收两个附加的参数:this和arguments。参数this在面向对象变成中非常重要,它的值取决于调用的模式。调用运算符是跟在任何产生一个函数值的表达式之后的一对圆括号。圆括号中包含了参数(如果有的话),当实际参数的个数过多时,超出的参数值将被忽略,如果实际参数值过少,缺失的值将会被替代为undefined。

方法调用模式

当一个函数被保存为对象的一个属性时,我们称它为一个方法。当一个方法被调用时,this被绑定到该对象。

// 常见 myObject,它有一个value属性和一个increment方法
// increment方法接受一个可选的参数。如果参数不是数字,那么默认使用数字1。
var myObject = {
    value: 0,
    increment: function(inc){
        this.value += typeof inc === 'number' ? inc : 1;
    }
};

myObject.increment();
document.writeln(myObject.value); // 1

myObject.increment(2);
document.writeln(myObject.value); // 3

this到对象的绑定发生在调用的时候,这个“超级”迟绑定使得函数可以对this高度复用。通过this可取得它们所属对象的上下文的方法称为公共方法

函数调用模式

当一个函数并非一个对象的属性时,那么它被当作一个函数来调用:

var sum = add(3, 4);   // sum 为 7

当函数以此模式调用时,this被绑定到全局对象。这是一个设计错误,这个错误的后果是方法不能利用内部函数来帮助它工作。解决方法是:如果该方法定义一个变量并给它赋值为this,那么内部函数就可以通过哪个变量访问到this。按照约定,我给这个变量命名为that:

// 给myObject增加一个double方法。
myObject.double = function(){
    var that = this; // this指向全局变量的解决方法

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

    helper(); // 以函数的形式调用helper。
};

// 以方法的形式调用double。
myObject.double();
document.writeln(myObject.value); // 6

构造器调用模式

尽管原型继承有着强大的表现力,但它并不被广泛理解。JavaScript本身对其原型的本质也缺乏信心,所以它提供了一套和基于类的语言类似的对象构建语法。

如果在一个函数前面带上new来调用,那么将创建一个隐藏连接到该函数的prototype成员的新对象,同时this将会被绑定到那个新对象上。

// 创建一个名为Quo的构造器函数,它构造一个带有status属性的对象。
var Quo = function(string){
    this.status = string;
};

// 给Quo的所有实例提供一个名为get_status的公共方法。
Quo.prototype.get_status = function(){
    return this.status;
};

// 构造一个Quo实例
var myQuo = new Quo("confused");

document.writeln(myQuo.get_status()); // 令人困惑

结合new前缀调用的函数被称为构造器函数。按照约定,它们保存在以大写格式命名的变量里。如果调用构造器函数时没有在前面加上new,可能会发生非常糟糕的事情,既没有编译时警告,也没有运行时警告,因此大写约定非常重要。不推荐使用这种形式的构造器函数。

Apply 调用模式

因为JavaScript是一门函数式的面向对象编程语言,所以函数可以拥有方法。apply方法让我们创建一个参数数组并用其去调用函数。它允许我们选择this的值。apply方法接收两个参数,第一个将被绑定给this的值,第二个就是一个参数数组。

var array = [3, 4];
var sum = add.apply(null, array); // 7

var statusObject = {
    status: 'A-OK'
};

// get_status 没有参数,statusObject指定了将被绑定给this的值
var status = Quo.prototype.get_status.apply(statusObject);
document.writeln(status); // A-OK

4. 参数

当函数被调用时,会得到一个“免费”奉送的参数,那就是 arguments 数组。通过它函数可以访问所有它被调用时传递给它的参数列表,包括那些没有被分配给函数声明时定义的形式参数的多余参数。这使得编写一个无须指定参数个数的函数成为可能:

// 构造一个将很多个值想加的函数
var sum = function(){
    var i, sum = 0;
    for(i = 0; i < arguments.length; i += 1){
        sum += arguments[i];
    }   
    return sum;
};

document.writeln(sum(4, 8, 15, 16, 23, 42));  // 108

因为语言的一个设计错误,arguments并不是一个真正的数组,arguments拥有一个length属性,但它缺少所有数组方法。

5. 返回

return 语句可用来使函数提前返回。当return被执行时,函数立即返回而不再执行余下的语句。一个函数总是会返回一个值。如果没有指定返回值,则返回undefined。

6. 异常

JavaScrpit提供了一套异常处理机制。throw语句中断函数的执行,它应该抛出一个exception对象,该对象包含可识别异常类型的name属性和一个描述性的message属性。你也可以添加其他的属性。

var add_throw = function(a,b){
    if(typeof a !== 'number' || typeof b !== 'number'){
        throw {
            name: 'TypeError',
            message: 'add needs numbers'
        };
    }
    return a + b;
};

该exception对象将被传递到一个try语句的catch从句:

var try_it = function(){
    try {
        add_throw('sever');
    } catch (e) {
        document.writeln(e.name + ': ' + e.message);
    }
};

try_it();

一个try语句只会有一个将捕获所有异常的catch代码块。如果你的处理手段取决于异常的类型,那么异常处理器必须检查异常对象的name属性以确定异常的类型。

成熟就是当你不再相信爱情的那一刻

成熟就是当你不再相信爱情的那一刻

周六那天天气阴暗无比,一如我那时的心情。当那个哭泣的电话响彻在耳边时,我眩晕到不行,我都做了些什么,我都做了些什么?之前的晚上我就已经知道,灾难即将到来,我的灾难,父母的灾难,但是即使我花费一个通宵的时间去准备应对,也是无法应对的。因为无解!

于是我对自己说:“当悲剧发生,痛苦来临,每个人都觉得他(她)是最痛苦的那个,其实如果让他(她)拿自己的痛苦与别人的痛苦交换,却会变得犹豫不决。痛苦是无法比较的,痛苦没有大小只有长短。”也只是惘然。人生必然要面对各种各样的灾难,但我却制造做绝的灾难,抛给亲者,使亲者痛仇者快。

面对各种选择我选择了最悲情的一个,结果是惨痛的,在这惨痛的结果面前,爱情显得一文不值。

原来爱情一直以来只是我自己的坚持,事到临头之时,没有人提过她,没有人想到她,我自己也不敢提她,我在想提她一定会被笑掉大牙的,爱情在别人眼里根本不值一提,这就是我坚持的东西,我甚至开始不相信自己,不再相信这么多年那么认真包裹在内心中的她——爱情。

成熟就是当你不再相信爱情的那一刻!

可是我还是说:如果你说你还爱我,我就不让你走...