欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

javascript框架設(shè)計(jì)之類工廠

 更新時(shí)間:2015年06月23日 10:23:42   投稿:hebedich  
這篇文章主要介紹了javascript框架設(shè)計(jì)之類工廠的相關(guān)資料,非常淺顯易懂,有需要的小伙伴可以查看下。

類與繼承在javascript的出現(xiàn),說明javascript已經(jīng)達(dá)到大規(guī)模開發(fā)的門檻了,在之前是ECMAScript4,就試圖引入類,模塊等東西,但由于過分引入太多的特性,搞得javascript烏煙瘴氣,導(dǎo)致被否決。不過只是把類延時(shí)到ES6.到目前為止,javascript還沒有正真意義上的類。不過我們可以模擬類,曾近一段時(shí)間,類工廠是框架的標(biāo)配,本章會(huì)介紹各種類實(shí)現(xiàn),方便大家在自己的框架中或選擇時(shí)自己喜歡的那一類風(fēng)格。

1.javascript對(duì)類的支持

在其它語言中 ,類的實(shí)例都要通過構(gòu)造函數(shù)new出來。作為一個(gè)刻意模仿java的語言。javascript存在new操作符,并且所有函數(shù)都可以作為構(gòu)造器。構(gòu)造函數(shù)與普通的方法沒有什么區(qū)別。瀏覽器為了構(gòu)建它繁花似錦的生態(tài)圈,比如Node,Element,HTMLElement,HTMLParagraphElement,顯然使用繼承關(guān)系方便一些方法或?qū)傩缘墓蚕?,于是javascript從其它語言借鑒了原型這種機(jī)制。Prototype作為一個(gè)特殊的對(duì)象屬性存在于每一個(gè)函數(shù)上。當(dāng)一個(gè)函數(shù)通過new操作符new出其“孩子”——“實(shí)例”,這個(gè)名為實(shí)例的對(duì)象就擁有這個(gè)函數(shù)的Prototype對(duì)象所有的一切成員,從而實(shí)現(xiàn)實(shí)現(xiàn)所有實(shí)例對(duì)象都共享一組方法或?qū)傩浴6鴍avascript所謂的“類”就是通過修改這個(gè)Prototype對(duì)象,以區(qū)別原生對(duì)象及其其它定義的“類”。在瀏覽器中,node這個(gè)類基于Object修改而來的,而Element則是基于Node,而HTMLElement又基于Element....相對(duì)我們的工作業(yè)務(wù),我們可以創(chuàng)建自己的類來實(shí)現(xiàn)重用與共享。

  function A(){

  }
  A.prototype = {
    aa:"aa",
    method:function(){
    }
  };
  var a = new A;
  var b = new A;
  console.log(a.aa === b.aa);
  console.log(a.method === b.method)

一般地,我們把定義在原型上的方法叫原型方法,它為所有的實(shí)例所共享,這有好也有不好,為了實(shí)現(xiàn)差異化,javascript允許我們直接在構(gòu)造器內(nèi)指定其方法,這叫特權(quán)方法。如果是屬性,就叫特權(quán)屬性。它們每一個(gè)實(shí)例一個(gè)副本,各不影響。因此,我們通常把共享用于操作數(shù)據(jù)的方法放在原型,把私有的屬性放在特權(quán)屬性中。但放于this上,還是讓人任意訪問到,那就放在函數(shù)體內(nèi)的作用域內(nèi)吧。這時(shí)它就成為名副其實(shí)的私有屬性。

  function A() {
    var count = 0;
    this.aa = "aa";
    this.method = function() {
      return count;
    }
    this.obj = {}
  }
  A.prototype = {
    aa:"aa",
    method:function(){

    }
  };
  var a = new A;
  var b = new A;
  console.log(a.aa === b.aa);//true 由于aa的值為基本類型,比較值
  console.log(a.obj === b.obj) //false 引用類型,每次進(jìn)入函數(shù)體都要重新創(chuàng)建,因此都不一樣。
  console.log(a.method === b.method); //false

特權(quán)方法或?qū)傩灾皇侵皇钦谧≡偷姆椒ɑ驅(qū)傩?,因此只要?jiǎng)h掉特權(quán)方法,就能方法到同名的原型方法或?qū)傩浴?/p>

  delete a.method;
  delete b.method;
  console.log(a.method === A.prototype.method);//true
  console.log(a.method === b.method); //true

