摘要:唯一需要注意的的是回调函数需要有值,否则新数组都是。唯一需要注意的的是回调函数需要布尔值或,如果忘记写语句,返回得到的是空数组,表示一个都不匹配。
JavaScript数组的应用应该都比较熟悉了。
• forEach,map,filter
• some,every
• reduce,reduceRight
引用块内容
• slice,splice
• indexOf,lastIndexOf
• sort
• 类数组对象
forEach,map,filter
forEach遍历数组,函数声明:[].forEach( function(value, index, array) { … }, [thisArg] );。
第一个参数是回调函数,它支持3个参数,第1个是遍历的数组内容,第2个是对应索引,第3个是数组自身。
第二个参数thisArg可选,可用于以改变回调函数里面的this指针
因为forEach是第一个被介绍的数组方法,所以稍微详细点用console.log看一下回调函数的3个参数。(之后的数组方法有兴趣的可以自己用console.log看一下回调函数,不赘述)
[1, 2 ,3, 4].forEach(console.log); // 1, 0, [1, 2, 3, 4] // 2, 1, [1, 2, 3, 4] // 3, 2, [1, 2, 3, 4] // 4, 3, [1, 2, 3, 4]
上面已经清晰地展现了遍历的结果,第一列是value,第二列是对应的index值,第三列是数组本身。
现在用forEach实现数组求和:
var price = 0; [1, 2, 3, 4].forEach(function (value) { price += value; }); console.log(price); //10
相比for循环,上述代码除了更简单外,还避免了常见的for循环的起始,终止条件越界等错误。对于数组遍历来说,forEach和map是优于for循环的。
现在看看第二个参数thisArgs的作用,如果不指定该参数,回调函数内的this指向的是window(关于this可以参照这里),例如上例中的回调函数里,你可以写成this.price += value;,效果是一样的(当然前提是变量确实是window的全局属性)。但有时this指向window就不对了,如下:
var group = { members: ["Jack", "Andy", "Natasha"], joinParty: "Yes", getInfo: function (m) { this.isJoinParty(m); console.log(m + " " + this.joinParty); }, isJoinParty: function (m) { switch(m) { case "Andy" : this.joinParty = "No"; break; default: this.joinParty = "Yes"; break; } } }; group.members.forEach(group.getInfo);
代码很简单,小组内3人,Andy不参加聚会,另两人参加聚会。期望把统计结果打印出来。但很遗憾上面代码会报Error。按理说getInfo函数里的this应该指向group对象,但遗憾地是getInfo作为[].forEach的回调函数时相当于普通函数,因此getInfo里的this指向的是window。而window对象里显然不存在isJoinParty。
因此正确的调用方式是,添加第二个参数,明确指定this的绑定对象:
group.members.forEach(group.getInfo, group);
//Jack Yes
//Andy No
//Natasha Yes
map映射创建新数组,函数声明:[].map( function(value, index, array) { … }, [thisArg] );。和forEach一样,不赘述。唯一需要注意的的是回调函数需要有return值,否则新数组都是undefined。
其实map能干的事forEach都能干,你可以把map理解为forEach的一个特例,专门用于:通过现有的数组建立新数组。例如将旧数组中字符串都trim一下,去除空格后生成新数组:
var trimmed = [" Jack","Betty "," Chirs "].map(function(s) { return s.trim(); //需要return值,否则新数组里都是undefined }); console.log(trimmed); //["Jack", "Betty", "Chirs"]
在没有map之前是通过forEach来创建新数组的。你需要先定义一个空数组,再将每次trim后的字符串push到新数组内,比较麻烦。因为“通过现有的数组建立新数组”这个需求是如此的普遍,因此ES5中干脆追加了map方法,相比forEach代码简单优雅多了。
filter用于过滤数组,函数声明:[].filter( function(value, index, array) { … }, [thisArg] );。和forEach一样,不赘述。唯一需要注意的的是回调函数需要return布尔值true或false,如果忘记写return语句,返回得到的是空数组,表示一个都不匹配。例如:
var newArray = [0, 1, 2].filter(function(value) {}); console.log(newArray); //[],没有return语句得到的是空数组 //过滤出不超过10的正数 var newArray2 = [0, 1, 2, 14].filter(function(value) { return value > 0 && value <= 10; }); console.log(newArray2); //[1, 2]
some,every
some表示只要某一个满足条件就OK,every表示全部满足条件才OK。
some的函数声明:[].some( function(value, index, array) { … }, [thisArg] );
every的函数声明:[].every( function(value, index, array) { … }, [thisArg] );
参照MDN。其实都和上面的forEach一样,不赘述。唯一需要注意的的是回调函数需要return布尔值true或false,如果忘记写return语句,表示不满足条件,返回false
[1, 10, 100].some(function(x) { x > 5; }); //false,忘记写return了 [1, 2, 3, 4, 5].every(function(x) { x > 0; }); //false,忘记写return了 [1, 10, 100].some(function(x) { return x > 5; }); // true [1, 10, 100].some(function(x) { return x < 0; }); // false [1, 2, 3, 4, 5].every(function(x) { return x > 0; }); // true [1, 2, 3, 4, 5].every(function(x) { return x < 3; }); // false
reduce,reduceRight
两者都是用于迭代运算。区别是reduce从头开始迭代,reduceRight从尾开始迭代。
reduce的函数声明:[].reduce( function(previousValue, currentValue, currentIndex, array) { … }, [initialValue] );
第一个参数是回调函数,有4个参数:previousValue,currentValue,currentIndex,array。看名字也能知道意思:前一个值,当前值,当前索引,数组本身。
第二个参数initialValue可选,表示初始值。如果省略,初始值为数组的第一个元素,这样的话回调函数里previousValue就是第一个元素,currentValue是第二个元素。因此不设initialValue的话,会少一次迭代。例如:
var sum = [1, 2, 3, 4].reduce(function (previous, current) { return previous + current; }); console.log(sum); //10 //给它加上initialValue初始值10 var sum2 = [1, 2, 3, 4].reduce(function (previous, current) { return previous + current; }, 10); console.log(sum2); //20
上图清楚地表明了各个运算步骤,很容易理解。如果不设initialValue,会少一次迭代。
reduceRight的函数声明:[].reduceRight( function(previousValue, currentValue, currentIndex, array) { … }, [initialValue] );。和reduce一样,不赘述
用reduce和reduceRight很容易就能实现二维数组扁平化,如下:
var flat1 = [[0, 1], [2, 3], [4, 5]].reduce(function(a, b) { return a.concat(b); }); console.log(flat1); //[0, 1, 2, 3, 4, 5] var flat2 = [[0, 1], [2, 3], [4, 5]].reduceRight(function(a, b) { return a.concat(b); }); console.log(flat2); //[4, 5, 2, 3, 0, 1]
slice,splice
两者做的事情还不太一样,但名字实在太像了,所以放一起介绍。
slice用于复制数组,复制完后旧数组不变,返回得到的新数组是旧数组的子集。函数声明:[].slice(begin, [end])。参照MDN
第一个参数begin是开始复制的位置,需要注意的是,可以设负数。设负数表示从尾往前数几个位置开始复制。例如slice(-2)将从倒数第2个元素开始复制。另外需要注意的是,该参数虽未标注为可选,但实际上是可以省略的,省略的话默认为0。
第二个参数end可选,表示复制到该位置的前一个元素。例如slice(0,3)将得到前3个元素,但不包含第4个元素。不设的话默认复制到数组尾,即等于array.length。
var fruits = ["Banana", "Orange", "Lemon", "Apple", "Mango"].slice(0, 3); console.log(fruits); //["Banana", "Orange", "Lemon"] var fruits2 = ["Banana", "Orange", "Lemon", "Apple", "Mango"].slice(-1); console.log(fruits2); //["Mango"]
当然slice最常见的是用在将类数组arguments对象转换为真正的数组:
var args = [].slice.call(arguments);
splice用于剥离数组,从旧数组中移除元素,返回得到的新数组是被移除的元素。函数声明:[].splice(start, deleteCount, [item…])。参照MDN
第一个参数start是开始剥离的位置,需要注意的是,可以设负数。设负数表示从尾往前数几个位置开始剥离。例如splice (-2)将从倒数第2个元素开始剥离。
第二个参数deleteCount是要剥离的元素个数,设0表示一个都不剥离。
第三个参数开始可选,用于替换旧数组中被移除的元素
var oldArray = ["a", "b", "c"]; var newArray = oldArray.splice(1, 2, "Jack", "Betty", "Andy"); console.log(oldArray); //["a", "Jack", "Betty", "Andy"] console.log(newArray); //["b", "c"]
一个常见的应用就是删除数组内某元素,用delete的话会留下空洞,应该用splice方法:
//错误的方法用delete
var numbers = [0, 1, 2, 3, 4]; delete numbers[2]; console.log(numbers); //[0, 1, undefined, 3, 4] //正确的方法用splice numbers.splice(2, 1); console.log(numbers); //[0, 1, 3, 4]
indexOf,lastIndexOf
两者都用于返回项目的索引值。区别是indexOf从头开始找,lastIndexOf从尾开始找。如果查找失败,无匹配,返回-1
indexOf的函数声明:[].indexOf( searchElement, [fromIndex = 0] );。参照MDN
lastIndexOf的函数声明:[].lastIndexOf( searchElement, [fromIndex = arr.length – 1] );
第一个参数searchElement即需要查找的元素。第二个参数fromIndex可选,指定开始查找的位置。如果忽略,indexOf默认是0,lastIndexOf默认是数组尾。
["a", "b", "d", "e"].indexOf("b"); //1 ["a", "b", "d", "e"].indexOf("b", 2); //-1,从2号位开始找没找到 ["a", "b", "d", "e"].indexOf("c"); //-1,没找到 ["a", "b", "d", "e"].lastIndexOf("b"); //1 ["a", "b", "d", "e"].lastIndexOf("b", 2); //1,逆向2号位等价于正向1号位 ["a", "b", "d", "e"].lastIndexOf("c"); //-1,没找到
sort
sort用于排序数组,函数声明:[].sort( [sortfunction] );。参照MDN
它就一个参数,就是排序函数指针。而且是可选的,不设的话有默认的排序函数,数字的话会升序排列,string会根据Unicode升序排列。
var sumArray = [4, 3, 1, 0, 2]; var sumArray2 = ["d", "z", "a"]; sumArray.sort(); sumArray2.sort(); console.log(sumArray); //[0, 1, 2, 3, 4] console.log(sumArray2); //["a", "d", "z"]
但是内置的默认排序函数,是不可靠的,如下:
var scores = [1, 10, 2, 21]; scores.sort(); console.log(scores); //[1, 10, 2, 21]
因此保险起见最好自定义排序函数:
var scores = [1, 10, 2, 21]; function compareNumbers(x, y) { if (x < y) { return -1; } if (x > y) { return 1; } return 0; } scores.sort(compareNumbers); console.log(scores); //[1, 2, 10, 21]
而且如果数组内是对象,或排序逻辑复杂的话,那默认排序函数更是力不从心了,必须自定义排序函数:
var items = [ { name: "Jack", value: 37 }, { name: "Betty", value: 21 }, { name: "Andy", value: 45 } ]; items.sort(function (a, b) { if (a.value < b.value) { return -1; } if (a.value > b.value) { return 1; } return 0; }); console.log(items); //[{ name="Betty", value=21}, // { name="Jack", value=37}, // { name="Andy", value=45}]
剩下的比较简单,大致说一下,就不详细介绍了。
push和pop用于数组尾处压入和弹出元素。
unshift和shift用于数组头部压入和弹出元素。
reverse用于反转数组,concat用于连接数组,join用于数组元素间插入些东西后拼接成string。
类数组
var arr1 = new Array();
arr1.push(1);
arr1.push(2);
arr1.push(3);
console.log(arr1); //[1,2,3]
console.log(arr1.pop()); //3 弹出栈顶数据
JS里有很多类数组对象。什么叫类数组对象呢?它们首先是对象,并没有继承Array,但长的却很像数组。最典型的如arguments对象,HTMLCollection对象。
类数组对象不能直接使用数组方法,但数组方法是如此简单便利,要在类数组对象身上使用数组方法,需要让数组函数通过call绑定类数组对象。
处理arguments对象:
var args = [].slice.call(arguments);
处理HTMLCollection对象:
//用forEach遍历页面所有div,输入className
var divs = document.getElementsByTagName("div");
Array.prototype.forEach.call(divs, function(div) {
console.log("该div类名是:" + (div.className || "空"));
});
//下面这样直接调用forEach将报错,因为divs是HTMLCollection对象而非Array
divs.forEach(function(div) {
console.log("该div类名是:" + (div.className || "空"));
});
处理字面量对象:
var arrayLike = { 0: "a", 1: "b", 2: "c", length: 3 }; var result = Array.prototype.map.call(arrayLike, function(s) { return s.toUpperCase(); }); console.log(result); //["A", "B", "C"] 处理字符串: var result = Array.prototype.map.call("abc", function(s) { return s.toUpperCase(); }); console.log(result); //["A", "B", "C"]
但Array的concat会检查参数的[[Class]]属性,只有参数是一个真实的数组才会将数组内容连接起来,否则将作为单个元素来连接。要完全实现连接,我们需要自己在对象上增加slice方法:
//单用concat的话,arguments对象将作为一个单一整体被连接 function namesColumn() { return ["Jack"].concat(arguments); } var newNames = namesColumn("Betty", "Andy", "Chris"); console.log(newNames); //["Jack", ["Betty", "Andy", "Chris"]] //配合slice能实现完全连接 function namesColumn() { return ["Jack"].concat([].slice.call(arguments)); } var newNames = namesColumn("Betty", "Andy", "Chris"); console.log(newNames); //["Jack", "Betty", "Andy", "Chris"]
更多资源上:去转盘;或者加我的QQ群一起讨论学习js,css等技术(QQ群:512245829)
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/80163.html
摘要:个人前端文章整理从最开始萌生写文章的想法,到着手开始写,再到现在已经一年的时间了,由于工作比较忙,更新缓慢,后面还是会继更新,现将已经写好的文章整理一个目录,方便更多的小伙伴去学习。 showImg(https://segmentfault.com/img/remote/1460000017490740?w=1920&h=1080); 个人前端文章整理 从最开始萌生写文章的想法,到着手...
摘要:看下面一个例子优点使用构造器函数的好处在于,它可以在创建对象时接收一些参数。按照惯例,构造函数的函数名应始终以一个大写字母开头,以区分普通函数。返回该对象的源代码。使您有能力向对象添加属性和方法。 基本概念 ECMA关于对象的定义是:无序属性的集合,其属性可以包含基本值、对象或者函数。对象的每个属性或方法都有一个名字,而每个名字都映射到一个值。 类 在现实生活中,相似的对象之间往往都有...
摘要:不会对空数组进行遍历遍历数组的每一项,数组当前项的下标,原数组函数内没有执行,证明数组为空是并不执行遍历返回一个新数组,长度等于原数组长度遍历数组的每一项,数组当前项的下标,原数组即便函数返回空结果数组的 map() 不会对空数组进行遍历 let arr = [] let newArr = arr.map((item, i, arr) => { ...
摘要:使用一元加模拟函数原理对非数值类型的数据使用一元加,会起到与函数相同的效果。中,若判断不为则不再进行下一步操作。使用逻辑或设置默认值逻辑或也属于短路操作,即当第一个操作数可以决定结果时,不再对第二个操作数进行求值。 善于利用JS中的小知识的利用,可以很简洁的编写代码 1. 使用!!模拟Boolean()函数 原理:逻辑非操作一个数据对象时,会先将数据对象转换为布尔值,然后取反,两个!!...
摘要:很简单,不是数组,但是有属性,且属性值为非负类型即可。至于属性的值,给出了一个上限值,其实是感谢同学指出,因为这是中能精确表示的最大数字。如何将函数的实际参数转换成数组 这篇文章拖了有两周,今天来跟大家聊聊 JavaScript 中一类特殊的对象 -> Array-Like Objects。 (本文节选自 underscore 源码解读系列文章,完整版请关注 https://githu...
阅读 1377·2021-10-08 10:04
阅读 2681·2021-09-22 15:23
阅读 2724·2021-09-04 16:40
阅读 1172·2019-08-29 17:29
阅读 1492·2019-08-29 17:28
阅读 2987·2019-08-29 14:02
阅读 2220·2019-08-29 13:18
阅读 838·2019-08-23 18:35