標籤:注意 行合并 基礎資料型別 (Elementary Data Type) 模組 傳遞 獨立 底線 ejs 原理
一、單例模式
對象資料類型的作用
把描述同一個事物(同一個對象)的屬性和方法放在一個記憶體空間下,起到了分組的作用,這樣不同事物之間的屬性即使屬性名稱相同 ,相互也不會發生衝突
我們把這種分組編寫代碼的模式叫做"單例模式"
在單例模式中我們把person1或者person2也叫做"命名空間"
var person1 = { name:‘張三‘, age:18 } var person2 = { name:"李四", age:34 }
單例模式是一種項目開發中經常使用的模式,因為項目中我們可以使用單例模式來進行我們的"模組化開發"
模組化開發:對於一個相對來說比較大的項目,需要多人協作的開發的,我們一般情況下會根據當前項目的需求劃分成幾個功能模組,每個人負責一部分,同時開發,最後把每個人的代碼進行合并。
var utils = { select:function(){ } } var searchRender = { change:function(){ utils.select();//調用外部的方法 this.clickEven(); }, clickEven:function(){ } }
二、原廠模式
單例模式雖然解決了分組的作用,但是不能實現批量的生產,屬於手工作業模式,所以出現了原廠模式
把實現同一件事情的代碼放到一個函數中,以後如果再想實現這個功能,不需要重新的編寫這些代碼了,只需要執行當前的函數即可->函數的封裝
低耦合高內聚:減少頁面中的冗餘代碼,提高代碼的重複利用率
function createJsPerson(name,age){ var obj = {}; obj.name = name; obj.age = age; obj.writeJs = function(){ console.log(‘ok‘) } return obj; } var p1 = createJsPerson("小王",12);
js是一門輕量級的指令碼“程式設計語言”(html+css不屬於程式設計語言,屬於標記語言)
所有的程式設計語言都是物件導向開發的->類的繼承、封裝、多態。
繼承:子類繼承父類中的屬性和方法
多態:當前方法的多種形態 後台語言中:多態包含重載和重寫(重載:方法名相同,參數類型不同)
js中不存在重載,方法名一樣的話,後面的會把前面的覆蓋掉,最後只保留一個
js中有一個操作類似重載但是不是重載:我們可以根據傳遞的參數不一樣,實現不同的功能。
function sum(num){ if(typeof num === "undefined"){ return 0 } return num } sum(100); sum()
重寫:子類重寫父類的方法
三、建構函式模式
建構函式模式的目的就是為了建立一個自訂類,並且建立這個類的執行個體
建構函式模式和原廠模式的區別:
1、執行的時候
普通函數執行->createJsPerson()
建構函式模式->new CreateJsPerson() 通過new執行後,我們的CreateJsPerson就是一個類,而函數執行的傳回值(p1)就是CreateJsPerson()這個類的一個執行個體
2、在函數代碼執行的時候
相同:都是形成一個私人的範圍,然後形參賦值->預解釋->代碼從上到下執行(類和普通函數一樣,他也有函數的一面)
不同:在代碼執行之前,不用自己在手動的建立obj對象了,瀏覽器會預設的建立一個對象資料類型的值,這個對象其實就是我們當前類的一個執行個體,接下來代碼從上到下執行,以當前的執行個體為執行的主體(this代表的就是當前的執行個體),然後分別的把屬性名稱和屬性值賦值給當前的執行個體。瀏覽器會預設的把建立的執行個體返回。
function CreateJsPerson(name,age){
//瀏覽器預設建立的對象就是我們的執行個體p1 -> this this.name = name; this.age = age; this.writeJs = function(){ console.log(‘ok‘) } }
var p1 = new CreateJsPerson(‘張三‘,12);
var p2 = new CreateJsPerson(‘李四‘,13);
var res = CreateJsPerson("類似",7);//這樣寫不是建構函式模式執行而是普通的函數執行 由於沒有寫return 所以res = undefined
並且CreateJsPerson這個方法中的this是window
建立一個數組:
var ary = [];//字面量方式
var ary = new Array();//執行個體建立的方式->建構函式模式執行的方式
不管哪一種方式ary都是Array這個類的執行個體
注意:
js中所有的類都是函數資料類型的,它通過new執行變成了一個類,但是它本身也是一個普通的函數。
js中的所有的執行個體都是對象資料類型的
在建構函式模式中,類中(函數體中)出現的this.xxx = xxx中的this是當前類的一個執行個體
雖然p1和p2都是CreateJsPerson這個類的執行個體,所以都擁有writeJs這個方法,但是不同執行個體之間的方法是不一樣的。
在類中給執行個體增加的屬性(this.xxx = xxx)屬於當前執行個體的私人的屬性,執行個體和執行個體之間是單獨的個體,所以私人的屬性之間是不相等的。
擴充代碼如下:
function Fn(){ var num = 10; this.x = 100; this.getX = function(){ //this->需要看getX執行的時候才知道 console.log(this.x) } } var f1 = new Fn; var f2 = new Fn; f1.getX();// this 是f1 ->100 var ss = f1.getX; ss()//this 代表window ->undefined console.log(f1.num)//undefined /* 1、在建構函式模式中new Fn()執行,如果Fn不需要傳遞參數的話,後面的小括弧可以省略 2、this的問題:在類中出現的this.xxx = xxx中的this都是當前類的執行個體,而某一個屬性值(方法),方法中的this需要看方法執行的時候,前面是否有".",才知道this是誰 3、類有普通函數的一面,當函數執行的時候,var num其實只是當前形成的私人範圍的私人變數而已,它和我們的f1這個執行個體沒有任何的關係,只有this.xxx = xxx 才相當於給f1這個執行個體增加私人的屬性和方法,才和我們的f1有關係... 4、在建構函式模式中,瀏覽器會預設的把我們的執行個體返回(返回的是一個對象資料類型的值);如果我們自己手動寫了return 返回: 返回的是一個基礎資料型別 (Elementary Data Type)的值,當前執行個體是不變的,例如:return 100。我們的f1還是當前Fn的執行個體 返回的是一個引用資料類型的值,當前的執行個體會被自己返回的值替換掉,例如:return{name:‘李四‘}我們的f1就不在是Fn的執行個體了,而是對象{name:‘李四‘} 5、檢測某一個執行個體是否屬於這個類->instanceof console.log(f1 instanceof Fn)//true console.log(f1 instanceof Array)//false console.log(f1 instanceof Object)//true 因為所有的執行個體都是對象資料類型的,而每一個對象資料類型都是Object這個內建類的一個執行個體,所以f1也是它的一個執行個體 對於檢測資料類型來說,typeof有自己的局限性,不能細分object下的對象、數組、正則... 6、f1和f2都是Fn這個類的一個執行個體,都擁有x和getX兩個屬性,但是這兩個屬性是各自私人的屬性,所以: console.log(f1.getX===f2.getX);//false 7、in:檢測某一個屬性是否屬於這個對象 attr in object ,不管是私人的屬性還是公有的屬性,只要存在,用in來檢測都是true console.log(‘getX‘ in f1)// true hasOwnProperty:用來檢測某一個屬性是否為這個對象的"私人"屬性,這個方法只能檢測私人的屬性 console.log(f1.hasOwnProperty(‘getX‘))// true
8、isPrototypeOf:函數用於指示對象是否存在於另一個對象的原型鏈中。如果存在,返回true,否則返回false 思考:檢測某一個屬性是否為該對象的"公有屬性" function hasPubProperty(obj,attr){ return (attr in obj) && !obj.hasOwnProperty(attr) } */
四、原型鏈模式
建構函式模式中擁有了類和執行個體的概念,並且執行個體和執行個體之間是相互獨立開的->執行個體識別
基於建構函式模式的原型模式解決了 方法或者屬性公有的問題 ->把執行個體之間相同的屬性和方法提取成公有的屬性和方法 ->想讓誰公有就把它放在CreateJsPerson.prototype上即可。
1、每一個函數資料類型(普通函數、類)都有一個天生內建的屬性:prototype(原型),並且這個屬性是一個對象資料類型的值
2、並且在prototype上瀏覽器天生給它加了一個屬性constructor(建構函式),屬性值是當前函數(類)本身
3、每一個對象資料類型(普通的對象、執行個體、prototype...)也天生內建一個屬性:__proto__(兩個底線),屬性值是當前執行個體所屬類的原型(prototype)
function Fn(){ this.x = 100
this.sum = function(){
}
} Fn.prototype.getX = function(){ console.log(this.x); } Fn.prototype.sum = function(){} var f1 = new Fn; var f2 = new Fn; console.log(Fn.prototype.constructor===Fn)// true
下面結合圖來理解這三句話
注意:
object是JS中所有對象資料類型的基類(最頂層的類)
1、f1 instanceof object ->true 因為f1通過__proto__可以向上級尋找,不管有多少級,最後總能找到Object
2、在Object.prototype上沒有__proto__這個屬性
3、原型鏈模式
f1.hasOwnProperty(‘x‘);// ->hasOwnProperty是f1的一個屬性
但是我們發現在f1的私人屬性上並沒有這個方法,那如何處理的呢?
通過對象名.屬性名稱的方式擷取屬性值的時候,首先在對象的私人屬性上進行尋找,如果私人中存在這個屬性,則擷取的是私人的屬性值。如果私人的沒有,則通過__proto__找到所屬類的原型(類的原型上定義的屬性和方法都是當前執行個體的公有的屬性和方法),原型上存在的話,擷取的是公有的屬性值,如果原型上也沒有,則繼續通過原型上的__proto__繼續向上尋找,一直找到Object.prototype 為止...
這種尋找的機制就是我們的"原型鏈模式"
f1.getX === f2.getX ->true
f1.__proto__.getX ===f2.getX->true
f1.getX === Fn.prototype.getX -> true
f1.sum === f2.__proto__.sum ->false
f1.sum === Fn.prototype.sum ->false
在ie瀏覽器中,我們原型模式也是同樣的原理,但是ie瀏覽器怕你通過__proto__把公有的修改,禁止我們使用__proto__
擴充代碼:
/* 在原型模式中,this常用的有兩種情況 在類中this.xxx = xxx;this->當前類的執行個體 某一個方法中的this->看執行的時候"."前面是誰this就是誰 1、需要先確定this的指向(this是誰) 2、把this替換成對應的代碼 3、按照原型鏈尋找,得出結果 */ function Fn(){ this.x = 100; this.y = 200; this.getY = function(){ console.log(this.y) } } Fn.prototype = { constructor:Fn, y:300, getX:function(){ console.log(this.x) }, getY:function(){ console.log(this.y) } } var f = new Fn f.getX();//100 f.__proto__.getX();//undefined console.log(f.__proto__.x) //在內建類的原型上擴充我們的方法: 數組去重 Array.prototype.myUnique = function(){ //this->ary var obj = {} for(var i = 0;i<this.length;i++){ var cur = this[i]; if(obj[cur] == cur){ this[i] = this[this.length-1]; this.length--; continue } obj[cur] = cur; } obj = null; return this;//目的為了實現鏈式寫法 } //鏈式寫法:執行完成數組的一個方法可以緊接著執行下一個方法 //原理:ary為什麼可以使用sort方法:因為sort是Array.prototype上的公有的方法,而數組ary是Array的一個執行個體,所以ary可以使用sort方法->數組才能使用我們Array原型上定義的屬性和方法。 //sort執行完成的傳回值是一個排序後的"數組",可以執行reverse //reverse執行完成的傳回值是一個排序後的"數組",可以執行pop //pop執行完成的傳回值是刪除的資料,不是一個數組了 ary.sort(function(a,b){ return a - b; }).reverse().pop();
擴充-大量設定公有屬性
//1、起一個別名 function Fn(){ this.x = 100; } var pro = Fn.prototype;//把原來原型指向的地址賦值給pro,現在他們操作的是同一個記憶體空間 pro.getX = function(){ } pro.getY = function(){ } pro.getZ = function(){ } //2、重構原型對象的方法->自己新開闢一個堆記憶體,儲存我們公有的屬性和方法,把瀏覽器原來給Fn.prototype開闢的那個替換掉 function Fn(){ this.x = 100; } Fn.prototype = { constructor:Fn, a:function(){ }, b:function(){ } } var f = new Fn() //1、只有瀏覽器天生給Fn.prototype開闢的堆記憶體裡面才有constructor,而我們自己開闢的這個堆記憶體沒有這個屬性,這樣constructor指向就不在是Fn而是Object了 console.log(f.constructor)//沒做任何處理之前 //為了和原來的保持一致,我們需要手動的增加constructor的指向 //2、用這種方式給內建類增加公有的屬性 //給內建類Array增加數組去重的方法 Array.prototype.unique = function(){ } Array.prototype = { constructor:Array, unique:function(){ } } console.log(Array.prototype); //我們這種方式會把之前已經存在於原型上的屬性和方法給替換掉,所以我們用這種方式修改內建類的話,瀏覽器會給屏蔽掉的 //但是我們可以一個個的修改內建的方法,當我們通過下述方式在數組的原型上增加方法,如果方法名和原來內建的重複,會把內建的修改掉-->我們以後再內建類的原型上增加方法,命名都需要加特殊的首碼 Array.prototype.sort = function(){ console.log(‘ok‘) }
js學習總結----設計模式