1. 程式人生 > >JavaScript設計模式基礎-----封裝、繼承、多型

JavaScript設計模式基礎-----封裝、繼承、多型

// 函式的基本形式
function checkName() {
    "use strict";

}
function checkEmail() {
    "use strict";

}
function checkPassword() {
    "use strict";

}

// 函式的另一種形式
var checkName = function () {
    "use strict";

}
var checkEmail = function () {
    "use strict";

}
var checkPassword = function () {
    "use strict";

}

// 上面兩種等於聲明瞭全域性變數
// 這個從功能上講是沒有問題的,但是如果別人也定義了同樣的方法就會覆蓋掉這個函式

/*
 * 用物件收編變數
 * */
var CheckObject = {
    checkName: function () {
        "use strict";

    },
    checkEmail: function () {
        "use strict";

    },
    checkPassword: function () {
        "use strict";

    }
}

// 將所有的函式作為checkObject的方法,這樣就只有一個物件 用法:CheckObject.checkName()

/*
 * 物件的另一種形式
 * */
var CheckObject = function () {

}
CheckObject.checkName = function () {
    "use strict";

}
CheckObject.checkEmail = function () {
    "use strict";

}
CheckObject.checkPassword = function () {
    "use strict";

}
//用法:CheckObject.checkName()  不好處就是比人不能複製你的程式碼,這個物件在用new關鍵字建立新物件的時候新建立的物件是不能繼承這些方法的

/*
 * 實現簡單複製
 * */
var CheckObject = function () {
    "use strict";
    return {
        checkName: function () {
            "use strict";

        },
        checkEmail: function () {
            "use strict";

        },
        checkPassword: function () {
            "use strict";

        }
    }
}
// 這樣每個人用的時候都是返回一個新物件 通過new 例項化的時候 互不影響
// 用法:var a= CheckObject(); a.checkName();

/*
 * 用類的寫法 每次new的時候新建立的物件都會有一套自己的方法,這樣很消耗記憶體
 * */
var CheckObject = function () {
    "use strict";
    this.checkName = function () {

    }
    this.checkEmail = function () {

    }
    this.checkPassword = function () {

    }
}
// 用法:var a = new CheckObject(); a.checkPassword();

/*
 這個物件創建出來的方法就都是相同的,因為他們都需要一來prototype原型一次查詢
 * */
var CheckObject = function () {
    "use strict";

}
CheckObject.prototype.checkName = function () {
    "use strict";

}
CheckObject.prototype.checkEmail = function () {

}
CheckObject.prototype.checkPassword = function () {

}
//簡寫
var CheckObject = function () {
}
CheckObject.prototype = {
    checkName: function () {
        "use strict";

    },
    checkEmail: function () {
        "use strict";

    },
    checkPassword: function () {
        "use strict";

    }
}
// 用法:var a = new CheckObject(); a.checkName();
// =================== 注意==========================
// 這兩種方法不能一起用 一起用的話 後面的prototype 方法會覆蓋前面的

var CheckObject = {
    checkName: function () {
        "use strict";
        return this;
    },
    checkEmail: function () {
        "use strict";
        return this;
    },
    checkPassword: function () {
        "use strict";
        return this;
    }
}
// 鏈式呼叫
// CheckObject.checkName().checkEmail();
// 也可以放到原型中
var CheckObject = function () {
    "use strict";

}
CheckObject.prototype = {
    checkName: function () {
        "use strict";
        return this;

    },
    checkEmail: function () {
        "use strict";
        return this;

    },
    checkPassword: function () {
        "use strict";
        return this;
    }
}
// 用法:var a = new CheckObject(); a.checkName().checkEmail();

/*
 為函式的原型新增方法
 * */
Function.prototype.addMethod = function (name, fn) {
    this[name] = fn; // 如果要新增到prototype上 使用的時候要使用new 關鍵字 this.prototype[name]=fn; 使用的時候要使用new
    return this;
}
var methods = new function ();
methods.addMethod('checkName', function () {
    // 如果要鏈式呼叫 返回this

}).addMethod('checkEmail', function () {

});
methods.chekcName();

// ============================ 面向物件 ============================ //

// 建立一個類
var Book = function (id, bookname, price) {
    this.id = id;
    this.bookname = bookname;
    this.price = price;
}
// 為類新增方法
Book.prototype = {
    display: function () {

    }
}

// 封裝
var Book = function (id, name, price) {
    // 私有屬性 相當於php private 定義的方法
    var num = 1;
    // 私有方法
    function checkId() {

    }

    // 特權方法  相當於php為變數設定和獲取值的方法
    this.getName = function () {

    }
    this.setName = function () {

    }
    this.getPrice = function () {

    }
    this.setPrice = function () {

    }

    // 公有屬性 相當於php的public定義的屬性和方法 【這相當於css的style 優先順序要比prototype的高】
    this.id = id;
    // 公有方法
    this.copy = function () {

    }

    // 構造器 相當於php的__construct
    this.setName(name);
    this.setPrice(price);
}
// 類外部通過 . 語法定義的屬性和方法 稱為 靜態公有屬性和方法 但是物件不能訪問 相當於php static 定義的屬性和方法 只能通過類名.屬性、方法訪問
Book.isChinese = true;
Book.resetTime = function () {
    console.log('new time')
}
// 通過prototype定義的屬性和方法稱為公有屬性和方法
Book.prototype = {
    // 公有屬性
    isJsBook: false,
    // 公有方法
    display: function () {

    }
}
// 用法 : var b= new Book(11,'JavaScript設計模式',50);

/*
 * 升級版本的封裝 function 物件的
 * */

function Person(info) {
    this._init_(info)
}
Person.prototype = {
    constructor: Person,
    _init_: function (info) {
        this.name = info.name;
        this.age = info.age;
        this.sex = info.sex;
    },
    sayHello: function () {
        console.log('hello');
    }
}

/*
 * 升級版本2  仿照jquery
 * */
var Person = (function (window) {
    var Person = function (name) {
        // 直接例項化
        return new Person.fn.init(name);
    }

    Person.fn = Person.prototype = {
        constructor: Person,
        init: function (name) {
            this.name = name;
            this.sayHello = function () {
                this.makeArray();
            }
        },
        makeArray: function () {
            console.log(this.name);
        }
    }

    Person.fn.init.prototype = Person.fn;

    return Person;
})();
/*
 var p = Person('pawn');
 console.log(p);
 p.sayHello();
 */

每個類都有三部分組成
1.
建構函式內部的這是供例項化物件複製用的
2.
建構函式外部的直接通關.語法新增的
只是類使用的
只有類.才可以訪問
3.
新增在prototype中的
例項化物件時可以通過原型直接訪問到
是給所有例項化物件所共用的

/*
 * 繼承 http://www.cnblogs.com/humin/p/4556820.html
 * */
// 定義一個動物類
function Animal(name) {
    // 屬性
    this.name = name || 'Animal';
    // 例項方法
    this.sleep = function () {
        console.log(this.name + '正在睡覺!');
    }
}
// 原型方法
Animal.prototype.eat = function (food) {
    console.log(this.name + '正在吃:' + food);
};

//1、原型鏈繼承
//核心: 將父類的例項作為子類的原型

function Cat() {
}
Cat.prototype = new Animal();
Cat.prototype.name = 'cat';

// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.eat('fish'));
console.log(cat.sleep());
//instanceof主要的目的是檢測引用型別
//typeof 是一個操作符,主要的目的是檢測一個變數是不是基本資料型別的變數
console.log(cat instanceof Animal); //true
console.log(cat instanceof Cat); //true

/*特點:

 非常純粹的繼承關係,例項是子類的例項,也是父類的例項
 父類新增原型方法/原型屬性,子類都能訪問到
 簡單,易於實現

 缺點:

 要想為子類新增屬性和方法,必須要在new Animal()這樣的語句之後執行,不能放到構造器中
 無法實現多繼承
 來自原型物件的引用屬性是所有例項共享的(詳細請看附錄程式碼: 示例1)
 建立子類例項時,無法向父類建構函式傳參
 推薦指數:★★(3、4兩大致命缺陷)*/

//2、構造繼承
//核心:使用父類的建構函式來增強子類例項,等於是複製父類的例項屬性給子類(沒用到原型)
function Cat(name) {
    "use strict";
    Animal.call(this);
    this.name = name || 'Tom';
}
// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // false
console.log(cat instanceof Cat); // true

/*特點:

 解決了1中,子類例項共享父類引用屬性的問題
 建立子類例項時,可以向父類傳遞引數
 可以實現多繼承(call多個父類物件)
 缺點:

 例項並不是父類的例項,只是子類的例項
 只能繼承父類的例項屬性和方法,不能繼承原型屬性/方法
 無法實現函式複用,每個子類都有父類例項函式的副本,影響效能
 推薦指數:★★(缺點3)*/

//3、例項繼承
//核心:為父類例項新增新特性,作為子類例項返回
function Cat(name) {
    "use strict";
    var instance = new Animal();
    instance.name = name || 'Tom';
    return instance;
}

// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); // false
/*
 特點:

 不限制呼叫方式,不管是new 子類()還是子類(),返回的物件具有相同的效果
 缺點:

 例項是父類的例項,不是子類的例項
 不支援多繼承
 推薦指數:★★*/


//4、拷貝繼承

function Cat(name) {
    var animal = new Animal();
    for (var p in animal) {
        Cat.prototype[p] = animal[p];
    }
    Cat.prototype.name = name || 'Tom';
}

// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // false
console.log(cat instanceof Cat); // true
/*特點:

 支援多繼承
 缺點:

 效率較低,記憶體佔用高(因為要拷貝父類的屬性)
 無法獲取父類不可列舉的方法(不可列舉方法,不能使用for in 訪問到)
 推薦指數:★(缺點1)*/

/*
 5、組合繼承

 核心:通過呼叫父類構造,繼承父類的屬性並保留傳參的優點,然後通過將父類例項作為子類原型,實現函式複用
 */

function Cat(name) {
    Animal.call(this);
    this.name = name || 'Tom';
}
Cat.prototype = new Animal();

// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); // true
/*特點:

 彌補了方式2的缺陷,可以繼承例項屬性/方法,也可以繼承原型屬性/方法
 既是子類的例項,也是父類的例項
 不存在引用屬性共享問題
 可傳參
 函式可複用
 缺點:

 呼叫了兩次父類建構函式,生成了兩份例項(子類例項將子類原型上的那份遮蔽了)
 推薦指數:★★★★(僅僅多消耗了一點記憶體)*/

/*
 6、寄生組合繼承

 核心:通過寄生方式,砍掉父類的例項屬性,這樣,在呼叫兩次父類的構造的時候,就不會初始化兩次例項方法/屬性,避免的組合繼承的缺點
 */

function Cat(name) {
    Animal.call(this);
    this.name = name || 'Tom';
}
(function () {
    // 建立一個沒有例項方法的類 在js 中 繼承是依賴於原型prototype鏈實現的
    var Super = function () {
    };
    Super.prototype = Animal.prototype;
    //將例項作為子類的原型
    Cat.prototype = new Super();
})();

// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); //true
/*
 特點:

 堪稱完美
 缺點:

 實現較為複雜
 推薦指數:★★★★(實現複雜,扣掉一顆星)
 */

//=============================================javascript 多繼承========================================================
// 在JavaScript中繼承是一來與prototype鏈實現的。因為只有一條原型鏈。理論上是不能繼承多個父類,但是js是很靈活的。通過一些
//技巧和方法可以實現多繼承
/**
 * 作用就是將傳入的多個物件的屬性複製到源物件中 這樣就可以實現對多個物件屬性的繼承
 * 使用的時候第一個引數----需要傳入要繼承的物件
 * @returns {*}
 */
var mix = function () {
    var i = 1,// 從第一個引數起為被繼承的物件
        len = arguments.length,// 獲取引數長度
        target = arguments[0],// 第一個物件為目標物件
        arg;// 快取引數物件
    for (; i < len; i++) {
        // 快取當前物件
        arg = arguments[i];
        // 遍歷被繼承物件中的屬性
        for (var property in arg) {
            // 將被繼承物件中的屬性複製的目標物件中
            target[property] = arg[property];
        }
    }
    return target;
};

Object.prototype.mix = function () {
    var i = 0, // 從第一個引數起為被繼承的物件
        len = arguments.length, // 獲取引數長度
        arg;// 快取引數物件
    // 遍歷被繼承的物件
    for (; i < len; i++) {
        // 快取當前物件
        arg = arguments[i];
        // 遍歷被繼承物件中的屬性
        for (var property in arg) {
            // 將被繼承物件中的屬性複製的目標物件中
            this[property] = arg[property];
        }
    }
};

var book1 = {
    name: 'book1',
    alike: ['css', 'js', 'java']
};
var book3 = {
    names: 'book1',
    alikes: ['css', 'js', 'java']
};
var book2 = {
    ccc: 'ccccccc'
};
var outhorBook = {
    color: 'red'
};
outhorBook.mix(book1, book2, book3);
console.log(outhorBook);

/*
 * ============================================= 多型 =============================================
 * 指的是同一種方法多種呼叫方式
 * */
function add() {
    // 獲取引數
    var arg = arguments,
    // 獲取長度
        len = arg.length;
    switch (len) {
        // 如果沒有傳引數
        case 0:
            return 10;
        case 1:
            return 10 + arg[0];
        case 2:
            return arg[0] + arg[1];
    }
}
console.log(add()); // 10
console.log(add(5)); // 15
console.log(add(6, 7)); // 13

// or
function Add() {
    function zero() {
        return 10;
    }

    function one(num) {
        return 10 + num;
    }

    function two(num1, num2) {
        return num1 + num2;
    }

    // 相加共有方法
    this.add = function () {
        var arg = arguments,
            len = arg.length;
        switch (len) {
            case 0:
                return zero();
            case 1:
                return one(arg[0]);
            case 2:
                return two(arg[0], arg[1])
        }
    }
}
var a = new Add();
console.log(a.add());
console.log(a.add(5));
console.log(a.add(6, 7));