1. 程式人生 > >js之建構函式、原型模式

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屬性

  2. 當原型重定向後,瀏覽器預設開闢的那個原型堆記憶體會被釋放掉,如果之前已經儲存了一些方法和屬性,這些東西都會丟失。(所以:內建類的原型不允許重定向到自己開闢的堆記憶體,因為內建類原型上自帶很多屬性和方法,重定向後都沒了,這樣是不被允許的)
  3. 內建類:內建類擴充套件時,如果和內建的方法名相同,會對其進行覆蓋。但是內建類原型的空間地址不可以被修改,只能向其中新增一些方法。所以類.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)