摘要:介一回,偶們來聊一下用中的類,有些盆友可能用過或者的,知道語法糖,可是在中并沒有,中需要用到構造函數來模擬類。而且要注意一點,構造函數沒有語句,是自動返回。
本回內容介紹
上一回聊到JS的Function類型,做了柯里化,數組去重,排序的題。
介一回,偶們來聊一下用JS中的類,有些盆友可能用過ES6或者TypeScript的,知道Class語法糖,可是在ES5中并沒有,ES5中需要用到構造函數來模擬類。
既然是類,肯定要聊到繼承,而聊到繼承,那原型也少不了,但是,繼承、原型這些知識在網上的基礎講解已經很多了。
所以,偶就把jquery,extjs,nodejs,underscore.js的繼承源碼拿來做個比較分析,至于模擬接口蝦米的,后面會聊滴,來吧開始咯:
1. 再談對象重溫對象,還是先來個書上(高程3)的例子:
var o = { name:"飛狐", age:"21", sayName:function(){ alert(this.name); } };
這是之前講的對象,要修改屬性的特性,比如把name修改為不可更改,可能有的盆友說了,用之前聊過的對象凍結isFrozen()方法,是的,可以做到,但是防篡改的方法是作用對象定義之后,如果要修改屬性的默認特性,就得用ES5的Object.defineProperty()方法了,
2. Object.defineProperty()方法Object.defineProperty()方法接收3個參數,屬性所在對象,屬性名,描述符對象;其中描述符對象的屬性必須是:configurable,enumerable,writable,value。這是書上的描述,好像有點抽象,來吧看例子:
var o = {}; Object.defineProperty(o,"name",{ // 這個地方的name,就是創建的屬性, writable:false, // 這個地方定義為只讀,不可修改 value:"飛狐" // 默認值,沒什么好說的 }); alert(o.name); // 飛狐 o.name = "帥狐"; alert(o.name); // 飛狐
怎么樣,配上注釋,應該不難理解吧。
3. 訪問器屬性getter,settergetter,setter,這倆函數具有4個屬性(配置,枚舉,訪問,寫入),對寫過java的一定很親切吧,來吧直接看例子要更直觀些:
var o = { _name:"帥狐", feature:"帥" }; Object.defineProperty(o,"name",{ get:function(){ // 這里的get用于獲取 return this._name; }, // 這里的set用于寫入,而value就是所定義屬性name的值 set:function(value){ if(value=="飛狐"){ // 這里是修改_name的值 this._name = value; // 這里是修改屬性feature的值 this.feature = value+this.feature; } }, enumerable: true, // 可枚舉 configurable: true, // 可配置 }); o.name = "飛狐"; alert(o.feature); // 飛狐帥
這里簡單的改了一下書上的例子,聊到Object.defineProperty()就順便說一下AngularJS的雙向綁定,做一個知識的擴展吧:
AngularJS的雙向綁定受到了很多人JSer的喜愛,其中有仨方法:
$scope.$apply(),$scope.$digest(),$scope.$watch()。雙向綁定離不開這仨。
玩兒過AngularJS的盆友都知道,臟值檢測scope中的對象綁狀態,一旦發生改變,$digest就>會循環監測,調用相應的方法,$watch則監聽$digest中被監聽的對象,$apply僅僅只是進入Angular context,然后通過>$digest去觸發臟檢查。其中,$watch的源碼段是介么寫的
如下:
$watch: function (watchExp, listener, objectEquality) { //...這里有一些屬性定義,先忽略 if(!isFunction(listener)){ // 這里的compileToFn函數其實是調用$parse實例來分析監控參數,返回一個函數 var listenFn = compileToFn(listener || noop, "listener"); // 這里的watcher是個對象,fn傳入的listener watcher.fn = function(newVal, oldVal, scope) {listenFn(scope);}; }; // 這里的watchExp是傳入的監聽對象 if(typeof watchExp == "string" && get.constant) { var originalFn = watcher.fn; watcher.fn = function(newVal, oldVal, scope) { // 這里的通過對象冒充,指向當前作用域 originalFn.call(this, newVal, oldVal, scope); arrayRemove(array, watcher); }; } //...這里有一些判斷返回,也忽略 }
這里做一個最簡單的模擬,只是為了演示Object.defineProperty(),如下:
$watch: function (watchExp, listener, objectEquality) { var o = this.$$watchers[watchExp]; // 檢測的對象 Object.defineProperty(this, watchExp, { // this指向調用者 get: function () { return o; }, set: function (listener) { // 傳遞監聽函數 o.listener = listener; }, enumerable: true, configurable: true, }); };
Object.defineProperty是ES5的新玩意兒,不支持IE低版本。很多盆友又要疑惑了,那avalon框架就支持低版本又咋玩的嘞,其實是使用VBScript來實現低版本IE的兼容。
(注:以上的代碼,純屬擴展,如果感覺暈菜請跳過)
如果有盆友感興趣,那我再多帶帶寫個angular1.x源碼學習讀后感,把我讀過的Angular源碼段分享出來,O(∩_∩)O~
Object.defineProperties()方法,定義多個屬性,接收兩個對象參數,第一個是對象要操作的對象本身,第二個是要操作的對象屬性。
var o = {}; Object.defineProperties(o,{ _name:{ value:"帥狐" }, feature:{ value:"帥", writable:true // 可修改 }, name:{ get:function(){ // 這里的get用于獲取 return this._name; }, // 這里的set用于寫入,而value就是所定義屬性name的值 set:function(val){ if(val=="飛狐"){ // 這里是修改_name的值 this._name = val; // 這里是修改屬性feature的值 this.feature = val+this.feature; } } } }); alert(o.name); // 帥狐 o.name = "飛狐"; alert(o.feature); // 飛狐帥
這里光看例子可能有點抽象,沒關系,后面講設計模式,聊到觀察者模式的時候還會聊到事件。
5. 類的模擬(1) 工廠模式,這里就直接用書上的例子:
function createPerson(name,age){ var o = new Object(); o.name = name; o.age = age; o.feature = function(){ alert(this.name+"就是帥!"); }; return o; } var person = createPerson("飛狐",21); var person1 = createPerson("帥狐",19);
工廠模式雖然簡單,而且解決了創建多個相似對象的問題,卻無從識別對象的類型,因為全部都是Object,不像Date、Array等,于是乎構造函數模式應運而生。
(2) 構造函數模式,這里也直接用書上的例子:
function Person(name,age){ // 雖然沒有嚴格規定,但按照慣例,構造函數的首寫字母用大寫來區別于其他函數 this.name = name; // 直接將屬性和方法賦值給this對象 this.age = age; this.feature = function(){ alert(this.name+"就是帥!"); }; } var person = new Person("飛狐",21); var person1 = new Person("帥狐",19); // 這里返回為true,這正式構造函數優于工廠模式來創建對象之處 alert(person instanceof Person); alert(person instanceof Object);// true
構造函數沒有顯示創建對象:new Object(),但會隱式地自動new Object()。而且要注意一點,構造函數沒有return語句,是自動返回。
看到這里,構造函數已經很不錯了吧,但是:
每次創建實例的時候都要重新創建一次方法,看下面的例子:
function Person(name,age){ this.name = name; this.age = age; this.feature = feature; // 把方法寫到外面 }; // 每次實例化一個對象,都會創建一次方法,而且這個feature函數是全局函數 function feature(){ alert(this.name+"就是帥!"); }; var person = new Person("飛狐",21); var person1 = new Person("帥狐",19); person.feature(); person1.feature();
可以看出,對象的方法是相同的,而重復的創建導致了定義了多個全局函數,用書上的原話,絲毫木有封裝性可言,那啷個辦呢,于是乎,引出了原型模式。
6. 原型模式我們創建的每個函數都有prototype(原型)屬性,這個屬性是一個指針,指向一個對象,而這個對象的用途是包含可以由特定類型的所有實例共享的屬性和方法。使用原型對象的好處就是可以讓所有對象實例共享它所包含的屬性及方法。
高程3上的這個解釋貌似有點繞腦袋,來吧,直接看例子:
function Person(){ }; Person.prototype.name = "飛狐"; Person.prototype.feature = function(){ alert(this.name+"就是帥!"); }; var person = new Person(); var person1 = new Person(); alert(person.feature == person1.feature); // true
看上去很不錯了,所有對象實例共享了所包含的屬性及方法,但是嘞,構造函數傳遞初始化參數木有了,而且因為共享,一個實例修改了引用,另一個也隨之被更改了,這樣的話就可以結合原型模式與構造函數模式使用,繼續下一個。
7. 組合構造函數 + 原型模式構造函數模式用于定義實例屬性,原型模式用于定義共享屬性,看例子:
function Person(name){ this.name = name; }; Person.prototype = { // 匿名對象 constructor:Person, // 這里有點跳躍,默認的對象指針是指向Object的,這里是讓指針指向本身 feature:function(){ return this.name+"就是帥!"; } }; var person = new Person("飛狐"); var person1 = new Person("帥狐"); alert(person.feature()); // 飛狐就是帥 alert(person1.feature()); // 帥狐就是帥 alert(person.feature == person1.feature); // true
看上去很不錯了,每個實例都會有自己的一份實例屬性,但同時又共享著方法,最大限度的節省了內存。
8. 動態原型模式動態原型模式是把所有信息都封裝在構造函數中,通過構造函數中初始化原型,檢測該方法是否有效而選擇是否需要初始化原型,直接看例子吧:
function Person(name){ this.name = name; if(typeof this.feature != "function"){ // 這里的代碼只執行了一次 Person.prototype.feature = function(){ return this.name+"就是帥!"; } } }; var person = new Person("飛狐"); var person1 = new Person("帥狐"); alert(person.feature()); // 飛狐就是帥 alert(person1.feature()); // 帥狐就是帥 alert(person.feature == person1.feature); // true
金星老師說:"完美"!
在高程3的書上還介紹有寄生構造函數模式,穩妥構造函數模式,這里我們不一一列舉,咱現在有個大概的理解了,后面就可以繼續裝逼繼續飛了。
這一回聊的有點兒多,先裝個逼,話說薛之謙最近有首新歌不錯喲,歌名《紳士》。
9. JQuery的extend源碼分析這一回講的內容比較繞腦袋,下面的內容會更繞腦袋,哈哈~~,不過沒關系,還是那句話,一時理解不了也沒關系,先囫圇吞棗,后面的內容還會涉及,
在高程的書上講繼承,講了6種方法,在網上呢,關于JS的繼承資料多多,所以嘞,咱就裝逼一點,分析比較熱門的框架關于JS繼承的源碼,來吧:
jQuery.extend = jQuery.fn.extend = function() { var options, name, src, copy, copyIsArray, clone, // 這里定義的一堆先不管 target = arguments[0] || {}, // 這里target為arguments[0],表示取傳入的第一個參數 i = 1, length = arguments.length, // 這里的length是傳入的參數總長度 deep = false; // 判斷第一個參數為布爾值的情況,如:jQuery.extend(true,o1,o2); 深拷貝,第一個值不可以是false if ( typeof target === "boolean" ) { deep = target; // 把布爾值的target賦值給deep,相當于deep=true target = arguments[i] || {}; // target改為第二個參數o1 i++; } // 處理像string的case,如:jQuery.extend("fox",{name: "fatfox"}) if ( typeof target !== "object" && !jQuery.isFunction(target) ) { target = {}; } // 這里就是判斷傳入1個參數的情況,則等于本身,最簡單的例子就是JQuery.extend(o); if (i === length ) { target = this; // 1,jQuery.extend時,this指的是jQuery; 2,jQuery.fn.extend時,this指的是jQuery.fn i--; } for ( ; i < length; i++ ) { // 判斷傳入項是有效值的時候,就賦值給options;這里是從第二項開始的遍歷,就是被拷貝項 if ( (options = arguments[i]) != null ) { // for in 枚舉循環沒啥說的 for (name in options ) { src = target[name]; copy = options[name]; // 防止死循環 if ( target === copy ) { continue; } // deep=true為深拷貝,且被拷貝的屬性值本身是個對象 if (deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { // 判斷被拷貝的屬性值是個數組 if (copyIsArray ) { copyIsArray = false; clone = src && jQuery.isArray(src) ? src : []; } else { clone = src && jQuery.isPlainObject(src) ? src : {}; } // 遞歸,修改原對象屬性值 target[name] = jQuery.extend(deep, clone, copy ); // 淺拷貝的情況,屬性值不為undefined } else if ( copy !== undefined ) { target[ name ] = copy; } } } } // 返回修改后的對象 return target; };
可以看出來,JQuery的這個深拷貝和淺拷貝就是拷貝,說白了就是復制,粘貼。
這個是摘自jquery的源碼關于繼承的段兒,我加工的注釋,如果感覺有難度,可以跳過,看下一個。
_.extend = function(obj) { // each循環參數中的每一個對象 // 很熟悉吧,還記得聊柯里化的時候Aarry.prototype.slice.call(arguments,1)嗎 each(slice.call(arguments, 1), function(source) { // 將對象中的全部屬性復制或覆蓋到obj對象 for(var prop in source) { obj[prop] = source[prop]; } }); return obj; };
是不是感覺,很簡單粗暴。
11. node.js的inherits源碼分析Object.create()是ES5的新玩意兒,所以IE9以下不支持
exports.inherits = function(ctor, superCtor) { ctor.super_ = superCtor; // 子類得到的是父類的原型,第二個參數是個對象 ctor.prototype = Object.create(superCtor.prototype, { constructor: { // 構造屬性 value: ctor, // 指針指向子類 enumerable: false, // 不可枚舉 writable: true, // 可修改 configurable: true // 可配置 // 這仨熟悉吧,描述符,對象的屬性 } }); };
這個是node.js的底層源碼,因為是nodejs,所以不用去考慮瀏覽器的兼容性,直接用新玩意兒Object.create(),代碼量少,功能強大。
那么問題來了,要考慮瀏覽器的兼容性,那啷個辦呢,來吧,帥狐show給你看。
這個模擬ExtJS源碼實現,摘自JavaScript設計模式書上的例子。
var c = console; function extend(sub, sup) { // 創建一個函數做為中轉函數 var F = function(){}; // 把父類的原型對象復制給中轉函數的原型對象 F.prototype = sup.prototype; // 實例化的中轉函數,實現子類的原型繼承 sub.prototype = new F(); // 還原子類的構造器 sub.prototype.constructor = sub; // 定義一個靜態屬性保存父類的原型對象 sub._super = sup.prototype; // 降級操作,防止父類原型構造器指向Object if(sup.prototype.constructor == Object.prototype.constructor){ sup.prototype.constructor = sup; } }; function Person(name){ this.name = name; } Person.prototype.getName = function(){ return this.name; } function Gentleman(name,feature){ Gentleman._super.constructor.call(this, name); this.feature = feature; this.getFeature = function(){ alert(this.name+this.feature); }; } extend(Gentleman, Person); var gm = new Gentleman("飛狐","就是帥!"); gm.getFeature(); // 飛狐就是帥
這個是模擬ExtJS的繼承實現,每一步我都寫了注釋,應該還是不難理解吧,ExtJS底層源碼做了很多擴展,這里只是簡單展現思路。
這一回,主要過了一下類的模擬,原型,分析了下繼承在JQuery,underscore,nodejs,ExtJS的源碼實現,感覺還好吧,哈哈~~
下一回,咱主要聊一聊,接口的模擬,裝飾者模式。
話說最近偽裝者過了又是瑯琊榜,梅長蘇又迷到了萬千少女,不知道鐘漢良的新戲啥時候上映啊,漢良哥,快快出來挑戰胡歌吖...
注:此系飛狐原創,轉載請注明出處
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/86093.html
摘要:本回內容介紹上一回,聊了聊狀態模式,并介紹了一下介一回,聊鏈式編程,模擬一下,再模擬一下封裝一個庫。這一回,主要聊了鏈式調用,模擬了,尤其是,希望大家能喜歡這次代碼分享。下一回,聊一聊的策略模式。 本回內容介紹 上一回,聊了聊狀態模式(State),并介紹了一下vue.js;介一回,聊鏈式編程,模擬一下jQuery,再模擬一下underscore.js,封裝一個庫。 1. 鏈式調用 (...
本回內容介紹 上一回聊到JS中模擬接口,裝飾者模式,摻元類,分析了backbone的繼承源碼,感覺還好吧! 介一回,偶們來聊一下在JS單例模式(singleton),單例模式其實運用很廣泛,比如:jquery,AngularJS,underscore吖蝦米的都是單例模式,來吧,直接開始咯: 1. 單例模式 保證一個類只有一個實例,從全局命名空間里提供一個唯一的訪問點來訪問該對象。其實之前寫過的對象...
摘要:介一回聊狀態模式,官方描述允許一個對象在其內部狀態改變時改變它的行為。有限狀態機有限狀態機是一個非常有用的模型,可以模擬世界上大部分事物。這個是官方說法,簡單說,她有三個特征,狀態總數是有限的。,任一時刻,只處在一種狀態之中。 本回內容介紹 上一回聊了聊組合模式(Composite),用組合模式模擬了個圖片庫,聊了遞歸。介一回聊狀態模式(State),官方描述允許一個對象在其內部狀態改...
摘要:本回內容介紹上一回聊到數據類型,簡單的過了一遍,包括個數組新特性等,這一回來聊聊對象,結合數組來實戰一些例子,在做題中成長,記憶會更深刻,來吧,開始咯創建實例的方式有兩種使用操作符后跟構造函數飛狐使用對象字面量表示法飛狐也可以飛狐這種寫法與 本回內容介紹 上一回聊到JS數據類型,簡單的過了一遍,包括9個數組新特性等,這一回來聊聊Object對象,結合數組來實戰一些例子,在做題中成長,記...
摘要:橋接模式之特權函數特權函數,用一些具有特權的方法作為橋梁以便訪問私有空間,可以回憶一下之前的系列。連續自然數分組,計算最多組的個數將至這個連續自然數分成組使每組相加的值相等。個數組中數字最多的一組有個此時的和為。 本回內容介紹 上一回,聊了適配器模式,圖片預加載,介一回,聊橋接模式(Bridge),跟之前一樣,難度比較小,橋接模式將抽象部分與它的實現部分分離,通過橋接模式聯系彼此,同時...
閱讀 3316·2021-11-16 11:45
閱讀 4387·2021-09-22 15:38
閱讀 2841·2021-09-22 15:26
閱讀 3347·2021-09-01 10:48
閱讀 827·2019-08-30 15:56
閱讀 715·2019-08-29 13:58
閱讀 1487·2019-08-28 18:00
閱讀 2160·2019-08-27 10:53