用java的語言來說,原型方法與特權(quán)方法都屬性實(shí)例方法,在java中還有一種叫類方法與類屬性的東西。它們用javascript來模擬也非常簡單,直接定義在函數(shù)上就行了。

  A.method2 = function(){} //類方法
  var c = new A;
  console.log(c.method2); //undefined

接下來,我們看下繼承的實(shí)現(xiàn),上面說過,Prototype上有什么東西,它的實(shí)例就有什么東西,不論這個(gè)屬性是后來添加的,還是整個(gè)Prototype都置換上去的。如果我們將這個(gè)prototype對(duì)象置換為另一個(gè)類的原型,那么它就輕而易舉的獲得那個(gè)類的所有原型成員。

  function A() {};
  A.prototype = {
    aaa : 1
  }
  function B() {};
  B.prototype = A.prototype;
  var b = new B;
  console.log(b.aaa); //=> 1;
  A.prototype.bb = 2;
  console.log(b.bb) //=> 2;

由于是引用著同一個(gè)對(duì)象,這意味這,我們修改A類的原型,也等同于修該了B類的原型。因此,我們不能把一個(gè)對(duì)象賦值給兩個(gè)類。這有兩種辦法,

方法1:通過for in 把父類的原型成員逐一賦給子類的原型
方法2是:子類的原型不是直接由父類獲得,先將父類的原型賦值給一個(gè)函數(shù),然后將這個(gè)函數(shù)的實(shí)例作為子類的原型。

方法一,我們通常要實(shí)現(xiàn)mixin這樣的方法,有的書稱之為拷貝繼承,好處就是簡單直接,壞處就是無法通過instanceof驗(yàn)證。Prototype.js的extend方法就用來干這事。

  function extend (des, source) { //des = destination
    for (var property in source)
      des[property] = source[property];
    return des;
  }

方法二,就在原型上動(dòng)腦筋,因此稱之為原型繼承。下面是個(gè)范本

  function A() {};
  A.prototype = {
    aa:function(){
      alert(1)
    }
  }
  function bridge() {

  };
  bridge.prototype = A.prototype;

  function B() {}
  B.prototype = new bridge();

  var a = new A;
  var b = new B;

  console.log(a == b) //false 證明成功分開原型
  console.log(A.prototype == B.prototype) //true 子類共享父類的原型方法
  console.log(a.aa === b.aa); //為父類動(dòng)態(tài)添加新的方法
  A.prototype.bb = function () {
    alert(2)
  }
  //true,繼承父類的方法
  B.prototype.cc = function (){
    alert(3)
  }
  //false 父類未必有子類的new實(shí)例
  console.log(a.cc === b.cc)
  //并且它能夠正常通過javascript自帶的驗(yàn)證機(jī)制instanceof
  console.log(b instanceof A) ;//true
  console.log(b instanceof B) ; //true

方法二能通過instanceof驗(yàn)證,es5就內(nèi)置了這種方法來實(shí)現(xiàn)原型繼承,它就是Object.create,如果不考慮第二個(gè)參數(shù),它約等于下面的代碼。

  Object.create = function (o) {
    function F() {}
    F.prototype = o;
    return new F();
  }

上面的方法,要求傳入一個(gè)父類的原型作為參數(shù),然后返回子類的原型

不過,我們這樣還是遺漏了一點(diǎn)東西——子類不只是繼承父類的遺產(chǎn),還應(yīng)該有自己的東西,此外,原型繼承并沒有讓子類繼承父類的成員與特權(quán)成員。這些我們都得手動(dòng)添加,如類成員,我們可以通過上面的extend方法,特權(quán)成員我們可以在子類構(gòu)造器中,通過apply實(shí)現(xiàn)。

  function inherit(init, Parent, proto){
    function Son(){
      Parent.apply(this, argument); //先繼承父類的特權(quán)成員
      init.apply(this, argument); //在執(zhí)行自己的構(gòu)造器
    }
  }
  //由于Object.create是我們偽造的,因此避免使用第二個(gè)參數(shù)
  Son.prototype = Object.create(Parent.prototype,{});
  Son.prototype.toString = Parent.prototype.toString; //處理IEbug
  Son.prototype.valueOf = Parent.prototype.valueOf; //處理IEbug
  Son.prototype.constructor = Son; //確保構(gòu)造器正常指向,而不是Object
  extend(Son, proto) ;//添加子類的特有的原型成員
  return Son;

下面,做一組實(shí)驗(yàn),測(cè)試下實(shí)例的回溯機(jī)制。當(dāng)我們?cè)L問對(duì)象的一個(gè)屬性,那么他先尋找其特權(quán)成員,如果有同名就返回,沒有就找原型,再?zèng)]有,就找父類的原型...我們嘗試將它的原型臨時(shí)修改下,看它的屬性會(huì)變成那個(gè)。

  function A(){

  }
  A.prototype = {
    aa:1
  }
  var a = new A;
  console.log(a.aa) ; //=>1

  //將它的所有原型都替換掉
  A.prototype = {
    aa:2
  }
  console.log(a.aa); //=>1

  //于是我們想到每個(gè)實(shí)例都有一個(gè)constructor方法,指向其構(gòu)造器
  //而構(gòu)造器上面正好有我們的原型,javascript引擎是不是通過該路線回溯屬性呢
  function B(){

  }
  B.prototype = {
    aa:3
  }
  a.constructor = B;
  console.log(a.aa) //1 表示不受影響

因此類的實(shí)例肯定通過另一條通道進(jìn)行回溯,翻看ecma規(guī)范可知每一個(gè)對(duì)象都有一個(gè)內(nèi)部屬性[[prototype]],它保存這我們new它時(shí)的構(gòu)造器所引用的Prototype對(duì)象。在標(biāo)準(zhǔn)瀏覽器與IE11里,它暴露了一個(gè)叫__proto__屬性來訪問它。因此,只要不動(dòng)__proto__上面的代碼怎么動(dòng),a.aa始終堅(jiān)定不毅的返回1.

再看一下,new時(shí)操作發(fā)生了什么。

1.創(chuàng)建了一個(gè)空對(duì)象 instance
2.instance.__proto__ = intanceClass.prototype
3.將構(gòu)造函數(shù)里面的this = instance
4.執(zhí)行構(gòu)造函數(shù)里的代碼
5.判定有沒有返回值,沒有返回值就返回默認(rèn)值為undefined,如果返回值為復(fù)合數(shù)據(jù)類型,則直接返回,否則返回this
于是有了下面的結(jié)果。

  function A(){
    console.log(this.__proto__.aa); //1
    this.aa = 2
  }
  A.prototype = {aa:1}
  var a = new A;
  console.log(a.aa)
  a.__proto__ = {
    aa:3
  }
  console.log(a.aa) //=>2
  delete a. aa; //刪除特權(quán)屬性,暴露原型鏈上的同名屬性
  console.log(a.aa) //=>3

有了__proto__,我們可以將原型設(shè)計(jì)繼承設(shè)計(jì)得更簡單,我們還是拿上面的例子改一改,進(jìn)行試驗(yàn)

  function A() {}
  A.prototype = {
    aa:1
  }
  function bridge() {}
  bridge.prototype = A.prototype;

  function B(){}
  B.prototype = new bridge();
  B.prototype.constructor = B;
  var b = new B;
  B.prototype.cc = function(){
    alert(3)
  }
  //String.prototype === new String().__proto__ => true
  console.log(B.prototype.__proto__ === A.prototype) //true
  console.log(b.__proto__ == B.prototype); //true 
  console.log(b.__proto__.__proto__ === A.prototype); //true 得到父類的原型對(duì)象

因?yàn)閎.__proto__.constructor為B,而B的原型是從bridge中得來的,而bride.prototype = A.prototype,反過來,我們?cè)诙x時(shí),B.prototype.__proto__ = A.prototype,就能輕松實(shí)現(xiàn)兩個(gè)類的繼承.

__proto__屬性已經(jīng)加入es6,因此可以通過防止大膽的使用

2.各種類工廠的實(shí)現(xiàn)。

上節(jié)我們演示了各種繼承方式的實(shí)現(xiàn),但都很凌亂。我們希望提供一個(gè)專門的方法,只要用戶傳入相應(yīng)的參數(shù),或按照一定簡單格式就能創(chuàng)建一個(gè)類。特別是子類。

由于主流框架的類工廠太依賴他們龐雜的工具函數(shù),而一個(gè)精巧的類工廠也不過百行左右

相當(dāng)精巧的庫,P.js

https://github.com/jiayi2/pjs

使用版:https://github.com/jiayi2/factoryjs

這是一個(gè)相當(dāng)精巧的庫,尤其調(diào)用父類的同名方法時(shí),它直接將父類的原型拋在你面前,連_super也省了。

  var P = (function(prototype, ownProperty, undefined) {
 return function P(_superclass /* = Object */, definition) {
  // handle the case where no superclass is given
  if (definition === undefined) {
   definition = _superclass;
   _superclass = Object;
  }

  // C is the class to be returned.
  //
  // When called, creates and initializes an instance of C, unless
  // `this` is already an instance of C, then just initializes `this`;
  // either way, returns the instance of C that was initialized.
  //
  // TODO: the Chrome inspector shows all created objects as `C`
  //    rather than `Object`. Setting the .name property seems to
  //    have no effect. Is there a way to override this behavior?
  function C() {
   var self = this instanceof C ? this : new Bare;
   self.init.apply(self, arguments);
   return self;
  }

  // C.Bare is a class with a noop constructor. Its prototype will be
  // the same as C, so that instances of C.Bare are instances of C.
  // `new MyClass.Bare` then creates new instances of C without
  // calling .init().
  function Bare() {}
  C.Bare = Bare;

  // Extend the prototype chain: first use Bare to create an
  // uninitialized instance of the superclass, then set up Bare
  // to create instances of this class.
  var _super = Bare[prototype] = _superclass[prototype];
  var proto = Bare[prototype] = C[prototype] = C.p = new Bare;

  // pre-declaring the iteration variable for the loop below to save
  // a `var` keyword after minification
  var key;

  // set the constructor property on the prototype, for convenience
  proto.constructor = C;

  C.extend = function(def) { return P(C, def); }

  return (C.open = function(def) {
   if (typeof def === 'function') {
    // call the defining function with all the arguments you need
    // extensions captures the return value.
    def = def.call(C, proto, _super, C, _superclass);
   }

   // ...and extend it
   if (typeof def === 'object') {
    for (key in def) {
     if (ownProperty.call(def, key)) {
      proto[key] = def[key];
     }
    }
   }

   // if no init, assume we're inheriting from a non-Pjs class, so
   // default to using the superclass constructor.
   if (!('init' in proto)) proto.init = _superclass;

   return C;
  })(definition);
 }

 // as a minifier optimization, we've closured in a few helper functions
 // and the string 'prototype' (C[p] is much shorter than C.prototype)
})('prototype', ({}).hasOwnProperty);

我們嘗試創(chuàng)建一個(gè)類:

  var Dog = P (function(proto, superProto){
    proto.init = function(name) { //構(gòu)造函數(shù)
      this.name = name;
    }
    proto.move = function(meters){ //原型方法
      console.log(this.name + " moved " + meters + " m.")
    }
  });
  var a = new Dog("aaa")
  var b = new Dog("bbb"); //無實(shí)例變化
  a.move(1);
  b.move(2);

我們?cè)诂F(xiàn)在的情況下,可以嘗試創(chuàng)建更簡潔的定義方式

  var Animal = P (function(proto, superProto){
    proto.init = function(name) { //構(gòu)造函數(shù)
      this.name = name;
    }
    proto.move = function(meters){ //原型方法
      console.log(this.name + " moved " + meters + " m.")
    }
  });
  var a = new Animal("aaa")
  var b = new Animal("bbb"); //無實(shí)例變化
  a.move(1);
  b.move(2);
  //...............
  var Snake = P (Animal, function(snake, animal){
    snake.init = function(name, eyes){
      animal.init.call(this, arguments); //調(diào)運(yùn)父類構(gòu)造器
      this.eyes = 2;
    }
    snake.move = function() {
      console.log('slithering...');
      animal.move.call(this, 5); //調(diào)運(yùn)父類同名方法
    }
  });
  var s = new Snake("snake", 1);
  s.move();
  console.log(s.name);
  console.log(s.eyes);

