Javascript插件封裝的基礎知識

引言javascript

因爲一些特殊緣由因此,此次就講解下基礎的js插件封裝。java

 

既然須要封裝,那麼就須要一個封裝的容器,那這個容器是什麼?函數

1.什麼是對象性能

對象就是相似於 類的一個實例  好比同事是一個類,那我身邊的一個同事就是一個對象。this

若是舉個簡單的例子:spa

var a =0;var b = 「javascript」;var c = [1,2,3];var d = { key:11,value:22}

這些都是一個對象,可是光有對象還不行,還須要有構造。prototype

2.什麼是構造函數插件

簡單說構造函數是類函數,函數名與類名徹底相同,且無返回值。構造函數是類的一個特殊成員函數。指針

js建立構造的幾種方式:code

1.工廠模式

  考慮到在 ECMAScript 中沒法建立類,開發人員就發明了一種函數,用函數來封裝以特定接口建立對象的細節,以下面的例子所示:

  function createPerson(id,name){
    var o = new Object();
            o.id = id;
    o.name = name;

    o.sayName = function(){
      alert(this.name);
    }
    return o;
  }
  var person1 = createPerson(1,27);

 

  函數 createPerson()可以根據接受的參數來構建一個包含全部必要信息的 Person 對象。能夠無數次地調用這個函數,而每次它都會返回一個包含三個屬性一個方法的對象。工廠模式雖然解決了建立\多個類似對象的問題,但卻沒有解決對象識別的問題(即怎樣知道一個對象的類型)。 

 

  主要好處就是能夠消除對象間的耦合,經過使用工程方法而不是new關鍵字。將全部實例化的代碼集中在一個位置防止代碼重複。

 

  工廠模式解決了重複實例化的問題 ,但還有一個問題,那就是識別問題,由於根本沒法 搞清楚他們究竟是哪一個對象的實例。

2.構造函數模式

  ECMAScript中的構造函數可用來建立特定類型的對象,像Array和Object這樣的原生構造函數,在運行時會自動出如今執行環境中。此外,也能夠建立自定義的構造函數,從而定義自定義對象的屬性和方法。使用構造函數的方法,既解決了重複實例化的問題,又解決了對象識別的問題。例如,可使用構造函數模式將前面的例子重寫以下:

  function Person(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function(){
      alert(this.name);
    }
  }
  var person1 = new Person("Nicholas", 29, "Software Engineer"); 
  var person2 = new Person('Grey',27,'Doctor');

  Person()中的代碼除了與 createPerson()中相同的部分外,還存在如下不一樣之處:

     沒有顯式地建立對象;
     直接將屬性和方法賦給了 this 對象;
     沒有 return 語句。

 3.原型模式

  咱們建立的每一個函數都有一個 prototype(原型)屬性,這個屬性是一個指針,指向一個對象,而這個對象的用途是包含能夠由特定類型的全部實例共享的屬性和方法。

  若是按字面意思來理解,那麼prototype就是經過調用構造函數而建立的那個對象實例的原型對象。使用原型對象的好處是可讓全部對象實例共享它所包含的屬性和方法。換句話說,沒必要在構造函數中定義對象實例的信息,而是能夠將這些信息直接添加到原型對象中。例如:

 

  function Person(){
  }
  Person.prototype.name = 'Nicholas';
  Person.prototype.age = 29;
  Person.prototype.job = 'Software Engineer';
  Person.prototype.sayName = function(){
    alert(this.name);
  }
  var person1 = new Person();
  person1.sayName();//"Nicholas"

  var person2 = new Person(); 
  person2.sayName(); //"Nicholas" 
  alert(person1.sayName == person2.sayName); //true

 

  在此,咱們將 sayName()方法和全部屬性直接添加到了 Person 的 prototype 屬性中,構造函數變成了空函數。

  即便如此,也仍然能夠經過調用構造函數來建立新對象,並且新對象還會具備相同的屬性和方法。但與構造函數模式不一樣的是,新對象的這些屬性和方法是由全部實例共享的。換句話說,person1 和 person2 訪問的都是同一組屬性和同一個 sayName()函數。要理解原型模式的工做原理,必須先理解 ECMAScript 中原型對象的性質。

3 繼承

既然要實現繼承,那麼首先咱們得有一個父類,代碼以下:

