国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

繼承工廠實(shí)現(xiàn)-深入理解JS原型鏈繼承原理

Tony / 759人閱讀

摘要:繼承實(shí)現(xiàn)在中,我們可以這樣寫初始函數(shù)使用接收一個(gè)對(duì)象參數(shù)創(chuàng)建了的構(gòu)造函數(shù),之后實(shí)例化調(diào)用函數(shù)輸出。完成繼承操作之后調(diào)用函數(shù)將參數(shù)復(fù)制到的原型上。

功能實(shí)現(xiàn)參考了Leaflet源碼。

功能介紹

我們構(gòu)造一個(gè)Class類,實(shí)現(xiàn)以下功能:

基礎(chǔ)的繼承功能、提供了初始化函數(shù)。

初始函數(shù)鉤子(hook)功能。

內(nèi)置選項(xiàng)的繼承與合并。

靜態(tài)屬性方法。

Mixins。

基礎(chǔ)繼承 JavaScript的繼承

JavaScirpt并不是一個(gè)典型的OOP語言,所以其繼承實(shí)現(xiàn)略為繁瑣,是基于原型鏈的實(shí)現(xiàn),但好在ES6實(shí)現(xiàn)了Class的語法糖,可以方便的進(jìn)行繼承。

Leaflet可能為了瀏覽器的兼容,所以并未采用ES6的語法,同時(shí)也大量使用了[polyfill]的寫法(在[Util.js]中實(shí)現(xiàn))。關(guān)于polyfill,以后進(jìn)行專門介紹。

繼承實(shí)現(xiàn)

在leaflet中,我們可以這樣寫:

let Parent = Class.extend({
  initialize(name) { //初始函數(shù)
    this.name = name;
  },
  greet() {
    console.log("hello " + this.name);
  }
});

let parent = new Parent("whj");
parent.greet(); // hello whj

使用L.Class.extend接收一個(gè)對(duì)象參數(shù)創(chuàng)建了Parent的構(gòu)造函數(shù),之后實(shí)例化調(diào)用greet函數(shù)輸出hello whj。

實(shí)際上L.Class.extend返回了一個(gè)函數(shù)(JavaScript是以函數(shù)實(shí)現(xiàn)類的功能)。

以下是實(shí)現(xiàn)代碼:

function Class() {} // 聲明一個(gè)函數(shù)Class

Class.extend = function (props) { // 靜態(tài)方法extend
  var NewClass = function () {
    if (this.initialize) {
      this.initialize.apply(this, arguments);//因?yàn)椴⒉恢纈nitialize
    }                       //傳入?yún)?shù)數(shù)量,所以使用apply
  } 

  if (props.initialize){
    NewClass.prototype.initialize = props.initialize;
  }

  if (props.greet) {
    NewClass.prototype.greet  = props.greet;
  }

 return NewClass;
};

可以看見Class的靜態(tài)方法extend中,聲明了一個(gè)NewClass函數(shù),之后判斷參數(shù)中是否有initializegreet,并將他們復(fù)制到NewClassprototype中,最后返回。當(dāng)對(duì)返回對(duì)象進(jìn)行new操作時(shí)就會(huì)調(diào)用initialize函數(shù)。這就實(shí)現(xiàn)了最初代碼所展現(xiàn)的功能。

但是,這里傳入?yún)?shù)限定了只有initializegreet才能復(fù)制到其原型上,那么我傳入的參數(shù)不止這兩個(gè)呢?所以得對(duì)代碼進(jìn)行修改,使其通用化,并實(shí)現(xiàn)繼承功能。

Class.extend = function (props) {
  var NewClass = function () {
    if (this.initialize) {
      this.initialize.apply(this, arguments);
    }
  }

 //將父類的prototype取出并復(fù)制到NewClass的__super__ 靜態(tài)變量中
  var parentProto = NewClass.__super__ = this.prototype;
  var proto = Object.create(parentProto); //復(fù)制parentProto到proto中
                       //protos是一個(gè)新的prototype對(duì)象
  proto.constructor = NewClass; 
  NewClass.prototype = proto; //到這完成繼承

  extend(proto, props); //將參數(shù)復(fù)制到NewClass的prototypez中

  return NewClass;
};

