js之構造函數、原型模式


單例模式

把描述同一事物的屬性放進同一個空間地址下,避免了全局變量的干擾,這種開發的模式就是單例模式。

var singleton1={
   fn:function{
   }
}
var singleton2={
   fn:function{
   }
}
singleton1.fn();
singleton2.fn();

高級單例模式

采用了自執行函數閉包的作用,保護里面的私有變量不受外界的干擾;同時,如果在閉包外面使用里面的函數時,可以把其return作為返回值。

  var niu = (function () {
       var  num = 1;
       var  niuCode = {
           fn:function () {
               console.log(num);
           },
           sum:function () {
               console.log(num);
           }
       }
       return niuCode;//niuCode只是一個空間地址,所以定義一個niu來接收它。
   })();
    box.onclick = niu.fn;

工廠模式

把實現同一功能的代碼放到一個函數中,當想實現這個功能時,執行這個函數即可;減少了代碼的冗余,“高內聚,低耦合”,實現了函數的封裝。

  function createPerson(name,age) {
        var obj ={};// 創建一個對象
        obj.name = name;// name獲取形參的值
        obj.age = age;
        return obj;//return 出一個對象
    }
    // p1接收了createPerson 的返回值;
    var p1 = createPerson("張三",12);
    var p2 = createPerson("王五",30);

構造函數

new:操作符,在函數執行前加new

  1. 形成一個私有作用域之后,代碼執行之前,會默認創建一個空的對象(堆內存);
  2. 並且改變了當前作用域下的this指向,讓其指向那個空對象;
  3. 當代碼運行完,把這個對象返回,這是瀏覽器默認返回的。

:內置類和自定義類;所有的類都是函數;
實例:通過構造函數或者類new出來的都是一個實例。

普通函數和構造函數區別

  • 普通函數 形成私有作用域-->形參賦值-->變量提升-->代碼從上到下執行-->作用域是否銷毀
  • 構造函數 形成私有作用域-->形參賦值-->變量提升-->創建對象,讓this指向這個對象-->代碼從上到下執行-->return這個對象-->作用域是否銷毀

構造函數的特征

  1. 構造函數中的this永遠指向實例
  2. 構造函數的函數名一般都是大寫,為了區分普通的函數。
  3. 如果構造函數,並且不需要參數的情況下,可以省略小括號。
  4. 如果return一個基本數據類型值,不能覆蓋內置的(即默認的返回的對象或者實例);如果return一個引用的數據類型值,會覆蓋默認的return的對象。
  5. instanceof:檢測當前實例是否屬於這個的方法,屬於返回true,不屬於返回false。
  6. hasOwnProperty:檢測當前屬性是否是私有屬性,如果是返回true;不是返回false
    實例.hasOwnProperty(屬性)
//封裝一個檢測共有屬性的方法
function hasPublicProperty(obj,attr){
if(obj[attr}){//由於傳入的參數是字符串,所以不能用obj.attr,可以用obj[attr]或者attr in obj;
               if(obj.hasOwnProperty(attr)===false){
         return true;
      }else{
         return false;
      }
      else{
         return false;
       }
   }
}
hasPublicProperty(obj,"tostring")

//優化以后
function hasPublicProperty(obj,attr){
       return attr in obj && !obj.hasOwnProperty(attr)?true:false;
}
hasPublicProperty(obj,"tostring")

原型模式

  1. 所有的函數數據類型都天生自帶一個屬性:prototype(原型),這個屬性的值是一個對象:存儲了當前類供實例調取使用的共有屬性和方法。瀏覽器會默認給它開辟一個堆內存。
  2. 在瀏覽器默認給prototype開辟的堆內存中,即prototype屬性中有一個天生自帶的屬性:constructor,這個屬性存儲的值是當前原型所屬的類(當前函數本身)。
  3. 所有的對象數據類型都天生自帶一個__proto__的屬性,這個屬性的屬性值指向當前實例所屬類的prototype(如果不能確定它是誰的實例,都是object的實例,即object.prototype)
  4. 所有的函數,包括普通函數、類(內置類、自定義類)都是Function的一個實例。
    (理解這四句話,基本上原型模式就算理解了)

原型鏈

它是一種基於__proto__向上查找的機制。當我們操作實例的某個屬性或者方法的時候,首先找自己空間中私有的屬性或者方法

  1. 找到了,則結束查找,使用自己私有的即可
  2. 沒有找到的話,則基於__proto__找所屬類的prototype,如果找到,就用這個共有的,如果沒找到,基於原型上的__proto__繼續向上查找,一直找到Object.prototype的原型為止,如果再沒有,操作的屬性或者方法不存,得到undefined。

