亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

從Vue數(shù)組響應(yīng)化所引發(fā)的思考

hikui / 2514人閱讀

摘要:因?yàn)闊o(wú)法通過(guò)借用構(gòu)造函數(shù)的方式創(chuàng)建響應(yīng)式屬性雖然屬性可以被創(chuàng)建,但不具備響應(yīng)式功能,因此在我們是沒法繼承數(shù)組的。上面整個(gè)的文章都是基于監(jiān)聽數(shù)組響應(yīng)的一個(gè)點(diǎn)想到的。

前言

  首先歡迎大家關(guān)注我的Github博客,也算是對(duì)我的一點(diǎn)鼓勵(lì),畢竟寫東西沒法獲得變現(xiàn),能堅(jiān)持下去也是靠的是自己的熱情和大家的鼓勵(lì)。

  從上一篇文章響應(yīng)式數(shù)據(jù)與數(shù)據(jù)依賴基本原理開始,我就萌發(fā)了想要研究Vue源碼的想法。最近看了youngwind的一篇文章如何監(jiān)聽一個(gè)數(shù)組的變化發(fā)現(xiàn)Vue早期實(shí)現(xiàn)監(jiān)聽數(shù)組的方式和我的實(shí)現(xiàn)稍有區(qū)別。并且在兩年前作者對(duì)其中的一些代碼的理解有誤,在閱讀完評(píng)論中@Ma63d的評(píng)論之后,感覺收益匪淺。

Vue實(shí)現(xiàn)數(shù)據(jù)監(jiān)聽的方式

  在我們的上一篇文章中,我們想嘗試監(jiān)聽數(shù)組變化,采用的是下面的思路:

function observifyArray(array){
  //需要變異的函數(shù)名列表
  var methods = ["push", "pop", "shift", "unshift", "splice", "sort", "reverse"];
  var arrayProto = Object.create(Array.prototype);
  _.each(methods, function(method){
    arrayProto[method] = function(...args){
      // 劫持修改數(shù)據(jù)
      var ret = Array.prototype[method].apply(this, args);
      //可以在修改數(shù)據(jù)時(shí)觸發(fā)其他的操作
      console.log("newValue: ", this);
      return ret;
    }
  });
  Object.setPrototypeOf(array, arrayProto);
}

  我們是通過(guò)為數(shù)組實(shí)例設(shè)置原型prototype來(lái)實(shí)現(xiàn),新的prototype重寫了原生數(shù)組原型的部分方法。因此在調(diào)用上面的幾個(gè)變異方法的時(shí)候我們會(huì)得到相應(yīng)的通知。但其實(shí)setPrototypeOf方法是ECMAScript 6的方法,肯定不是Vue內(nèi)部可選的實(shí)現(xiàn)方案。我們可以大致看看Vue的實(shí)現(xiàn)思路。

function observifyArray(array){
    var aryMethods = ["push", "pop", "shift", "unshift", "splice", "sort", "reverse"];
    var arrayAugmentations = Object.create(Array.prototype);
    
    aryMethods.forEach((method)=> {
    
        // 這里是原生Array的原型方法
        let original = Array.prototype[method];
       // 將push, pop等封裝好的方法定義在對(duì)象arrayAugmentations的屬性上
       // 注意:是屬性而非原型屬性
        arrayAugmentations[method] = function () {
            console.log("我被改變啦!");
            // 調(diào)用對(duì)應(yīng)的原生方法并返回結(jié)果
            return original.apply(this, arguments);
        };
    });
    array.__proto__ = arrayAugmentations;
}

  __proto__是我們大家的非常熟悉的一個(gè)屬性,其指向的是實(shí)例對(duì)象對(duì)應(yīng)的原型對(duì)象。在ES5中,各個(gè)實(shí)例中存在一個(gè)內(nèi)部屬性[[Prototype]]指向?qū)嵗龑?duì)象對(duì)應(yīng)的原型對(duì)象,但是內(nèi)部屬性是沒法訪問(wèn)的。瀏覽器各家廠商都支持非標(biāo)準(zhǔn)屬性__proto__。其實(shí)Vue的實(shí)現(xiàn)思路與我們的非常相似。唯一不同的是Vue使用了的非標(biāo)準(zhǔn)屬性__proto__。

  其實(shí)閱讀過(guò)《JavaScript高級(jí)程序設(shè)計(jì)》的同學(xué)應(yīng)該還記得原型式繼承。其重要思路就是借助原型可以基于已有的對(duì)象創(chuàng)建對(duì)象。比如說(shuō):

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

  其實(shí)我們上面Vue的思路也是這樣的,我們借助原型創(chuàng)建的基于arrayAugmentations的新實(shí)例,使得實(shí)例能夠訪問(wèn)到我們自定義的變異方法。

  上面一篇文章的作者youngwind寫文章的時(shí)候就提出了,為什么不去采用更為常見的組合式繼承去實(shí)現(xiàn),比如:

function FakeArray() {
    Array.apply(this,arguments);
}

FakeArray.prototype = [];
FakeArray.prototype.constructor = FakeArray;

FakeArray.prototype.push = function () {
    console.log("我被改變啦");
    return Array.prototype.push.apply(this,arguments);
};

let list = ["a","b","c"];

let fakeList = new FakeArray(list);

  結(jié)果發(fā)現(xiàn)fakeList并不是一個(gè)數(shù)組而是一個(gè)對(duì)象,作者當(dāng)時(shí)這這樣認(rèn)為的:

構(gòu)造函數(shù)默認(rèn)返回的本來(lái)就是this對(duì)象,這是一個(gè)對(duì)象,而非數(shù)組。Array.apply(this,arguments);這個(gè)語(yǔ)句返回的才是數(shù)組

我們能不能將Array.apply(this,arguments);直接return出來(lái)呢?

如果我們r(jià)eturn這個(gè)返回的數(shù)組,這個(gè)數(shù)組是由原生的Array構(gòu)造出來(lái)的,所以它的push等方法依然是原生數(shù)組的方法,無(wú)法到達(dá)重寫的目的。

首先我們知道采用new操作符調(diào)用構(gòu)造函數(shù)會(huì)依次經(jīng)歷以下四個(gè)步驟:

創(chuàng)建新對(duì)象

將構(gòu)造函數(shù)的作用域給對(duì)象(因此構(gòu)造函數(shù)中的this指向這個(gè)新對(duì)象)

執(zhí)行構(gòu)造函數(shù)的代碼

返回新對(duì)象(如果沒有顯式返回的情況下)

  在沒有顯式返回的時(shí)候,返回的是新對(duì)象,因此fakeList是對(duì)象而不是數(shù)組。但是為什么不能強(qiáng)制返回Array.apply(this,arguments)。其實(shí)下面有人說(shuō)作者這句話有問(wèn)題

這個(gè)數(shù)組是由原生的Array構(gòu)造出來(lái)的,所以它的push等方法依然是原生數(shù)組的方法,無(wú)法到達(dá)重寫的目的。

  其實(shí)上面這句話本身確實(shí)沒有錯(cuò)誤,當(dāng)我們給構(gòu)造函數(shù)顯式返回的時(shí)候,我們得到的fakeList就是原生的數(shù)組。因此調(diào)用push方法是沒法觀測(cè)到的。但是我們不能返回的Array.apply(this,arguments)更深層的原因在于我們這邊調(diào)用Array.apply(this,arguments)的目的是為了借用原生的Array的構(gòu)造函數(shù)將Array屬性賦值到當(dāng)前對(duì)象上。

舉一個(gè)例子:

function Father(){
 this.name = "Father";
}

Father.prototype.sayName = function(){
 console.log("name: ", this.name);
}

function Son(){
 Father.apply(this);
 this.age = 100;
}

Son.prototype = new Father();
Son.prototype.constructor = Son;
Son.prototype.sayAge = function(){
 console.log("age: ", this.age);
}


var instance = new Son();
instance.sayName(); //name:  Father
instance.sayAge(); //age:  100

  子類Son為了繼承父類Father的屬性和方法兩次調(diào)用Father的構(gòu)造函數(shù),Father.apply(this)就是為了創(chuàng)建父類的屬性,而Son.prototype = new Father();目的就是為了通過(guò)原型鏈繼承父類的方法。因此上面所說(shuō)的才是為什么不能將Array.apply(this,arguments)強(qiáng)制返回的原因,它的目的就是借用原生的Array構(gòu)造函數(shù)創(chuàng)建對(duì)應(yīng)的屬性。

  但是問(wèn)題來(lái)了,為什么無(wú)法借用原生的Array構(gòu)造函數(shù)創(chuàng)建對(duì)象呢?實(shí)際上不僅僅是Array,String、NumberRegexp、Object等等JavaScript的內(nèi)置類都不能通過(guò)借用構(gòu)造函數(shù)的方式創(chuàng)建帶有功能的屬性(例如: length)。JavaScript數(shù)組中有一個(gè)特殊的響應(yīng)式屬性length,一方面如果數(shù)組數(shù)值類型下標(biāo)的數(shù)據(jù)發(fā)生變化的時(shí)候會(huì)在length上體現(xiàn),另一方面,修改length也會(huì)影響到數(shù)組的數(shù)值數(shù)據(jù)。因?yàn)闊o(wú)法通過(guò)借用構(gòu)造函數(shù)的方式創(chuàng)建響應(yīng)式length屬性(雖然屬性可以被創(chuàng)建,但不具備響應(yīng)式功能),因此在E55我們是沒法繼承數(shù)組的。比如:

function MyArray(){
    Array.apply(this, arguments);
}

MyArray.prototype = Object.create(Array.prototype, {
    constructor: {
        value: MyArray,
        writable: true,
        configurable: true,
        enumerable: true
    }
});

var colors = new MyArray();
colors[0] = "red"; 
console.log(colors.length); // 0

colors.length = 0;
console.log(colors[0]); //"red"

  好在我們迎來(lái)ES6的曙光,通過(guò)類class的extends,我們就可以實(shí)現(xiàn)繼承原生的數(shù)組,例如:

class MyArray extends Array {
}

var colors = new MyArray();
colors[0] = "red";
console.log(colors.length); // 0

colors.length = 0;
cosole.log(colors[0]); // undefined

  為什么ES6的extends可以做到ES5所不能實(shí)現(xiàn)的數(shù)組繼承呢?這是由于二者的繼承原理不同導(dǎo)致的。ES5的繼承方式中,先是生成派生類型的this(例如:MyArray),然后調(diào)用基類的構(gòu)造函數(shù)(例如:Array.apply(this)),這也就是說(shuō)this首先指向的是派生類的實(shí)例,然后指向的是基類的實(shí)例。由于原生對(duì)象(例如: Array)通過(guò)借用的方式并不能給this賦值length類似的具有功能的屬性,因此我們沒法實(shí)現(xiàn)想要的結(jié)果。

  但是ES6的extends的繼承方式卻是與之相反的,首先是由基類(Array)創(chuàng)建this的值,然后再由派生類的構(gòu)造函數(shù)修改這個(gè)值,因此在上面的例子中,一開始就可以通過(guò)this創(chuàng)建基類的所有內(nèi)建功能并接受與之相關(guān)的功能(如length),然后在此this的基礎(chǔ)上用派生類進(jìn)行擴(kuò)展,因此就可以達(dá)到我們的繼承原生數(shù)組的目的。

  不僅僅如此。ES6在擴(kuò)展類似上面的原生對(duì)象時(shí)還提供了一個(gè)非常方便的屬性: Symbol.species。

Symbol.species

  Symbol.species的主要作用就是可以使得原本返回基類實(shí)例的繼承方法返回派生類的實(shí)例,舉個(gè)例子吧,比如Array.prototype.slice返回的就是數(shù)組的實(shí)例,但是當(dāng)MyArray繼承Array時(shí),我們也希望當(dāng)使用MyArray的實(shí)例調(diào)用slice時(shí)也能返回MyArray的實(shí)例。那我們?cè)撊绾问褂媚?,其?shí)Symbol.species是一個(gè)靜態(tài)訪問(wèn)器屬性,只要在定義派生類時(shí)定義,就可以實(shí)現(xiàn)我們的目的。比如:

class MyArray extends Array {
  static get [Symbol.species](){
    return this;
  }
}

var myArray = new MyArray(); // MyArray[]
myArray.slice(); // MyArray []

  我們可以發(fā)現(xiàn)調(diào)用數(shù)組子類的實(shí)例myArrayslice方法時(shí)也會(huì)返回的是MyArray類型的實(shí)例。如果你喜歡嘗試的話,你會(huì)發(fā)現(xiàn)即使去掉了靜態(tài)訪問(wèn)器屬性get [Symbol.species],myArray.slice()也會(huì)仍然返回MyArray的實(shí)例,這是因?yàn)榧词鼓悴伙@式定義,默認(rèn)的Symbol.species屬性也會(huì)返回this。當(dāng)然你也將this改變?yōu)槠渌祦?lái)改變對(duì)應(yīng)方法的返回的實(shí)例類型。例如我希望實(shí)例myArrayslice方法返回的是原生數(shù)組類型Array,就可以采用如下的定義:

class MyArray extends Array {
  static get [Symbol.species](){
    return Array;
  }
}

var myArray = new MyArray(); // []
myArray.slice(); // []

  當(dāng)然了,如果在上面的例子中,如果你希望在自定義的函數(shù)中返回的實(shí)例類型與Symbol.species的類型保持一致的話,可以如下定義:

class MyArray extends Array {
  static get [Symbol.species](){
    return Array;
  }
  
  constructor(value){
    super();
    this.value = value;
  }
  
  clone(){
    return new this.constructor[Symbol.species](this.value)
  }
}

var myArray = new MyArray();
myArray.clone(); //[]

  通過(guò)上面的代碼我們可以了解到,在實(shí)例方法中通過(guò)調(diào)用this.constructor[Symbol.species]我們就可以獲取到Symbol.species繼而可以創(chuàng)造對(duì)應(yīng)類型的實(shí)例。

  上面整個(gè)的文章都是基于監(jiān)聽數(shù)組響應(yīng)的一個(gè)點(diǎn)想到的。這里僅僅是起到拋磚引玉的作用,希望能對(duì)大家有所幫助。如有不正確的地方,歡迎大家指出,愿共同學(xué)習(xí)。

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/95443.html

相關(guān)文章

  • Vue響應(yīng)式數(shù)據(jù): Observer模塊實(shí)現(xiàn)

    摘要:響應(yīng)式數(shù)據(jù)是在模塊中實(shí)現(xiàn)的我們可以看看是如何實(shí)現(xiàn)的。早期代碼使用是進(jìn)行單元測(cè)試,是事件模型的單元測(cè)試文件。模塊實(shí)際上采用采用組合繼承借用構(gòu)造函數(shù)原型繼承方式繼承了其目的就是繼承的,等方法。 前言   首先歡迎大家關(guān)注我的Github博客,也算是對(duì)我的一點(diǎn)鼓勵(lì),畢竟寫東西沒法獲得變現(xiàn),能堅(jiān)持下去也是靠的是自己的熱情和大家的鼓勵(lì)。接下來(lái)的日子我應(yīng)該會(huì)著力寫一系列關(guān)于Vue與React內(nèi)部原...

    shinezejian 評(píng)論0 收藏0
  • vue 子組件修改props引用類型參數(shù)引發(fā)思考

    摘要:但是當(dāng)傳遞的參數(shù)為對(duì)象或者數(shù)組的時(shí)候,是通過(guò)引用傳入的,所以對(duì)于一個(gè)引用類型的來(lái)說(shuō),在子組件中改變這個(gè)參數(shù)本身將會(huì)影響到父組件的數(shù)據(jù)狀態(tài)。 問(wèn)題 父級(jí)組件與子組件的通信一般都是通過(guò)props來(lái)實(shí)現(xiàn)的,因?yàn)閿?shù)據(jù)流向的單一才能保證數(shù)據(jù)變化的可追蹤性,在vue中props遵循的是單向數(shù)據(jù)流,原則上子組件修改props是不被允許的。但是當(dāng)props傳遞的參數(shù)為對(duì)象或者數(shù)組的時(shí)候,是通過(guò)引用傳入...

    周國(guó)輝 評(píng)論0 收藏0
  • 一次線上問(wèn)題排查所引發(fā)思考

    摘要:直到有一天你會(huì)碰到線上奇奇怪怪的問(wèn)題,如線程執(zhí)行一個(gè)任務(wù)遲遲沒有返回,應(yīng)用假死。正好這次借助之前的一次生產(chǎn)問(wèn)題來(lái)聊聊如何排查和解決問(wèn)題。本地模擬上文介紹的是線程相關(guān)問(wèn)題,現(xiàn)在來(lái)分析下內(nèi)存的問(wèn)題。盡可能的減少多線程競(jìng)爭(zhēng)鎖。 showImg(https://segmentfault.com/img/remote/1460000015568421?w=2048&h=1150); 前言 之前或...

    levy9527 評(píng)論0 收藏0
  • 記一次思否問(wèn)答問(wèn)題思考Vue為什么不能檢測(cè)數(shù)組變動(dòng)

    摘要:這里加了個(gè)簡(jiǎn)單判斷,只看數(shù)組元素的,然后寫了一個(gè)簡(jiǎn)單案例,主要測(cè)試使用改變數(shù)組元素能不能被監(jiān)測(cè)到,并響應(yīng)式的渲染頁(yè)面運(yùn)行頁(yè)面可以看到,運(yùn)行了次,我們數(shù)組長(zhǎng)度為,也就是說(shuō)數(shù)組被遍歷了兩遍。 問(wèn)題來(lái)源:https://segmentfault.com/q/10... 問(wèn)題描述:Vue檢測(cè)數(shù)據(jù)的變動(dòng)是通過(guò)Object.defineProperty實(shí)現(xiàn)的,所以無(wú)法監(jiān)聽數(shù)組的添加操作是可以理解的...

    raoyi 評(píng)論0 收藏0
  • 一道JS試題引發(fā)思考

    摘要:也給當(dāng)初出入迷宮的我不小考驗(yàn),一道題目可以引發(fā)許多思考,今天寫下的只是今時(shí)今日的想法,到未來(lái)也許還有別樣的看法。對(duì)于回調(diào)函數(shù),可以對(duì)其傳入三個(gè)參數(shù)分別是當(dāng)前元素,元素索引,調(diào)用的數(shù)組。 [1,2,3].map(parseInt) 這道JS題目,相信大家并不會(huì)陌生。也給當(dāng)初出入JS迷宮的我不小考驗(yàn),一道題目可以引發(fā)許多思考,今天寫下的只是今時(shí)今日的想法,到未來(lái)也許還有別樣的看法。...

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

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

0條評(píng)論

閱讀需要支付1元查看
<