私有屬性演示,由于放在函數(shù)體內(nèi)集中定義,因此安全可靠!

  var Cobra = P (Snake, function(cobra){
    var age = 1;//私有屬性
    //這里還可以編寫私有方法
    cobra.glow = function(){ //長大
      return age++;
    }
  });
  var c = new Cobra("cobra");
  console.log(c.glow()); //1
  console.log(c.glow()); //2
  console.log(c.glow()); //3

以上所述就是本文的全部內(nèi)容了,希望大家能夠喜歡。

相關(guān)文章

  • ES6 更易于繼承的類語法的使用

    ES6 更易于繼承的類語法的使用

    這篇文章主要介紹了ES6 更易于繼承的類語法的使用,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2019-02-02
  • js與jQuery實(shí)現(xiàn)的用戶注冊(cè)協(xié)議倒計(jì)時(shí)功能實(shí)例【三種方法】

    js與jQuery實(shí)現(xiàn)的用戶注冊(cè)協(xié)議倒計(jì)時(shí)功能實(shí)例【三種方法】

    這篇文章主要介紹了js與jQuery實(shí)現(xiàn)的用戶注冊(cè)協(xié)議倒計(jì)時(shí)功能,結(jié)合實(shí)例形式分析了三種倒計(jì)時(shí)功能的相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2017-11-11
  • 原生JS實(shí)現(xiàn)拖拽位置預(yù)覽

    原生JS實(shí)現(xiàn)拖拽位置預(yù)覽

    這篇文章主要為大家詳細(xì)介紹了原生JS實(shí)現(xiàn)拖拽位置預(yù)覽,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-10-10
  • JS中判斷某個(gè)字符串是否包含另一個(gè)字符串的五種方法

    JS中判斷某個(gè)字符串是否包含另一個(gè)字符串的五種方法

    本文給大家?guī)鞪S中判斷某個(gè)字符串是否包含另一個(gè)字符串的五種方法,有string對(duì)象的方法,match() 方法,RegExp對(duì)象的方法,test() 方法,exec() 方法,具體內(nèi)容詳情大家參考下本文
    2018-05-05
  • 通過DOM腳本去設(shè)置樣式信息

    通過DOM腳本去設(shè)置樣式信息

    在大多數(shù)場(chǎng)合,我們都用CSS去設(shè)置樣式,但在某些特殊情況下,例如要根據(jù)元素在節(jié)點(diǎn)樹里的位置來設(shè)置節(jié)點(diǎn)樣式信息時(shí),目前CSS還沒辦法做到這一點(diǎn)。但利用DOM就可以很輕易的完成。
    2010-09-09
  • js實(shí)現(xiàn)帶有動(dòng)畫的返回頂部

    js實(shí)現(xiàn)帶有動(dòng)畫的返回頂部

    這篇文章主要為大家詳細(xì)介紹了js實(shí)現(xiàn)帶有動(dòng)畫的返回頂部,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-08-08
  • js動(dòng)態(tài)添加帶圓圈序號(hào)列表的實(shí)例代碼

    js動(dòng)態(tài)添加帶圓圈序號(hào)列表的實(shí)例代碼

    這篇文章主要介紹了js動(dòng)態(tài)添加帶圓圈序號(hào)列表的實(shí)例代碼,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-02-02
  • 小程序開發(fā)實(shí)現(xiàn)access_token統(tǒng)一管理

    小程序開發(fā)實(shí)現(xiàn)access_token統(tǒng)一管理

    本文主要介紹了小程序開發(fā)實(shí)現(xiàn)access_token統(tǒng)一管理,文中通過示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-07-07
  • js中bool值的轉(zhuǎn)換及“&&”、“||”、 “!!”詳解

    js中bool值的轉(zhuǎn)換及“&&”、“||”、 “!!”詳解

    這篇文章主要給大家介紹了關(guān)于js中bool值的轉(zhuǎn)換方法以及"&&" 、"||"、 "!!"的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友下面來一起看看吧。
    2017-12-12
  • javascript字符串循環(huán)匹配實(shí)例分析

    javascript字符串循環(huán)匹配實(shí)例分析

    這篇文章主要介紹了javascript字符串循環(huán)匹配,實(shí)例分析三種常用的字符串循環(huán)匹配的使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-07-07

最新評(píng)論