The slice() method returns a shallow copy of a portion of an array into a new array object.git
1、方法 ArraySlice,源碼地址,直接添加到Array.prototype上的「入口」,內部通過參數、類型等等的判斷處理,分支爲SmartSlice和SimpleSlice處理。 github
function ArraySlice(start, end) { CHECK_OBJECT_COERCIBLE(this, "Array.prototype.slice"); var len = TO_UINT32(this.length); var start_i = TO_INTEGER(start); var end_i = len; if (!IS_UNDEFINED(end)) end_i = TO_INTEGER(end);//若是沒傳入end,end=length,即slice第二個參數可選。 if (start_i < 0) { start_i += len;//參數1的A分支 處理負值,+= length。如:爲-1,則start從倒數第一個開始,負值絕對值小於length if (start_i < 0) start_i = 0;//參數1的A.a分支 若仍未負值,則等於0。 即處理負值絕對值大於length [1,2,3].slice(-4)。 } else { if (start_i > len) start_i = len;//參數1的B分支 參數大於length,則等於length,處理 [1,2,3].slice(5),返回[] } if (end_i < 0) { end_i += len;//參數2的A分支 處理負值,+= length。如:爲-1,則start從倒數第一個開始,負值絕對值小於length if (end_i < 0) end_i = 0;//參數2的A.a分支 若仍未負值,則等於0。 即處理負值絕對值大於length [1,2,3].slice(1,-4)。 } else { if (end_i > len) end_i = len;//參數2的B分支 參數大於length,則等於length,處理 [1,2,3].slice(1,5) == [1,2,3].slice(1) == } //最終返回結果的值。能夠看到這裏會返回一個新的真正的數組(ps:slice的好基友splice是修改原數組的。) var result = []; // 處理分支1 若是經歷了上面代碼的層層檢查設置,結束值小於開始值,那麼直接返回空數組,處理 [1,2,3].slice(2,1) if (end_i < start_i) return result; // 處理分支2 若是是數組 && !%IsObserved(this) && 結束大於1000 && %EstimateNumberOfElements(this) < 結束值 ,那麼使用方法SmartSlice來處理 if (IS_ARRAY(this) && !%IsObserved(this) && (end_i > 1000) && (%EstimateNumberOfElements(this) < end_i)) { SmartSlice(this, start_i, end_i - start_i, len, result); } else { // 處理分支2 調用SimpleSlice 處理。 SimpleSlice(this, start_i, end_i - start_i, len, result); } //設置length,彷佛多餘?仍是v8中的數組[] 需指定length。 此處待探尋。。。 result.length = end_i - start_i; return result; } /* * ...... */ // Set up non-enumerable functions of the Array.prototype object and // set their names. // Manipulate the length of some of the functions to meet // expectations set by ECMA-262 or Mozilla. InstallFunctions($Array.prototype, DONT_ENUM, $Array( //...... "slice", getFunction("slice", ArraySlice, 2) //...... ));
2、 SmartSlice,源碼地址,字面意思是智能的slice。SimpleSlice,源碼地址,簡單的slice,無論他們的判斷邏輯,能夠看到,全部的slice處理,都是for循環,操做新建的result空數組的。也就是說,正由於返回值是新建的真實的數組,全部Array.prototype.slice.call(ArrayLike) 纔會將類數組轉化爲真實的數組。數組
1 // This function implements the optimized splice implementation that can use 2 // special array operations to handle sparse arrays in a sensible fashion. 3 /** 4 * 源碼:https://github.com/v8/v8/blob/master/src/array.js#L196-L221 5 * @param {Array} array 具體須要艹作的數組 6 * @param {Number} start_i 參數1,從何處開始 7 * @param {Number} del_count 須要取到的長度。 參數2 - 參數1, 8 * @param {Number} len 數組長度 9 * @param {Array} deleted_elements 對於slice來講,是選擇的那部分數組,對於splice來講,是刪除的那些數組。 10 * @returns {undefined} 此處直接艹作 傳入的reuslt,便可反饋到ArraySlice做用域的result,與真實的瀏覽器環境不同!。 11 */ 12 function SmartSlice(array, start_i, del_count, len, deleted_elements) { 13 // Move deleted elements to a new array (the return value from splice). 14 // 猜想? 獲取start_i + del_count的key。[1,2,3,4].slice(1,2) 返回 [1,2,3,4][1+2]索引3 ,而當tart_i + del_count大於length時候返回整個數組,如[1,2,3,4].slice(2,3) 即[1,2,3,4][5] 返回整個數組 15 var indices = %GetArrayKeys(array, start_i + del_count); 16 if (IS_NUMBER(indices)) { 17 var limit = indices; 18 for (var i = start_i; i < limit; ++i) { 19 var current = array[i]; 20 if (!IS_UNDEFINED(current) || i in array) { 21 deleted_elements[i - start_i] = current; 22 } 23 } 24 } else { 25 var length = indices.length; 26 for (var k = 0; k < length; ++k) { 27 var key = indices[k]; 28 if (!IS_UNDEFINED(key)) { 29 if (key >= start_i) { 30 var current = array[key]; 31 if (!IS_UNDEFINED(current) || key in array) { 32 deleted_elements[key - start_i] = current; 33 } 34 } 35 } 36 } 37 } 38 } 39 40 41 // This is part of the old simple-minded splice. We are using it either 42 // because the receiver is not an array (so we have no choice) or because we 43 // know we are not deleting or moving a lot of elements. 44 /** 45 * 源碼:https://github.com/v8/v8/blob/master/src/array.js#L271-L282 46 * @param {Array} array 具體須要艹作的數組 47 * @param {Number} start_i 參數1,從何處開始 48 * @param {Number} del_count 須要取到的長度。 參數2 - 參數1, 49 * @param {Number} len 數組長度 50 * @param {Array} deleted_elements 對於slice來講,是選擇的那部分數組,對於splice來講,是刪除的那些數組。 51 * @returns {undefined} 此處直接艹作 傳入的reuslt,便可反饋到ArraySlice做用域的result,與真實的瀏覽器環境不同!。 52 */ 53 function SimpleSlice(array, start_i, del_count, len, deleted_elements) { 54 for (var i = 0; i < del_count; i++) { 55 var index = start_i + i; 56 // The spec could also be interpreted such that %HasLocalProperty 57 // would be the appropriate test. We follow KJS in consulting the 58 // prototype. 59 var current = array[index]; 60 if (!IS_UNDEFINED(current) || index in array) { 61 deleted_elements[i] = current; 62 } 63 } 64 }
3、 既然瞭解了實現思路,咱們能夠寫個本身的slice方法,來實現slice的功能,不難看出。「slice.call的做用原理就是,利用call,將slice的方法做用於arrayLike,slice的兩個參數爲空,slice內部解析使得arguments.lengt等於0的時候 至關於處理 slice(0) : 即選擇整個數組,slice方法內部沒有強制判斷必須是Array類型,slice返回的是新建的數組(使用循環取值)」,因此這樣就實現了類數組到數組的轉化,call這個神奇的方法、slice的處理缺一不可,花幾分鐘實現模擬slice以下:瀏覽器
ps: ie低版本,沒法處理dom集合的slice call轉數組。(雖然具備數值鍵值、length 符合ArrayLike的定義,卻報錯)搜索資料獲得?(此處待確認): 由於ie下的dom對象是以com對象的形式實現的,js對象與com對象不能進行轉換 app
(function(global, undefined) { 'use strict'; function SimpleSlice(array, start_i, del_count, len) { var deleted_elements = []; for (var i = 0; i < del_count; i++) { var index = start_i + i; var current = array[index]; if (current !== void(0) || index in array) { deleted_elements[i] = current; } } return deleted_elements; } Array.prototype.mySlice = function(start_i, end_i) { var len = this.length; start_i = start_i === undefined ? 0 : start_i - 0; end_i = end_i === undefined ? len : end_i - 0; if (start_i < 0) { start_i = Math.max(start_i + len, 0); } else if (start_i > len) { start_i = len; } if (end_i < 0) { end_i = Math.max(end_i + len, 0); } else if (end_i > len) { end_i = len; } if (end_i < start_i) return []; return SimpleSlice(this, start_i, end_i - start_i, len); } })(this); var arr = [1,2,3,4,5,6,7,8,9,10]; console.log('test ',arr) console.log(arr.slice(2),arr.mySlice(2)) console.log(arr.slice(6,7),arr.mySlice(6,7)) console.log(arr.slice(-4),arr.mySlice(-4)) console.log(arr.slice(-4,-2),arr.mySlice(-4,-2)); (function(){ console.log('slice call arguments : ',Array.prototype.slice.call(arguments)); console.log('mySlice call arguments : ',Array.prototype.mySlice.call(arguments)); })([],'String',false); console.log(Array.prototype.slice.call({0:'a',length:1}),Array.prototype.mySlice.call({0:'a',length:1}));
在控制檯輸出以下: