1. 程式人生 > >JavaScript精華筆記:ES5陣列新增函式的原始碼實現(1)

JavaScript精華筆記:ES5陣列新增函式的原始碼實現(1)

本篇文章中,對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的元素被建立成新陣列返回了。

剩下的陣列內建函式的原始碼實現,將持續更新,請上個人主頁瀏覽。。。。。