將父類的原型prototype取出,Object.create函數(shù)返回了一個(gè)全新的父類原型prototype對(duì)象proto,將其構(gòu)造函數(shù)指向當(dāng)前NewClass,最后將其賦給NewClass的原型,至此完成了繼承工作。注意,此時(shí)NewClass只是繼承了Class。
完成繼承操作之后調(diào)用extend函數(shù)將props參數(shù)復(fù)制到NewClass的原型proto上。

extend函數(shù)實(shí)現(xiàn)如下:

function extend(dest) {
  var i, j, len, src;

  for (j = 1, len = arguments.length; j < len; j++) {
    src = arguments[j];
    for (i in src) {
      dest[i] = src[i];
    }
  }
  return dest;
}

需要注意的是arguments的用法,這是一個(gè)內(nèi)置變量,保存著傳入的所有參數(shù),是一個(gè)類數(shù)組結(jié)構(gòu)。

現(xiàn)在離實(shí)現(xiàn)繼承只差一步了 (?????)? ?? 。

function Class() { }

Class.extend = function (props) {
  var NewClass = function () {
    ...
  }
    ...
 for (var i in this) {
  if (this.hasOwnProperty(i) && i !== "prototype" && i !== "__super__") {
    NewClass[i] = this[i];
    }
  }
  ...
  return NewClass;
};

for循環(huán)中將父類的靜態(tài)方法(不在原型鏈上的、非prototype、非super)復(fù)制到NewClass中。

現(xiàn)在,基本的繼承已經(jīng)實(shí)現(xiàn)。 <(?????)>

測(cè)試代碼:

let Parent = Class.extend({
  initialize(name) {
    this.name = name;
  },
  greet(word) {
    console.log(word + this.name);
  }
});

let Child = Parent.extend({
  initialize(name,age) {
    Parent.prototype.initialize.call(this,name);
    this.age = age;
  },
  greet() {
    Parent.prototype.greet.call(this,this.age);
  }
});

let child = new Child("whj",22);
child.greet(); //22whj
初始函數(shù)鉤子

這個(gè)功能可以在已存在的類中添加新的初始化函數(shù),其子類也繼承了這個(gè)函數(shù)。

let Parent = Class.extend({
  initialize(name) {
    this.name = name;
  },
  greet(word) {
    console.log(word + this.name);
  }
});  // 類已構(gòu)造完成

Parent.addInitHook(function () { //新增init函數(shù)
  console.log("Parent"s other init");
});

let parent = new Parent(); // Parent"s other init

可以看見類實(shí)例化時(shí)執(zhí)行了新增的init函數(shù)。

為了完成這個(gè)功能我們?cè)诖a上進(jìn)行進(jìn)一步修改。

首先在Class上新增addInitHook這個(gè)方法:

Class.addInitHook = function (fn) {
  var init = fn;

  this.prototype._initHooks = this.prototype._initHooks || [];
  this.prototype._initHooks.push(init);
  return this;
};

將新增函數(shù)push進(jìn)_initHooks。_initHooks中的函數(shù)之后會(huì)被依次調(diào)用。

Class.extend = function (props) {
  var NewClass = function () {
    if (this.initialize) {
      this.initialize.apply(this, arguments);
    }
    this.callInitHooks(); // 執(zhí)行調(diào)用新增的init函數(shù)的函數(shù)
  }

  ...

  proto._initHooks = []; // 新增的init函數(shù)數(shù)組

  proto.callInitHooks = function () {
    ...
  };

  return NewClass;
};

首先在原型上新增一個(gè)保存著初始化函數(shù)的數(shù)組 _initHooks、調(diào)用新增初始函數(shù)的方法
callInitHooks,最后在NewClass中調(diào)用callInitHooks。

現(xiàn)在看下callInitHooks的實(shí)現(xiàn):

  proto.callInitHooks = function () {
    if (this._initHooksCalled) { // 是新增函數(shù)否已被調(diào)用
      return;
    }

    if (parentProto.callInitHooks) { //先調(diào)用父類的新增函數(shù)
      parentProto.callInitHooks.call(this);
    }

    this._initHooksCalled = true; // 此init已被調(diào)用,標(biāo)志位置為true

    for (var i = 0, len = proto._initHooks.length; i < len; i++) {
      proto._initHooks[i].call(this); // 循環(huán)調(diào)用新增的初始化函數(shù)
    }
  };

