亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

javascript框架設計之類工廠

2019-11-20 12:11:47
字體:
來源:轉載
供稿:網友

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

1.javascript對類的支持

在其它語言中 ,類的實例都要通過構造函數new出來。作為一個刻意模仿java的語言。javascript存在new操作符,并且所有函數都可以作為構造器。構造函數與普通的方法沒有什么區別。瀏覽器為了構建它繁花似錦的生態圈,比如Node,Element,HTMLElement,HTMLParagraphElement,顯然使用繼承關系方便一些方法或屬性的共享,于是javascript從其它語言借鑒了原型這種機制。Prototype作為一個特殊的對象屬性存在于每一個函數上。當一個函數通過new操作符new出其“孩子”――“實例”,這個名為實例的對象就擁有這個函數的Prototype對象所有的一切成員,從而實現實現所有實例對象都共享一組方法或屬性。而javascript所謂的“類”就是通過修改這個Prototype對象,以區別原生對象及其其它定義的“類”。在瀏覽器中,node這個類基于Object修改而來的,而Element則是基于Node,而HTMLElement又基于Element....相對我們的工作業務,我們可以創建自己的類來實現重用與共享。

  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)

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

  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 引用類型,每次進入函數體都要重新創建,因此都不一樣。  console.log(a.method === b.method); //false

特權方法或屬性只是只是遮住原型的方法或屬性,因此只要刪掉特權方法,就能方法到同名的原型方法或屬性。

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

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

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

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

  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;

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

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

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

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

方法二,就在原型上動腦筋,因此稱之為原型繼承。下面是個范本

  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); //為父類動態添加新的方法  A.prototype.bb = function () {    alert(2)  }  //true,繼承父類的方法  B.prototype.cc = function (){    alert(3)  }  //false 父類未必有子類的new實例  console.log(a.cc === b.cc)  //并且它能夠正常通過javascript自帶的驗證機制instanceof  console.log(b instanceof A) ;//true  console.log(b instanceof B) ; //true

方法二能通過instanceof驗證,es5就內置了這種方法來實現原型繼承,它就是Object.create,如果不考慮第二個參數,它約等于下面的代碼。

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

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

不過,我們這樣還是遺漏了一點東西――子類不只是繼承父類的遺產,還應該有自己的東西,此外,原型繼承并沒有讓子類繼承父類的成員與特權成員。這些我們都得手動添加,如類成員,我們可以通過上面的extend方法,特權成員我們可以在子類構造器中,通過apply實現。

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

下面,做一組實驗,測試下實例的回溯機制。當我們訪問對象的一個屬性,那么他先尋找其特權成員,如果有同名就返回,沒有就找原型,再沒有,就找父類的原型...我們嘗試將它的原型臨時修改下,看它的屬性會變成那個。

  function A(){  }  A.prototype = {    aa:1  }  var a = new A;  console.log(a.aa) ; //=>1  //將它的所有原型都替換掉  A.prototype = {    aa:2  }  console.log(a.aa); //=>1  //于是我們想到每個實例都有一個constructor方法,指向其構造器  //而構造器上面正好有我們的原型,javascript引擎是不是通過該路線回溯屬性呢  function B(){  }  B.prototype = {    aa:3  }  a.constructor = B;  console.log(a.aa) //1 表示不受影響

因此類的實例肯定通過另一條通道進行回溯,翻看ecma規范可知每一個對象都有一個內部屬性[[prototype]],它保存這我們new它時的構造器所引用的Prototype對象。在標準瀏覽器與IE11里,它暴露了一個叫__proto__屬性來訪問它。因此,只要不動__proto__上面的代碼怎么動,a.aa始終堅定不毅的返回1.

再看一下,new時操作發生了什么。

1.創建了一個空對象 instance
2.instance.__proto__ = intanceClass.prototype
3.將構造函數里面的this = instance
4.執行構造函數里的代碼
5.判定有沒有返回值,沒有返回值就返回默認值為undefined,如果返回值為復合數據類型,則直接返回,否則返回this
于是有了下面的結果。

  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; //刪除特權屬性,暴露原型鏈上的同名屬性  console.log(a.aa) //=>3

有了__proto__,我們可以將原型設計繼承設計得更簡單,我們還是拿上面的例子改一改,進行試驗

  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 得到父類的原型對象

因為b.__proto__.constructor為B,而B的原型是從bridge中得來的,而bride.prototype = A.prototype,反過來,我們在定義時,B.prototype.__proto__ = A.prototype,就能輕松實現兩個類的繼承.

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

2.各種類工廠的實現。

上節我們演示了各種繼承方式的實現,但都很凌亂。我們希望提供一個專門的方法,只要用戶傳入相應的參數,或按照一定簡單格式就能創建一個類。特別是子類。

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

相當精巧的庫,P.js

https://github.com/jiayi2/pjs

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

這是一個相當精巧的庫,尤其調用父類的同名方法時,它直接將父類的原型拋在你面前,連_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);

我們嘗試創建一個類:

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

我們在現在的情況下,可以嘗試創建更簡潔的定義方式

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