這樣通過__proto__向上查找就會形成一個原型鏈。

原型重定向

在實際項目基於面向對象開發的時候(構造原型設計模式),我們根據需要,很多時候會重定向類的原型(讓類的原型指向自己開辟的堆內存)
當我們需要給類的原型批量設置屬性和方法的時候,一般都是讓原型重定向到自己創建的對象中

Fn.prototype={
   aa:function()
}

存在的問題:

  1. 自己開辟的堆內存中沒有consturctor屬性,導致類的原型構造函數缺失。

解決:手動在堆內存增加constructor屬性

  1. 當原型重定向后,瀏覽器默認開辟的那個原型堆內存會被釋放掉,如果之前已經存儲了一些方法和屬性,這些東西都會丟失。(所以:內置類的原型不允許重定向到自己開辟的堆內存,因為內置類原型上自帶很多屬性和方法,重定向后都沒了,這樣是不被允許的)
  2. 內置類:內置類擴展時,如果和內置的方法名相同,會對其進行覆蓋。但是內置類原型的空間地址不可以被修改,只能向其中新增一些方法。所以類.prototype={}只適用於自定義類,不能用於內置類,比如Array等這些內置類。

零碎知識點

私有屬性和公有屬性

  • 私有屬性:自己堆內存中存儲的屬性相對自己來說是私有的
  • 公有屬性:自己基於__proto__找到的屬性,想對自己來說是公有的

基於內置類的原型擴展方法,供它的實例調取使用

//實現數組的去重
Array.prototype.myUnique=function myUnique(){//后邊函數的名字可加可不加
//方法中的this一般都是當前類的實例(也就是我們要操作的數組)
   var obj={};
   for(var i=0;i<this.length;i++){
       var item=this[i];
       obj.hasOwnProperty(item)? (this[i]=this[this.length-1],this.length--,i--):obj[item]=item;
       }
    obj=null;//釋放堆內存
    return this;   
};
ary.myUnique();
//鏈式寫法求數組最大值
var max=ary.myUnique().sort(function(a,b){return a-b}).pop();
  1. 我們增加的方法最好設置“my”前綴(前綴自己定),防止把內置方法重寫

JS的鏈式寫法
保證每一個方法執行返回的結果依然是當前類的實例,這樣就可以繼續調取方法使用了

ary.sort(function(a,b){return a-b}).reverse().pop();
//執行完返回的結果是刪除的那一項的值,所在再調用數組方法會報錯,也可以接着使用其它內置類方法

創建變量之字面量創建和實例創建

var a=1;
var a1=new Number(1);
  • 通過字面量方式創建的實例(基本數據類型的實例)不是一個標准的實例,不能使用instanceof進行檢測
  • 通過new創建的實例不能通過typeof進行數據類型的檢測,都返回"object"

可枚舉屬性和不可枚舉屬性

用for...in...遍歷會輸出可枚舉的屬性

  • 可枚舉屬性:私有的屬性和自定義的共有屬性。
  • 不可枚舉屬性:是公有屬性並且內置的屬性。

call、apply、bind

call

[Fn].call([this],[param]...)
fn.call:當前實例(函數Fn)通過原型鏈的查找機制,找到Function.prototype上的call方法
fn.call():把找到的call方法執行

當call執行的時候,內部處理了一些事情

  1. 首先把要操作函數中的this關鍵字變為call方法第一個傳遞的參數值
  2. 把call方法第二個及第二個以后的實參獲取到
  3. 把要操作的函數執行,並且把第二個以后的傳遞進來的實參傳給函數

call中的細節(適用於apply和bind)

  1. 非嚴格模式下,如果參數不傳,或者第一個參數是null、undefined,this都指向window;
  2. 嚴格模式下,第一個參數是誰,this就指向誰(包括null、undefined),不傳this是undefined。

apply

apply:和call基本上一模一樣,唯一區別在於傳參的方式。apply把需要傳遞給FN的參數放到一個數組(或者類數組)中傳遞進去,雖然寫的是一個數組,但是也相當於FN接受的是數組的每一項,而不是整個數組。

bind

bind:預處理this;提前改變this指向,但是FN不立即執行;bind的傳參和call方式傳參一致,需要一個個向里傳。

fn.call(obj,10,20); //改變fn中的this,並且把fn立即執行
fn.bind(obj,10,20); //改變fn中的this,此時的fn並沒有執行(不兼容IE6~8)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM