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