執(zhí)行這段函數(shù)時(shí),先會(huì)遞歸的調(diào)用父類的callInitHooks函數(shù),之后循環(huán)調(diào)用已構(gòu)建好的
_initHooks數(shù)組中的初始函數(shù)。

內(nèi)置選項(xiàng)

首先看下示例程序:

var Parent= Class.extend({
    options: {
        myOption1: "foo",
        myOption2: "bar"
    }
});

var Child = Parent.extend({
    options: {
        myOption1: "baz",
        myOption3: 5
    }
});

var child = new Child ();
child.options.myOption1; // "baz"
child.options.myOption2; // "bar"
child.options.myOption3; // 5

在父類與子類中都聲明了options選項(xiàng),子類繼承其options并覆蓋了父類同名的options。

實(shí)現(xiàn)如下:

Class.extend = function (props) {
  var NewClass = function () {
    ...
  }
  ...
  if (proto.options) {
     props.options = extend(proto.options, props.options);
  }
  ...
  return NewClass;
};

這個(gè)功能有了之前的基礎(chǔ)實(shí)現(xiàn)就相當(dāng)簡(jiǎn)單了。判斷父類是否有optios選項(xiàng),若有者將子類的optios進(jìn)行復(fù)制。

靜態(tài)屬性方法
var MyClass = Class.extend({
  statics: {
      FOO: "bar",
      BLA: 5
  }
});

MyClass.FOO; // "bar"

實(shí)現(xiàn)如下:

Class.extend = function (props) {
  var NewClass = function () {
    ...
  }
  ...
  if (props.statics) {
     extend(NewClass, props.statics);
     delete props.statics;
  }
  ...

  extend(proto, props);

  ...
  return NewClass;
};

實(shí)現(xiàn)與內(nèi)置選項(xiàng)類似,需注意的是extend執(zhí)行之后得把props中的statics字段刪除,以免之后重復(fù)復(fù)制到原型上。

Mixins

Mixins 是一個(gè)在舊類上添加新的屬性、方法的技術(shù)。

 var MyMixin = {
    foo: function () { console.log("foo") },
    bar: 5
};

var MyClass = Class.extend({
    includes: MyMixin
});

// or 
// MyClass.include(MyMixin);

var a = new MyClass();
a.foo(); // foo

實(shí)現(xiàn)與靜態(tài)屬性方法類似:

Class.extend = function (props) {
  var NewClass = function () {
    ...
  }
  ...
  if (props.includes) {
     extend.apply(null, [proto].concat(props.includes));
     delete props.includes;
  }
  extend(proto, props); //將參數(shù)復(fù)制到NewClass的prototypez中
  
  return NewClass;
};

Class.include = function (props) {
   Util.extend(this.prototype, props);
   return this;
};

也是同樣調(diào)用了extend函數(shù),將include復(fù)制到原型中。為什么使用apply方法,主要是為了支持include為數(shù)組的情況。

總結(jié)

Leaflet中繼承功能已全部實(shí)現(xiàn)完成。實(shí)現(xiàn)思路與一些小技巧值得我們借鑒。

這是完整實(shí)現(xiàn)代碼。

文章首發(fā)于Whj"s Website。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/88585.html

相關(guān)文章

  • 前端進(jìn)擊的巨人(七):走進(jìn)面向?qū)ο螅?em>原型與原型,繼承方式

    摘要:除了以上介紹的幾種對(duì)象創(chuàng)建方式,此外還有寄生構(gòu)造函數(shù)模式穩(wěn)妥構(gòu)造函數(shù)模式。 showImg(https://segmentfault.com/img/remote/1460000018196128); 面向?qū)ο?是以 對(duì)象 為中心的編程思想,它的思維方式是構(gòu)造。 面向?qū)ο?編程的三大特點(diǎn):封裝、繼承、多態(tài): 封裝:屬性方法的抽象 繼承:一個(gè)類繼承(復(fù)制)另一個(gè)類的屬性/方法 多態(tài):方...

    wums 評(píng)論0 收藏0
  • JavaScript面向?qū)ο蟮某绦蛟O(shè)計(jì)

    摘要:目錄導(dǎo)語理解對(duì)象和面向?qū)ο蟮某绦蛟O(shè)計(jì)創(chuàng)建對(duì)象的方式的繼承機(jī)制原型對(duì)象原型鏈與原型對(duì)象相關(guān)的方法小結(jié)導(dǎo)語前面的系列文章,基本把的核心知識(shí)點(diǎn)的基本語法標(biāo)準(zhǔn)庫(kù)等章節(jié)講解完本章開始進(jìn)入核心知識(shí)點(diǎn)的高級(jí)部分面向?qū)ο蟮某绦蛟O(shè)計(jì),這一部分的內(nèi)容將會(huì)對(duì)對(duì)象 目錄 導(dǎo)語 1.理解對(duì)象和面向?qū)ο蟮某绦蛟O(shè)計(jì) 2.創(chuàng)建對(duì)象的方式 3.JavaScript的繼承機(jī)制 3.1 原型對(duì)象 3.2 原型鏈 3.3 與...

    gitmilk 評(píng)論0 收藏0
  • 面向?qū)ο蟮?JavaScript

    摘要:是完全的面向?qū)ο笳Z言,它們通過類的形式組織函數(shù)和變量,使之不能脫離對(duì)象存在。而在基于原型的面向?qū)ο蠓绞街校瑢?duì)象則是依靠構(gòu)造器利用原型構(gòu)造出來的。 JavaScript 函數(shù)式腳本語言特性以及其看似隨意的編寫風(fēng)格,導(dǎo)致長(zhǎng)期以來人們對(duì)這一門語言的誤解,即認(rèn)為 JavaScript 不是一門面向?qū)ο蟮恼Z言,或者只是部分具備一些面向?qū)ο蟮奶卣鳌1疚膶⒒貧w面向?qū)ο蟊疽猓瑥膶?duì)語言感悟的角度闡述為什...

    novo 評(píng)論0 收藏0
  • 深入理解JS的面向?qū)ο螅ǜ轮校?/b>

    摘要:的面向?qū)ο笾饕藘蓧K創(chuàng)建對(duì)象繼承。構(gòu)造函數(shù)一般來說,我們可以這樣定義構(gòu)造函數(shù)構(gòu)造函數(shù)的函數(shù)名常大寫在這里,我們沒有顯示的創(chuàng)建對(duì)象,沒有語句,卻將屬性和方法賦值給了。 面向?qū)ο笫擒浖_發(fā)方法。面向?qū)ο蟮母拍詈蛻?yīng)用已超越了程序設(shè)計(jì)和軟件開發(fā),擴(kuò)展到如數(shù)據(jù)庫(kù)系統(tǒng)、交互式界面、應(yīng)用結(jié)構(gòu)、應(yīng)用平臺(tái)、分布式系統(tǒng)、網(wǎng)絡(luò)管理結(jié)構(gòu)、CAD技術(shù)、人工智能等領(lǐng)域。面向?qū)ο笫且环N對(duì)現(xiàn)實(shí)世界理解和抽象的方法...

    陳偉 評(píng)論0 收藏0
  • js對(duì)象詳解(JavaScript對(duì)象深度剖析,深度理解js對(duì)象)

    摘要:對(duì)象詳解對(duì)象深度剖析,深度理解對(duì)象這算是醞釀很久的一篇文章了。用空構(gòu)造函數(shù)設(shè)置類名每個(gè)對(duì)象都共享相同屬性每個(gè)對(duì)象共享一個(gè)方法版本,省內(nèi)存。 js對(duì)象詳解(JavaScript對(duì)象深度剖析,深度理解js對(duì)象) 這算是醞釀很久的一篇文章了。 JavaScript作為一個(gè)基于對(duì)象(沒有類的概念)的語言,從入門到精通到放棄一直會(huì)被對(duì)象這個(gè)問題圍繞。 平時(shí)發(fā)的文章基本都是開發(fā)中遇到的問題和對(duì)...

    CatalpaFlat 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<