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