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. 实现较为复杂
相关文章
相关标签/搜索