// 定義一個動物類
function Animal (name) {
  // 屬性
  this.name = name || 'Animal';
  // 實例方法
  this.sleep = function(){
    console.log(this.name + '正在睡覺!');
  }
}
// 原型方法
Animal.prototype.eat = function(food) {
  console.log(this.name + '正在吃:' + food);
};

一、原型鏈繼承

核心: 將父類的實例做爲子類的原型

function Cat(){ 
}
Cat.prototype = new Animal();
Cat.prototype.name = 'cat';

// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.eat('fish'));
console.log(cat.sleep());
console.log(cat instanceof Animal); //true 
console.log(cat instanceof Cat); //true

特色:

  1. 很是純粹的繼承關係,實例是子類的實例,也是父類的實例
  2. 父類新增原型方法/原型屬性,子類都能訪問到
  3. 簡單,易於實現

缺點:

  1. 要想爲子類新增屬性和方法,必需要在new Animal()這樣的語句以後執行,不能放到構造器中
  2. 沒法實現多繼承
  3. 來自原型對象的全部屬性被全部實例共享(來自原型對象的引用屬性是全部實例共享的)(詳細請看附錄代碼: 示例1
  4. 建立子類實例時,沒法向父類構造函數傳參

二、構造繼承

核心:使用父類的構造函數來加強子類實例,等因而複製父類的實例屬性給子類(沒用到原型)

function Cat(name){
  Animal.call(this);
  this.name = name || 'Tom';
}

// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // false
console.log(cat instanceof Cat); // true

特色:

  1. 解決了1中,子類實例共享父類引用屬性的問題
  2. 建立子類實例時,能夠向父類傳遞參數
  3. 能夠實現多繼承(call多個父類對象)

缺點:

  1. 實例並非父類的實例,只是子類的實例
  2. 只能繼承父類的實例屬性和方法,不能繼承原型屬性/方法
  3. 沒法實現函數複用,每一個子類都有父類實例函數的副本,影響性能

三、實例繼承

核心:爲父類實例添加新特性,做爲子類實例返回

function Cat(name){
  var instance = new Animal();
  instance.name = name || 'Tom';
  return instance;
}

// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); // false

特色:

  1. 不限制調用方式,不論是new 子類()仍是子類(),返回的對象具備相同的效果

缺點:

  1. 實例是父類的實例,不是子類的實例
  2. 不支持多繼承

四、拷貝繼承

function Cat(name){
  var animal = new Animal();
  for(var p in animal){
    Cat.prototype[p] = animal[p];
  }
  Cat.prototype.name = name || 'Tom';
}

// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // false
console.log(cat instanceof Cat); // true

 

特色:

  1. 支持多繼承

缺點:

  1. 效率較低,內存佔用高(由於要拷貝父類的屬性)
  2. 沒法獲取父類不可枚舉的方法(不可枚舉方法,不能使用for in 訪問到)

五、組合繼承

核心:經過調用父類構造,繼承父類的屬性並保留傳參的優勢,而後經過將父類實例做爲子類原型,實現函數複用

function Cat(name){
  Animal.call(this);
  this.name = name || 'Tom';
}
Cat.prototype = new Animal();

Cat.prototype.constructor = Cat;

// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); // true

 

特色:

  1. 彌補了方式2的缺陷,能夠繼承實例屬性/方法,也能夠繼承原型屬性/方法
  2. 既是子類的實例,也是父類的實例
  3. 不存在引用屬性共享問題
  4. 可傳參
  5. 函數可複用

缺點:

  1. 調用了兩次父類構造函數,生成了兩份實例(子類實例將子類原型上的那份屏蔽了)

六、寄生組合繼承

核心:經過寄生方式,砍掉父類的實例屬性,這樣,在調用兩次父類的構造的時候,就不會初始化兩次實例方法/屬性,避免的組合繼承的缺點

function Cat(name){
  Animal.call(this);
  this.name = name || 'Tom';
}
(function(){
  // 建立一個沒有實例方法的類
  var Super = function(){};
  Super.prototype = Animal.prototype;
  //將實例做爲子類的原型
  Cat.prototype = new Super();
})();

// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); //true

Cat.prototype.constructor = Cat; // 須要修復下構造函數

 

特色:

  1. 堪稱完美

缺點:

  1. 實現較爲複雜
相關文章
相關標籤/搜索