JavaScript設計模式基礎-----封裝、繼承、多型
阿新 • • 發佈:2019-01-10
// 函式的基本形式 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));