前端中的简单编程题-数组(1)

本文分享了前端开发中实用技巧,包括数组操作、类型检测、数组去重等,适合初学者和有一定经验的开发者参考。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一边学习前端,一边通过博客的形式自己总结一些东西,当然也希望帮助一些和我一样开始学前端的小伙伴。

如果出现错误,请在评论中指出,我也好自己纠正自己的错误

author: thomaszhou

数组的内置方法应用

判断是否是数组类型

很多时候我们需要对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]
复制代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值