CoffeeScript—面向對象

概述

自從面向對象的編程思想出現以來,這個概念已經被炒爛了,只要編程開發你們都會拿面向對象來講事,好像只要跟面向對象沾邊就會顯得逼格很高同樣,不過確實逼格提升了。要知道,面向對象只是一種手段,最終目的是爲了提升咱們項目的重用性、靈活性和擴展性。前端

爲了迎合面向對象的程序設計思想,JavaScript也經過本身的語法實現了本身的一套面向對象機制。不過我想問下,前端開發當中有多少人使用過面向對象當中的繼承?程序員

JavaScript面向對象的實現確實有點不三不四的感受。下面先簡單說明下JavaScript當中面向對象的實現,涉及的東西比較深,要對constructor、prototype有必定的理解,否則看起來會很吃力,或者你也能夠跳開這部份內容,直接看CoffeeScript面向對象的實現。編程

JavaScript面向對象編程

JavaScript的實現一個類,能夠採用下面的方式:後端

function Animal(name) {
    this.name = name;
}

Animal.prototype.printName = function () {
    console.log(this.name);
};

var animal = new Animal('animal');

animal.printName();

C++、Java...某些程序員可能就吐槽了,我TM都連個關鍵字class都沒看到,這就是類了?app

圖片描述

做爲一個前端開發,我竟無言以對。。。函數

繼承

選取JavaScript當中幾種繼承方式,做一個簡單的說明,想要學習更深的內容,請經過搜索引擎吧。學習

對象冒充

function Animal(name) {
    this.name = name;

    this.printName = function () {
        console.log(this.name);
    };
}


function Cat(name) {
    this.inherit = Animal;
    this.inherit(name);

    //Animal.call(this, name);
    //Animal.apply(this, [name]);
}

var cat = new Cat('cat');

cat.printName();//cat

註釋是經過call和apply實現的方式,其實本質是同樣的。繼承實現了,閒着無聊,咱們來打印下:this

console.log(cat instanceof Animal);//false

此次別說後端開發看不下去,我都忍不了了。這種方式只能說是經過JavaScript的語法機制,模擬實現繼承,看起來好像是那麼回事,否則怎麼叫對象冒充呢。搜索引擎

原型鏈實現

function Animal(name) {
    this.name = name;
}

Animal.prototype.printName = function () {
    console.log(this.name);
};

function Cat() {
}

Cat.prototype = new Animal('cat');

var cat = new Cat();

cat.printName();//cat

打印:spa

console.log(cat instanceof Animal);//true

此次對了,cat也是Animal類的實例了,有點面向對象的的意思了。我又閒着無聊了(約麼?),再來打印

console.log(cat.constructor); //[Function: Animal]

咦,又不對了,爲啥cat的constructor指向Animal,不該該指向Cat麼?

問題出在Cat.prototype = new Animal('cat')上,直接給prototype賦值,改變了constructor的指向,這個時候,咱們還要作個處理

function Animal(name) {
    this.name = name;
}

Animal.prototype.printName = function () {
    console.log(this.name);
};

function Cat() {
}

Cat.prototype = new Animal('cat');
Cat.prototype.constructor = Cat;

var cat = new Cat();


console.log(cat.constructor); //[Function: Cat]

可是又有人吐槽了,new Cat()爲啥把名稱放到new Animal()裏面,看起來太奇怪了,綜合一下就有了第三中——混合型。

實際上instanceof的判斷原理是跟原型鏈是相關的。你們自行腦洞!

混合實現

function Animal(name) {
    this.name = name;
}

Animal.prototype.printName = function () {
    console.log(this.name);
};

function Cat(name) {
    Animal.call(this, name);
}

Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;

var cat = new Cat('cat');

cat.printName();//cat

看起來舒服點了,也就是舒服那麼一點點。

多態

直接拿混合型的繼承來講明瞭,這個比較簡單

function Animal(name) {
    this.name = name;
}

Animal.prototype.printName = function () {
    console.log(this.name);
};

function Cat(name) {
    Animal.call(this, name);
}

Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;

Cat.prototype.printName = function () {
    console.log('The name is:' + this.name);
};

var cat = new Cat('cat');
cat.printName();//The name is:cat

CoffeeScript面向對象編程

CoffeeScript的面向編程的語法同JavaScript比較起來,真是天上地下。一個陽春白雪,一個下里巴人。可是有一點咱們要記住:CoffeeScript只是編譯到JavaScript,它只是在JavaScript的基礎上進行了語法的抽象,本質上仍是JavaScript

CoffeeScript採用class關鍵字聲明類,整個語法看起來更加簡明流暢。

#編譯前
class Animal
  constructor: (name)->
    @name = name
  printName: ->
    console.log(@name)

animal = new Animal 'animal'

animal.printName() #animal


#編譯後
var Animal, animal;

Animal = (function () {
    function Animal(name) {
        this.name = name;
    }

    Animal.prototype.printName = function () {
        return console.log(this.name);
    };

    return Animal;

})();

animal = new Animal('animal');

animal.printName();

constructor是構造函數,就上面的例子來講,還能夠簡寫,實際上效果是同樣的

class Animal
  constructor: (@name)->
  printName: ->
    console.log(@name)

animal = new Animal 'animal'

animal.printName() #animal

CoffeeScript將咱們習慣性的書寫方式變成豐富的語法糖。說到這裏我就想說一句了,能不能把構造函數換個字符,constructor丫太長了。

繼承

繼承使用的是extends關鍵字

#編譯前
class Animal
  constructor: (@name)->
  printName: ->
    console.log(@name)

class Cat extends Animal

cat = new Cat 'cat'

cat.printName() #cat


#編譯後
var Animal, Cat, cat,
    extend = function (child, parent) {
        for (var key in parent) {
            if (hasProp.call(parent, key)) child[key] = parent[key];
        }
        function ctor() {
            this.constructor = child;
        }

        ctor.prototype = parent.prototype;
        child.prototype = new ctor();
        child.__super__ = parent.prototype;
        return child;
    },
    hasProp = {}.hasOwnProperty;

Animal = (function () {
    function Animal(name) {
        this.name = name;
    }

    Animal.prototype.printName = function () {
        return console.log(this.name);
    };

    return Animal;

})();

Cat = (function (superClass) {
    extend(Cat, superClass);

    function Cat() {
        return Cat.__super__.constructor.apply(this, arguments);
    }

    return Cat;

})(Animal);

cat = new Cat('cat');

cat.printName();

extend函數解析

咱們簡單分析下編譯後的extend函數,對JavaScript原型鏈不是很熟的能夠跳過這段。兩個參數分別是子類child和父類parent,第一段代碼:

for (var key in parent) {
    if (hasProp.call(parent, key)) child[key] = parent[key];
}

這段代碼就是將父類上面的屬性拷貝到子類上,由於JavaScript當中函數也是對象,能夠擴展屬性的。什麼意思?看代碼

class Animal
  constructor: (@name)->
  printName: ->
    console.log(@name)

Animal.prop = 'Animal prop'

class Cat extends Animal

console.log Cat.prop #Animal prop

第二段代碼:

function ctor() {
    this.constructor = child;
}

ctor.prototype = parent.prototype;
child.prototype = new ctor();

可能你們看不大明白,我稍微改動下,換種寫法

child.prototype = new parent();
child.prototype.constructor=child;

這裏就是咱們上面提到的原型鏈繼承。再看最後段代碼:

child.__super__ = parent.prototype;

這裏是爲了在子類中調用父類的方法,實現多態,看下面的例子就知道了。

多態

編譯後的代碼太長,就不粘貼了,看CoffeeScript代碼更易於學習。

直接重寫父類方法

class Animal
  constructor: (@name)->
  printName: ->
    console.log(@name)


class Cat extends Animal
  printName: ->
    console.log 'Cat name:' + @name

cat = new Cat 'cat'

cat.printName() #Cat name:cat

重寫父類方法,在重寫的方法中調用父類方法

class Animal
  constructor: (@name)->
  move: (meter)->
    console.log(meter)


class Cat extends Animal
  move: ->
    console.log 'Cat move'
    super 4

cat = new Cat 'cat'

cat.move() #Cat move 4

有任何問題,歡迎你們批評指出,咱們共同進步。

相關文章
相關標籤/搜索