私有屬性演示,由于放在函數體內集中定義,因此安全可靠!

  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

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

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
97在线观看视频| 日韩欧美中文第一页| 欧美亚洲国产另类| 欧美高清视频在线播放| 高清一区二区三区日本久| 美日韩在线视频| 欧美性资源免费| 久久久久999| 中文字幕亚洲欧美一区二区三区| 色综合久久中文字幕综合网小说| 国产婷婷色综合av蜜臀av| 两个人的视频www国产精品| 久久人人爽人人爽人人片av高清| 国产午夜精品免费一区二区三区| 欧美一级黑人aaaaaaa做受| 精品中文字幕在线观看| 欧美成人激情视频| 国产精品免费久久久久久| 色与欲影视天天看综合网| 91成人国产在线观看| 91久久久亚洲精品| 久久久久久有精品国产| 欧美高清电影在线看| 日本高清+成人网在线观看| 久久免费视频在线| 欧美日韩在线观看视频| 国产69久久精品成人看| 日韩高清人体午夜| 欧美日韩高清在线观看| 亚洲综合日韩在线| 国产精品日韩在线播放| 亚洲精品98久久久久久中文字幕| 亚洲а∨天堂久久精品喷水| 久久久久久久久国产精品| 日本精品一区二区三区在线播放视频| 国产精品99久久久久久久久久久久| 日本伊人精品一区二区三区介绍| 国内精品久久久久伊人av| 国产成人免费av| 97香蕉久久超级碰碰高清版| 91美女片黄在线观| 欧美日韩国内自拍| 一本色道久久综合亚洲精品小说| 欧美午夜www高清视频| 日韩精品在线观| 这里精品视频免费| 国产欧美精品xxxx另类| 日韩久久免费视频| 国产视频精品va久久久久久| 日韩电影第一页| 欧美日韩电影在线观看| 一区二区欧美激情| 欧美天天综合色影久久精品| 国产精品丝袜白浆摸在线| 亚洲视频网站在线观看| 亚洲精品视频网上网址在线观看| 欧美一级大片在线观看| 精品国内自产拍在线观看| 色综合久久久久久中文网| 国产精品精品一区二区三区午夜版| 最近2019中文免费高清视频观看www99| 中文字幕久精品免费视频| 精品国产乱码久久久久久虫虫漫画| 亚洲免费福利视频| 欧美福利在线观看| 亚洲三级av在线| 8050国产精品久久久久久| 国产精自产拍久久久久久| 中文字幕在线亚洲| 中文字幕精品影院| 日韩中文字幕视频在线观看| 亚洲a在线播放| 日韩av网址在线| 亚洲片在线观看| 国产网站欧美日韩免费精品在线观看| 国产欧美日韩精品在线观看| 91嫩草在线视频| 国语自产精品视频在免费| 蜜臀久久99精品久久久久久宅男| 成人一区二区电影| 久久成人精品一区二区三区| 欧美性一区二区三区| 精品久久久久久久久久久久| 色yeye香蕉凹凸一区二区av| 日韩精品欧美激情| 亚洲另类图片色| 国产精品视频99| 亚洲二区在线播放视频| 国产女精品视频网站免费| 亚洲香蕉av在线一区二区三区| 欧美人成在线视频| 久久人人爽人人爽人人片av高清| 亚洲人成啪啪网站| 国产精品日韩欧美| 自拍亚洲一区欧美另类| 国产精品v片在线观看不卡| 中文亚洲视频在线| 欧美日韩国产va另类| 日韩av在线看| 亚洲性生活视频在线观看| 国产精品一区二区久久精品| 国产精品久久久久久久久久尿| 一二美女精品欧洲| 日本精品久久久久影院| 欧美老女人www| 日本精品视频在线| 欧美成在线观看| 美女福利视频一区| 国产视频久久久久久久| 国内精久久久久久久久久人| 91九色蝌蚪国产| 中文字幕日韩高清| 成人激情av在线| 午夜精品一区二区三区在线视| 激情懂色av一区av二区av| 欧美麻豆久久久久久中文| 国外成人在线直播| 日韩精品在线第一页| 亚洲最大av网站| 神马久久久久久| 国产免费一区二区三区在线观看| 国产亚洲激情视频在线| 国产在线精品自拍| 成人妇女免费播放久久久| 91亚洲精品视频| 亚洲国产精品va在线| 欧美激情精品久久久久久免费印度| 最近2019年手机中文字幕| 亚洲国产女人aaa毛片在线| 中文字幕在线国产精品| 亚洲精品国产成人| 国产综合色香蕉精品| 日韩av免费在线| 日韩免费黄色av| 亚洲午夜激情免费视频| 亚洲天堂精品在线| 中文字幕久久久| 在线成人激情视频| 亚洲午夜国产成人av电影男同| 欧美另类精品xxxx孕妇| 亚洲变态欧美另类捆绑| 欧美日韩成人精品| 国产精品久久一| 久久久999精品| 久久躁日日躁aaaaxxxx| 久久免费精品视频| 国产精品久久久av久久久| 亚洲精品suv精品一区二区| 日本一区二区三区在线播放| 亚洲一级黄色片| 中文字幕日韩欧美在线| 久久偷看各类女兵18女厕嘘嘘| 国产精品久久久久国产a级| 色中色综合影院手机版在线观看| 国产成人短视频| 美乳少妇欧美精品| 色综合老司机第九色激情| 精品国产精品自拍| 成人情趣片在线观看免费| 中文字幕在线国产精品| 欧美成人一区二区三区电影| 91精品国产91久久久久久| 日本不卡高字幕在线2019| 亚洲va欧美va国产综合剧情|