陳鍾誠

Version 1.0

JavaScript 的物件導向

最簡單的物件

範例: people.js

var People={ 
  name:"john", 
  age:30,
  print:function() {
    console.log("name=", this.name, "age=", this.age);
  } 
}

People.print();

執行結果:

$ node people.js 
name= john age= 30

圓形物件

範例: circle.js

var circle = {
  r:3, 
  area:function() {
    return 3.14*this.r*this.r;
  }
}

console.log("circle.r=%d", circle.r);

console.log("circle.area()=%d", circle.area());

執行結果

NQU-192-168-60-101:ccc csienqu$ node circle.js
circle.r=3
circle.area()=28.259999999999998

圓形類別 (ES6 新語法)

class Circle {
  constructor(r) {
    this.r = r
  }
  area() {
    return 3.14*this.r*this.r;
  }
}

var c1 = new Circle(1)
console.log("c1.r=%d", c1.r);
console.log("c1.area()=%d", c1.area());
var c2 = new Circle(2)
console.log("c2.area()=%d", c2.area());

執行結果

csienqu-teacher:object csienqu$ node circleClass
c1.r=1
c1.area()=3.14
c2.area()=12.56

複數的範例

ES6 新版的類別寫法

檔案: ComplexClass.js

 class Complex {
    constructor(r,i) {
        this.r = r; 
        this.i = i;
    }

    add(c2) {
        return new Complex(this.r+c2.r, this.i+c2.i);
    }
    
    sub(c2) {
        return new Complex(this.r-c2.r, this.i-c2.i);
    }
    
    mul(c2) {
        return new Complex(this.r*c2.r-this.i*c2.i, 
                           this.r*c2.i+this.i*c2.r);
    }
    toString() { 
        return this.r+"+"+this.i+"i" 
    }
}

var a=new Complex(1,2), b=new Complex(2,1);

var x = a.add(b).sub(b).mul(b);

console.log("a=%s", a);
console.log("b=%s", b);
console.log("a.add(b)=%s", a.add(b));
console.log("a.sub(b)=%s", a.sub(b));
console.log("a.mul(b)=%s", a.mul(b));
console.log("x=%s", x);

執行結果:

$ node ComplexClass.js 
a=1+2i
b=2+1i
a.add(b)=3+3i
a.sub(b)=-1+1i
a.mul(b)=0+5i
x=0+5i

不管你總共建立了多少 Complex 物件, Complex 的 prototype 都只會有一份,因此這種寫法會比前一種寫法更省記憶體。

而這種寫法,也是最經典的 javascript 物件導向寫法。

在一般的物件導向語言裡,會有《繼承》的機制,但是在 javascript 的早期版本 (ES6 之前) 裡面,並沒有《繼承》的機制。

但是、javascript 仍然可以做到類似《繼承》的功能,方法是在原型裏再塞入原型,這種原型裡面還可以有原型的做法,真的是非常美妙的一種設計阿!

習題

1 請寫出一個具有加減乘除運算的複數物件? (Complex, add, sub, mul, div) (除法可以不寫,算加分題)

提示:第一題請參考本章內文

2 請寫出一個具有『加、減、內積、負』的向量物件? (Vector, add, sub, dot, neg)

提示:第二題架構如下:

class Vector {
  add(v2) { ... }
  sub(v2) { ... }
  dot(v2) { ... }
  neg() { ... }
}

3 請寫一組物件,包含《矩形、圓形》與抽象的形狀,每個物件都具有 area() 函數可以計算其面積? (Shape.area(), Rectangle, Circle)

提示:第三題架構如下:

class Shape {
}

class Circle {
  constructor(radius) {...}
  area() { ... }
}

class Rectangle {
  constructor(width, height) {...}
  area() { ... }
}

4 請寫一組物件,包含『浮點數,有理數,複數』,這三個都繼承『數』這個物件,而且每個都具有 add, sub, mul, div, power 等成員函數!

提示:第四題的架構如下:

class Number {
  power(n) {
    var p = this;
    for (var i=1; i<n; i++) {
      p = p.mul(this);
    }
    return p;
  }
}

class Float extends Number {
  add(o2) { ...}
  sub(o2) { ...}
  mul(o2) { ...}
  div(o2) { ...}
  toString() {... }
}

class Rational extends Number {
  add(o2) { ...}
  sub(o2) { ...}
  mul(o2) { ...}
  div(o2) { ...}
  toString() {... }
}

class Complex extends Number {
  add(o2) { ...}
  sub(o2) { ...}
  mul(o2) { ...}
  div(o2) { ...}
  toString() {... }
}

第四題不含有理數與浮點數,只有 Number 和 Complex 的版本如下:

class Number {
    power(n) {
        var p = this;
        for (var i=1; i<n; i++) {
            p = p.mul(this);
        }
        return p;
    }
 }
 
 class Complex extends Number {
    constructor(r,i) {
        super(r,i);
        this.r = r; 
        this.i = i;
    }

    add(c2) {
        return new Complex(this.r+c2.r, this.i+c2.i);
    }
    
    sub(c2) {
        return new Complex(this.r-c2.r, this.i-c2.i);
    }
    
    mul(c2) {
        return new Complex(this.r*c2.r-this.i*c2.i, 
                           this.r*c2.i+this.i*c2.r);
    }
    toString() { 
        return this.r+"+"+this.i+"i" 
    }
}

var a=new Complex(1,2), b=new Complex(2,1);

var x = a.add(b).sub(b).mul(b);

console.log("a=%s", a);
console.log("b=%s", b);
console.log("a.add(b)=%s", a.add(b));
console.log("a.sub(b)=%s", a.sub(b));
console.log("a.mul(b)=%s", a.mul(b));
console.log("x=%s", x);

console.log("a.power(3)=%s", a.power(3));