JavaScript精華筆記:陣列內建函式的原始碼實現
本篇文章中,對forEach、filter、map、Every、Some、reduce和reduceRight等函式,講述瞭如何自己編寫程式碼實現它們的功能。 通過閱讀原始碼,自己編寫原始碼,能瞭解編寫思想、熟悉設計模式,能鍛鍊自己編寫元件、框架的能力。
試驗物件
所有的函式原始碼編寫出來後,以這段資料作為試驗物件:
var personArr = [
{name: '王港', src: './src/img/3.png', des: '頸椎不好', sex: 'm', age: 20},
{name: '劉瑩', src: './src/img/5.png' ,des: '我是誰', sex: 'f', age: 25},
{name: '王秀瑩', src: './src/img/4.png', des: '我很好看', sex: 'f', age: 16},
{name: '劉金雷', src: './src/img/1.png', des: '你沒有見過陌生的臉', sex: 'm', age: 35},
{name: '劉飛翔', src: './src/img/2.png', des: '瓜皮劉', sex: 'm', age: 40}
];
apply函式
所有的原始碼編寫都使用到了apply函式,這裡先對apply函式的作用進行簡單的介紹。 apply(),能夠接受obj和args兩個引數(args為預設引數)。
function.apply(obj,args)
這兩個引數會傳遞給呼叫apply方法的函式function,並按function內容執行(apply裡的引數順序,對應的是函式裡的形參位置)。obj會替代function裡的this物件,args是陣列或零散的引數,這些引數會傳遞給呼叫apply方法的函式。
實現forEach,原始碼編寫
forEach() 方法可以遍歷陣列中的每一個元素,並將傳入的每一個數組元素按呼叫的函式執行。 所以forEach函式可傳入兩個引數:function和Object。而forEach中的引數function又自帶三個形參element,index,arrayself
Array.forEach(function(element,index,arrayself){},Object)
在編寫myForeach之前,我們先定義一個myForeach內的第一個引數,執行元素的function,呼叫myForeach的陣列中的每一個元素,都會被遍歷然後被function執行。
function deal (ele, index, self) {
console.log(ele, index, self, this);
}
這個function的作用是將陣列的每一個元素、索引值、陣列本身及forEach第二個引數obj都列印在控制檯上。 myForeach原始碼如下:
//給陣列新增新的原型方法myForEach
Array.prototype.myForEach = function (func) {
var len = this.length; //獲取陣列的元素個數
var _this = arguments[1] != undefined ? arguments[1] : window;
for (var i = 0;i < len; i++){
//apply第一個引數_this將代替func裡的this物件,而apply第二引數中的this指向的是personArr。
func.apply(_this,[this[i],i,this]);
}
}
這裡要注意,當物件呼叫函式時obj.func();
,func()裡面的this指向obj。在myForEach裡定義了apply函式,記住它的第一位引數是改變this指向的,也就是將通過內部定義的_this將myForEach裡的第二個引數或window傳遞給func。
執行一下這個程式:
var obj = {name : "zc"}; //外部定義一個物件作為myForEach第二個引數
personArr.myForEach(deal,obj);
-------------------------------------------
{name: "王港", src: "./src/img/3.png", des: "頸椎不好", sex: "m", age: 20} 0 (5) [{…}, {…}, {…}, {…}, {…}] {name: "zc"}
{name: "劉瑩", src: "./src/img/5.png", des: "我是誰", sex: "f", age: 25} 1 (5) [{…}, {…}, {…}, {…}, {…}] {name: "zc"}
{name: "王秀瑩", src: "./src/img/4.png", des: "我很好看", sex: "f", age: 16} 2 (5) [{…}, {…}, {…}, {…}, {…}] {name: "zc"}
{name: "劉金雷", src: "./src/img/1.png", des: "你沒有見過陌生的臉", sex: "m", age: 35} 3 (5) [{…}, {…}, {…}, {…}, {…}] {name: "zc"}
{name: "劉飛翔", src: "./src/img/2.png", des: "瓜皮劉", sex: "m", age: 40} 4 (5) [{…}, {…}, {…}, {…}, {…}] {name: "zc"}
在personArr.myForEach裡面,myForEach的this就是指向的personArr。而在myForEach裡又單獨定義了apply,它的第一位引數是改變this指向的,將myForEach裡的第二個引數或window傳遞給func。
實現filter,原始碼編寫
Array.prototype.filter它是對陣列進行過濾,所以要基於陣列的遍歷。filter同樣有引數,引數和forEach是一樣的,是個函式,函式同樣有四個形參function(element,index,arrayself,this), 注意!fileter執行完以後,返回的是一個新的陣列。
Array.filter(function(element,index,arrayself){},Object)
那麼過對forEach的詳細解釋後,接下來就都直接上原始碼和一些小解釋了。 首先,還是先來編寫一個function:
function deal(ele,index,self) {
return ele.sex == "m";
}
函式作用為,陣列中的元素如果性別為male則返回true,否則返回false。 myFilter原始碼內容如下:
Array.prototype.myFilter = function (func) {
var arr = [];//建立一個空陣列
var len = this.length;
//這裡的判斷為前面如果為真,後邊的就不執行了,同樣能達到三目運算子的效果。
var _this = arguments[1] || window;
for (var i = 0;i < len; i++){
//這裡是前邊為真還會繼續向後執行,前面為假後邊就直接不看了。
func.apply(_this,[this[i],i,this]) && arr.push(this[i])
//三目運算子 ? arr.push(this[i] : ""
}
return arr;
}
下面這段有些難理解
func.apply(_this,[this[i],i,this]) && arr.push(this[i])
程式碼轉化一下可以像下面這樣理解,deal函式執行為true則向aar新增元素,執行為false則不執行push函式。
deal([this[i])&& arr.push(this[i])
下面測試一下myFilter函式執行結果:
var obj = {name : "zc"}
var newArr = personArr.myFilter(deal,obj);
------------------------------------------
[{name: "王港", src: "./src/img/3.png", des: "頸椎不好", sex: "m", age: 20},
{name: "劉金雷", src: "./src/img/1.png", des: "你沒有見過陌生的臉", sex: "m", age: 35},
{name: "劉飛翔", src: "./src/img/2.png", des: "瓜皮劉", sex: "m", age: 40}]
性別為male的元素被建立成新陣列返回了。
剩下的陣列內建函式的原始碼實現,將持續更新,請上個人主頁瀏覽。。。。。