一邊學習前端,一邊通過部落格的形式自己總結一些東西,當然也希望幫助一些和我一樣開始學前端的小夥伴。
如果出現錯誤,請在評論中指出,我也好自己糾正自己的錯誤
author: thomaszhou
- 1. 判斷是否是陣列型別
- 2. 複製陣列(slice和concat)
- 3. 類陣列(NodeList)轉陣列(Array),函式引數arguments轉陣列
- 4. 重複n個字元
- 5. 建立 N x N 二維矩陣,並初始化資料
- 6. 陣列去重、合併陣列去重
陣列的內建方法應用
判斷是否是陣列型別
很多時候我們需要對JavaScript中資料型別( Function、String、Number、Undefined、Boolean和Object
)做判斷。在JavaScript中提供了typeof
操作符可以對這些常用的資料型別做判斷。但要使用typeof
來判斷資料是不是一個陣列,就不起作用了
console.log(typeof function () {return;}); // function
console.log(typeof "a"); // string
console.log(typeof 123); // number
console.log(typeof a); //undefined
console.log(typeof true); // boolean
console.log(typeof NaN); // number
console.log(typeof !NaN); //boolean
console.log(typeof {name:"leo",age: "37"}); // object
console.log(typeof ["leo","37"]); // object
console.log(typeof null); // object
複製程式碼
es5的isArray()函式
var arr = [1,2,3,45];
function isArray(obj){
return Array.isArray(obj);
}
alert(isArray(arr));
複製程式碼
ECMAScript 5 還有著非常大的相容性,所以我們並不能完美的使用原生判斷,當使用ie6/7/8的時候就會出現問題。
construct和instanceof
constructor
屬性返回對建立此物件的函式的引用,使用此屬性可以檢測陣列型別- 除了使用constructor自身屬性之外,還可以使用
instanceof
。 instanceof用來判斷某個建構函式的prototype
是否存在於被檢測物件的原型鏈裡。也就是判斷前面的物件是否是後者物件的類或者例項。
var arr = [1,2,3,45];
console.log(arr.constructor === Array);
console.log(arr instanceof Array);
複製程式碼
- 缺點:需要注意的是,當在不同的window或iframe裡構造的陣列時會失敗。這是因為每一個iframe都有它自己的執行環境,彼此之間並不共享原型鏈,所以此時的判斷一個物件是否為陣列就會失敗。此時我們有一個更好的方式去判斷一個物件是否為陣列。 當你在多個frame中回來跳的時候,這兩種方法就慘了。由於每一個frame都有自己的一套執行環境,跨frame例項化的物件彼此並不共享原型鏈,通過instanceof操作符和constructor屬性檢測的方法自然會失敗。
Object.prototype.toString
var is_array = function(obj){
return Object.prototype.toString.call(obj) === '[object Array]';
};
alert(is_array(arr));
複製程式碼
使用Object.prototype.toString
方法來檢測物件型別。toString將返回[object type]
type
為物件型別。下面是物件檢測顯示的結果。
var toString = Object.prototype.toString;
console.log(toString.call(dd));//[object Function]
console.log(toString.call(new Object));//[object Object]
console.log(toString.call(new Array));//[object Array]
console.log(toString.call(new Date));//[object Date]
console.log(toString.call(new String));//[object String]
console.log(toString.call(Math));//[object Math]
console.log(toString.call(undefined));//[object Undefined]
console.log(toString.call(null));//[object Null]
複製程式碼
因此我們可以利用物件的Object.prototype.toString方法檢測物件是否為陣列。在frame下也通過。
-
最佳檢測 -Array和Object.prototype.toString結合
最佳檢測方法就是,不管原生isArray是否可用,都回歸到object.prototype.toString的檢測上。
Object.prototype.toString
的行為:首先,取得物件的一個內部屬性[[Class]]
,然後依據這個屬性,返回一個類似於"[object Array]"的字串作為結果(看過ECMA標準的應該都知道,[[]]
用來表示語言內部用到的、外部不可直接訪問的屬性,稱為“內部屬性”)。利用這 個方法,再配合call,我們可以取得任何物件的內部屬性[[Class]],然後把型別檢測轉化為字串比較,以達到我們的目的。call
改變toString的this引用為待檢測的物件,返回此物件的字串表示,然後對比此字串是否是[object Array],以判斷其是否是Array的例項。為什麼不直接o.toString()?嗯,雖然Array繼承自Object,也會有toString方法,但是這個方法有可能會被改寫而達不到我們的要求,而Object.prototype則是老虎的屁股,很少有人敢去碰它的,所以能一定程度保證其“純潔性”:)
JavaScript 標準文件中定義: [[Class]] 的值只可能是下面字串中的一個:Arguments, Array, Boolean, Date, Error, Function, JSON, Math, Number, Object, RegExp, String。
var isArray = function () {
if (Array.isArray) {
return Array.isArray;
}
//下面語法不明白
var objectToStringFn = Object.prototype.toString, arrayToStringResult = objectToStringFn.call([]);
return function (subject) {
return objectToStringFn.call(subject) === arrayToStringResult;
};
}();
alert(isArray(arr));// true
複製程式碼
複製陣列
我們都知道陣列是引用型別資料。這裡使用slice,map複製一個陣列,原陣列不受影響。
let list1 = [1, 2, 3, 4];
let newList = list1.slice();//推薦slice
list1.push(5); // [1,2,3,4,5]
console.log(newList); //[1,2,3,4]
console.log(list1); //[1, 2, 3, 4, 5]
let list2 = [5,6,7,8];
let newList2 = list2.concat();
newList2.push(9); //
console.log(newList2); //[5, 6, 7, 8, 9]
console.log(list2); //[1, 2, 3, 4, 5]
複製程式碼
轉換成陣列
類陣列(NodeList)轉陣列(Array),函式引數轉陣列
-
通過三種方式實現:如slice,這裡加多陣列的from方法,ES6語法糖
getElementsByClassName獲取的dom元素的資料型別為HTMLCollection,雖然類似於陣列可以遍歷,但是該資料型別不存在陣列中用於運算元組的方法,通過y以下方法轉換後,就可以使用陣列的內建方法
//返回的不是真正的Array(你無法使用filter、map、reduce等方法)
const nodeList = document.querySelectorAll('div');
// 方法1: 使用Array.from
const arrayList1 = Array.from(nodeList);
// 方法2: 使用slice
const arrayList2 = Array.prototype.slice.call(nodeList);
// 方法3: 使用ES6語法糖
const arrayList3 = [...nodeList];
複製程式碼
-
函式引數 轉成 陣列
函式引數轉陣列 將函式引數轉陣列,利用arguments偽陣列形式,再用slice拷貝為新陣列。
function argsParam() {
//arguments偽陣列形式,再用slice拷貝為新陣列
return Array.prototype.slice.call(arguments);
}
console.log(argsParam(1,2,3,4)); //[1, 2, 3, 4]
複製程式碼
- 那Array.Prototype.slice的內部實現是什麼?
Array.prototype.slice = function(start,end){
var result = new Array();
start = start || 0;
end = end || this.length; //this指向呼叫的物件,當用了call後,能夠改變this的指向,也就是指向傳進來的物件,這是關鍵
for(var i = start; i < end; i++){
result.push(this[i]);
}
return result;
}
複製程式碼
- 其他:Array.prototype.slice.call能將具有length屬性的物件轉成陣列,除了IE下的節點集合(因為ie下的dom物件是以com物件的形式實現的,js物件與com物件不能進行轉換)
var a = {length:2, 0:'zero', 1: 'first'};
console.log(Array.prototype.slice.call(a)); // ["zero", "first"]
var b = {length:3, 0:'zero', 1: 'first', 2 : 'zero'};
console.log(Array.prototype.slice.call(b)); // ["zero", "first", "zero"]
var c = {length:3, 2 : 'zero'};
console.log(Array.prototype.slice.call(c)); // [empty, empty , "zero"]
複製程式碼
重複n個字元
- 利用Array建構函式傳參,再使用join函式分隔指定的字串
/**
@params
num: 重複次數
str: 重複字串
**/
function repeatStr(num, str) {
return new Array(num+1).join(str);
}
console.log(repeatStr(5, 's'));//sssss
複製程式碼
建立n乘n二位矩陣
建立n乘n二維矩陣,並初始化資料 使用Array物件傳入陣列length引數,呼叫fill再用map迴圈fill替換對應的值返回一個新陣列
/**
@params
num: 矩陣次數
str: 矩陣陣列中的值,由於fill函式替換所以值都是一致的
**/
function arrayMatrix(num, matrixStr) {
return Array(num).fill(null).map(() => Array(num).fill(matrixStr));
}
// ["a", "a", "a", "a"] ["a", "a", "a", "a"] ["a", "a", "a", "a"] ["a", "a", "a", "a"]
console.log(arrayMatrix(4, 'a'));
複製程式碼
陣列去重
!!! 方法12345對於數字1和字串1這類的問題,是無法去重的,但是方法6可以解決這個問題,但是也會導致需要設定最後是留下字串的1還是數字1
- 法一:雙層for迴圈(不用新的記憶體空間-但是時間複雜度很高)
- 第一層遍歷陣列,第二層用第一層得到的值來和後面所有的值依次比較,相同則用splice()方法從陣列中提除該重複元素。
- 法二:遍歷陣列法(不用新的記憶體空間-但是時間複雜度很高)
- 實現思路:新建一新陣列,遍歷傳入陣列,遍歷的值如果和新陣列內的元素不重複,就加入該新陣列中;注意:判斷值是否在陣列的方法“indexOf”是ECMAScript5 方法,以下不支援,需多寫一些相容低版本瀏覽器程式碼,
- indexOf就是找到新陣列的第一個某值,方便查詢是否重複
function unique1(array){
var n = []; //一個新的臨時陣列
//遍歷當前陣列
for(var i = 0; i < array.length; i++){
//如果當前陣列的第i已經儲存進了臨時陣列,那麼跳過,
//否則把當前項push到臨時陣列裡面
if (n.indexOf(array[i]) == -1)
n.push(array[i]);
}
return n;
}
複製程式碼
通過unique1.apply(arr)或unique1.call(arr)呼叫,為什麼?
- 法三:陣列下標判斷法
- 還是得呼叫“indexOf”效能跟遍歷陣列法差不多,實現思路:如果當前陣列的第i項在當前陣列中第一次出現的位置不是i,那麼表示第i項是重複的,忽略掉。否則存入結果陣列
unction unique3(arr){
var n = [];
n.push(arr[0]);
len = arr.length;
//從第二項開始遍歷
for(var i = 1; i < len; i++) {
//如果當前陣列的第i項在當前陣列中第一次出現的位置不是i,
//那麼表示第i項是重複的,忽略掉。否則存入結果陣列
if (arr.indexOf(arr[i]) == i) {
n.push(arr[i]);
}
}
return n;
}
console.log(unique3(arr));
複製程式碼
- 方法四:排序後相鄰去除法
- 雖然原生陣列的”sort”方法排序結果不怎麼靠譜,但在不注重順序的去重裡該缺點毫無影響。實現思路:給傳入陣列排序,排序後相同值相鄰,然後遍歷時新陣列只加入不與前一值重複的值。
function unique4(array){
array.sort();
var re=[array[0]];
for(var i = 1; i < array.length; i++){
if( array[i] !== re[re.length-1]){
re.push(array[i]);
}
}
return re;
}
複製程式碼
- 法五:物件鍵值對法(最優):思路如下
- 建立一個JavaScript物件以及新陣列
- 使用for迴圈遍歷原陣列,每次取出一個元素與JavaScript物件的鍵做對比
- 如果不包含,將存入物件的元素的值推入到結果陣列中,並且將存入object物件中該屬性名的值設定為1
// use 'strict';
var arr = ['1',1,2,3,4,4,45,65,'b','a','b'];
function unique4 (arr) {
var newArr = [],// 構建一個新陣列存放結果
object = {};
len = arr.length;
// for迴圈時,每次取出一個元素與物件進行對比
// 如果這個元素不重複,則將它存放到結果數中
// 同時把這個元素的內容作為物件的一個屬性,並賦值為1,
// 存入到第2步建立的物件中
for (var i = 0; i < len; i++) {
// 檢測在object物件中是否包含遍歷到的元素的值
if (!object[typeof arr[i] + arr[i]]) {
// 如果不包含,將存入物件的元素的值推入到結果陣列中
object[typeof arr[i] + arr[i]] = 1
//存入object物件中該屬性名的值設定為1
newArr.push(arr[i]);
}
}
// console.log(object);
return newArr;
}
console.log(unique4(arr));
複製程式碼
test:
var arr = [1,2,3,4,'a','b',1,3,4,56,32,34,2,'b','c',5,'1','2'];
arr.unique3(); // [1, 2, 3, 4, "a", "b", 56, 32, 34, "c", 5, "1", "2"]
複製程式碼
不同的鍵可能會被誤認為一樣;例如: a[1]、a["1"] 。這種方法所費時間:621ms。這種方法所費時間是最短,但就是佔用記憶體大一些
- 法六:不區分字串和數字的去重(物件鍵值法)
- 物件的key,在存入的時候,就自動轉化成string型別,所以像前四種方法針對1和'1'是無法區分,但是如果將值放進物件的key中,1和'1'的比較就會程式設計'1'和'1'的比較,也就是i 字串間的比較
function unique4 (arr) {
var newArr = [],
object = {};
len = arr.length;
for (var i = 0; i < len; i++) {
if (!object[arr[i]]) {
object[arr[i]] = 1;
newArr.push(arr[i]);
}
}
console.log(object);
return newArr;
}
console.log(unique4(arr));
複製程式碼
-
ES6去重
注意set返回的也是一個不重複的類陣列形式要使用Array.from或者別的方法方法轉成陣列形式
function unique (arr) {
return Array.from(new Set(arr))
}
或者:
const unique = arr => [...new Set(arr)];
複製程式碼
Array union (合併陣列去重)
用 a 和 b 的所有值建立一個 Set 並轉換成一個陣列。
const union = (a, b) => Array.from(new Set([...a, ...b]));
// union([1,2,3], [4,3,2]) -> [1,2,3,4]
複製程式碼
過濾陣列中的非唯一值
- 將Array.filter()用於僅包含唯一值的陣列。
- 思路:indexOf和lastIndexOf找的位置相同,那就是唯一的值
const filterNonUnique = arr => arr.filter(i => arr.indexOf(i) === arr.lastIndexOf(i));
// console.log(filterNonUnique([1,2,2,3,4,4,5])); // [1,3,5]
複製程式碼