oo的一些概念

http://docs.kissyui.com/5.0/guides/base/oo.html

 

JavaScript 语言自成体系,自有一套代码重用的模式,这些常见的代码重用模式可以在《Javascript 设计模式》、《JavaScript 编程模式》中找到理论基础。KISSY 即是实践了这些典型的代码重用模式,并作为 KISSY 代码库的面向对象的核心,向高层代码提供语言的完备性支撑。

这些理论基础大都来自"javascript 编程模式"一书:

KISSY 中面向对象的范畴包含两个方面,本篇讲解第一方面

  • 语言层面:JS 语言特有的编程模式
    • 面向对象相关的模式
    • JS 语言增强的工具函数
  • 最佳实践:KISSY 对于面向UI、模块生命周期的封装,包括组件模式、插件模式等

概要

JavaScript 语言没有原生类的概念,对象之间共享方法的关键渠道是通过原型。而具备某一类原型方法的'模板对象',通常被称作基类,子类除了调用基类的构造器(手动调用)外,最重要的就是要拥有基类的行为能力(继承基类原型上的方法)。这个过程中涉及到五种典型的行为:

  • 混合 mix
  • 克隆 clone
  • 扩充 augment
  • 继承 extend
  • 合并 merge

以 kissy 的 API 为例,merge 和 augment 都是基于 mix 方法,本质上利用了 JS 的动态特性,在运行时为对象增减成员;

extend 方法的实现比较典型,总体思路是子构造器的原型对象指向父构造器的一个实例,具体有一些细节问题要处理。类继承体系中继承的是对象的模板(即类),JS 没有对于对象的更高一层的抽象数据结构,即使有 constructor 这种东西,它本身也只是 function 对象而已。

mix

mix (receiver , supplier [ , overwrite = true , whitelist , deep ]) => Object

将 supplier 对象的成员复制到 receiver 对象上。参数:

  • receiver (object) – 属性接受者对象.
  • supplier (object) – 属性来源对象.
  • overwrite (boolean) – 是否覆盖接受者同名属性.
  • whitelist (Array) – 属性来源对象的属性白名单, 仅在名单中的属性进行复制.
  • deep (boolean) – 是否进行深度 mix (deep copy)

mix 默认不是递归进行的. 如果其中一个属性为对象或者数组,那么他将会被接下来对象的同名属性对应的值所代替,即值不会被合并。 如果设置了参数 deep = true ,那么会对数组和简单对象KISSY.isPlainObject()递归合并.

supplier undefined 的属性值不会被复制,不过对象从原型继承下来下的值则会被复制.

该方法仅适用于 javascript 对象,不要在浏览器对象上调用,例如 node.style

简单 mix

require(['util'], function(Util){
    var r = { a: 'a', b: 'b' };
    Util.mix(r, { c: 'c' });
    console.log(r.c); // => 'c'

    Util.mix(r, { a: 'a2' }, false);
    console.log(r.a); // => 'a'

    Util.mix(r, { e: 'e', f: 'f' }, true, ['f']);
    console.log(r.e); // => undefined
    console.log(r.f); // => 'f'
})

深度mix

var object1 = {
  apple: 0,
  banana: {weight: 52, price: 100},
  cherry: 97
};
var object2 = {
  banana: {price: 200},
  durian: 100
};

/* merge object2 into object1, recursively */
Util.mix(object1,object2,undefined,undefined,true);

console.log(object1); // => { apple: 0, banana: { weight: 52, price: 200 }, cherry: 97, durian: 100 }

mix是最自然也是最简单的为 JS 对象添加特性的方式,具体实现就是将一个对象的(所有或指定)属性指向给另一个对象,在静态语言中是无能为力的。

虽然 Yahoo 将这种方式学院式地命名为“augmentObject”并且声称使用它的合适场合是在“扩充一个‘静态类’”时。所谓“静态类”这种说法,只能说是中静态类语言的遗毒太深,什么概念都要一一映射至其上。事实上,“静态类”对应 JS 中的概念是“非构造器对象”,比如字面量对象。而之所以 Yahoo 强调“静态类”,那是因为“静态类”和构造器之间间有一个显区别,前者没有 prototype 属性。

补充一点,“静态类”也可以是构造器,只不过它的 prototype 没有多大意义(比如指向 {})。

事实上,这个方法是 augment 和 merge 的基础。可以看到,JS 中直接对对象进行各种操作其实非常方便,类什么的都是浮云。

merge

merge (s1,s2[,...]) => Object

将多个对象的成员合并到一个新对象上. 参数中, 后面的对象成员会覆盖前面的。如果用mix混合对象时,receiver 会被改变,如果想要保留原始的 receiver ,可以使用 KISSY.merge()

var object=Util.merge(object1,object2);

简单例子:

var a = { a: 'a' },
b = { b: 'b' },
c = { b: 'b2', c: 'c' };

var o = Util.merge(a, b, c);
console.log(o.a); // => 'a'
console.log(o.b); // => 'b2'
console.log(o.c); // => 'c'

简单情况下 merge 方法常用来合并配置信息. 推荐使用 Base 处理属性配置.

extend

extend (r,s[,px,sx]) => Function

让函数对象 r 继承函数对象 s,参数

  • r (function) – receiver,将要继承的子类函数
  • s (function|object) – supplier,继承自的父类函数
  • px (object) – prototype members, 需要添加/覆盖的原型成员
  • sx (object) – static members, 需要添加/覆盖的静态成员.

    function Bird(name) { this.name = name; } Bird.prototype.fly = function() { alert(this.name + ' is flying now!'); };

    function Chicken(name) {

      Chicken.superclass.constructor.call(this, name);
    

    } Util.extend(Chicken, Bird,{

      fly:function(){
          Chicken.superclass.fly.call(this)
          alert("it's my turn");
      }
    

    });

    new Chicken('kissy').fly();

extend 方法是 KISSY 里类继承的实现方式. 书写 JavaScript 代码时, 请忘记传统 OO 里的继承体系。子类方法中可通过 superclass 来访问父类函数的原型, 进而调用父类方法.

Util.extend 像 Node.js 里的 util.inherits,就是用于声明两个类的继承关系,与 util.inherits 相比,它更为贴心,还会维护 superclass 和 superclass.constructor。

注意构造函数体内,通过 Chicken 类上的 superclass 属性,子类不再需要显式写明父类的名称, 只需要直接调SubClass.superclass.constructor.call(this, attrs) 即可。

而在方法内,也可以通过 SubClass.superclass 拿到父类上的方法,类似其他编程语言中的 super 之类。

augment

augment(r, s1 [, s2 , ...], ov = true, wl) => Function

将 s1,s2.... 的 prototype 属性的成员复制到 r.prototype 上。这时被复制的成员来自于一个Fucntion对象,这个对象一般被称为掺元类(mixin class)。比如 KISSY 里的 CustemEvent 就是一个掺元类。掺元类只是被扩充用的。参数说明:

  • r (function) – 将要扩充的函数
  • ...s1 (function|object) – 扩充来源函数或对象. 非函数对象时复制的就是 s 的成员.
  • ov (boolean) – 是否覆盖 r.prototype 同名属性.
  • whitelist (Array) – 属性来源对象的属性白名单, 仅在名单中的属性进行复制.
var Shoutable = {
    shout: function() { alert('I am ' + this.name + '.'); }
};

function Dog(name) { this.name = 'Dog ' + name; }
function Pig(name) { this.name = 'Pig ' + name; }

Util.augment(Dog, Utilhoutable);
Util.augment(Pig, Shoutable);

new Dog('Jack').shout(); // => I am Dog Jack.
new Pig('Mary').shout(); // => I am Pig Mary.

augment 方法在 KISSY 里非常基础非常重要. 传统 OO 语言里, 可以通过继承或接口来实现共性方法. 在 JavaScript 里, 通过 mixin 特性, 一切变得更简单. augment 是动态语言 mixin 特性的体现, 灵活运用, 能让代码非常优雅简洁.

clone

对象的值的拷贝,绕过引用的拷贝。


有了 Util.augment,我们可以很方便得扩展类的原型;有了 Util.extend,我们可以很方便地继承;那么 KISSY 对属性 getter、setter 有什么好的解决方案么?答案自然是 Base。

顾名思义,Base 是个基础类;而这个类,也是通过 Util.augment 等搞定的。在Base小节讲解。

posted on 2015-06-22 19:37  dhj  阅读(448)  评论(0编辑  收藏  举报

导航