在讲原码实现之前,先将各个API的用法。这里我先放一个数组,以便后续使用~~
<script>
var dataArr = [
{name: "John", des: '全网最帅', sex: 'boy', age: 18, grade: 80},
{name: "sunny", des: '比刘宝宝丑那么点', sex: 'boy', age: 30, grade: 100},
{name: "MrDeng", des: '比成哥丑那么点', sex: 'boy', age: 45, grade: 100},
{name: "cst", des: '全网最丑', sex: 'boy', age: 26, grade: 100},
{name: "chang", des: '全网最矮', sex: 'girl', age: 18, grade: 80},
{name: "Monikatx", des: '全网最美', sex: 'girl', age: 20, grade: 100},
];
script>
1.forEach
dataArr.forEach(function (item, index, self){
this[index].innerText = item.name;
}, dom);
点击这里(forEach详细API)
forEach必须要传一个匿名函数作为实参,第二个参数是可选参数,讲用作改变匿名函数的this。
forEach的作用,可以看做是加强的for循环,用来遍历数组
forEach之所以是看做是加强版的for循环,就是因为他也是一个循环。所发挥的作用也for相差无几,底层也是利用的for循环。那他到底是怎么循环的呢?遍历数组length长度多长,这里的第一个实参,匿名函数就执行几次,而形参也会跟着被遍历。
这里我先说下匿名函数形参(形参是可以随意命名的,可以选择自己喜欢的名字):
形参一: item 代表的所遍历数组的value值, 如:arr[0] = "刘宝宝" ,"刘宝宝"是value值。
形参二: index 代表的所遍历数组的key值(索引), 如:arr[0] = "刘宝宝" ,[0]是key值。
形参二: self 代表的所遍历数组自己, 如:arr[0] = "刘宝宝" ,arr是self值。
forEach传的匿名函数会自己调用,而第二个实参,将被作为匿名函数的this传入 ,如:
2.filter
// filter
var younger = dataArr.filter(function (item, index, self){
let flag = item.age < 25;
this[index].innerText += ( flag ? " baby" : " old");
return flag;
}, dom)
console.log(younger);//输出[{name: "John"...}, {name: "chang"}, {name: "Monikatx"}]
filter与forEach 传参相同,必须要传一个匿名函数作为实参,可选择传一个值作为this。匿名函数的形参也与forEach 的匿名函数相同。请参考forEach 。
这里主要说说filter的功能:执行完成返回一个数组,我这里用younger接收。执行匿名函数内的代码时,返回一个boolean值判断真假,若不是布尔值返回给filter,filter自己将做隐式转化为布尔值判断真假。当返回值return 为真(true),将该次遍历的数组添加到younger。反之false舍弃,不添加。
3.every
// every 当遇上false停止遍历
var flagEvery = dataArr.every(function (item, index, self){
console.log(item.grade);
return item.grade > 90 ? true : false;
}, dom)
console.log(flagEvery);
every与forEach 传参相同,必须要传一个匿名函数作为实参,可选择传一个值作为this。匿名函数的形参也与forEach 的匿名函数相同。请参考forEach 。
every的功能:执行完成返回一个布尔值。执行匿名函数内的代码时,返回一个boolean值判断真假,若不是布尔值返回给every。当所有返回值return 都为真,返回真(true),只要有一个不为真(true)返回假(false)。
4.some
// some 遇上true停止遍历
var flagSome = dataArr.some(function (item, index, self){
console.log(item.grade);
return item.grade > 90 ? true : false;
}, dom)
console.log(flagSome);
some与forEach 传参相同,必须要传一个匿名函数作为实参,可选择传一个值作为this。匿名函数的形参也与forEach 的匿名函数相同。请参考forEach 。
some的功能:执行完成返回一个布尔值。执行匿名函数内的代码时,返回一个boolean值判断真假,若不是布尔值返回给some。当所有返回值return 有一个为真(true),返回true,全为假(false)返回假(false)。
5.map
// map 映射
var newAge = dataArr.map(function (item, index, self){
// return item.age + 10;
item.age += 10;
return item;
}, dom)
console.log(newAge);
map与forEach 传参相同,必须要传一个匿名函数作为实参,可选择传一个值作为this。匿名函数的形参也与forEach 的匿名函数相同。请参考forEach 。
map的功能:执行完成返回一个新的数组。执行匿名函数内的代码时,将该次遍历的返回值添加到新数组中。这段代码中我写了两段return,第一段return改变的是对象里的属性,并返回该属性值,这里返回的是原始值,和之前的的数组将没有联系。第二段return改变的也是对象里的属性,但返回的是对象本身,对象是引用值。如果你对新数组里的对象进行操作,原素组里的对象也将发生改变。
6.reduce
// reduce
var allName = dataArr.reduce(function (prev, current, index, self) {
return prev + current.name;
}, '+');
console.log(allName)
reduce这里需要注意,和forEach有点传参有些不一样了,第一个实参也是匿名函数,自行调用。这第二个实参也是可选参数,可传可不传,但是作用不一样。forEach是传一个实参作为this,而reduce的第二个实参是初始值。具体看下面~~
reduce的匿名函数形参:
reduce的功能:执行完成返回一个返回值。执行匿名函数内的代码时,执行匿名函数内的代码块。返回一个返回值,但这个返回值不是给reduce自己用的,而是给自己下一个遍历数组索引位下一个匿名函数形参prev赋值的。最后一个索引位匿名函数的返回值才是reduce的返回值。
7.reduceRight
// reduceRight
var allNameBack = dataArr.reduceRight(function (prev, current, index, self) {
return prev + current.name;
}, '-')
console.log(allNameBack)
reduceRight与reduce其实差不多,区别在于他的遍历顺序是反过来的。细节没什么好说的,不懂的可以参考reduce
reduceRight的功能:执行完成返回一个返回值。执行匿名函数内的代码时,执行匿名函数内的代码块。返回一个返回值。reduceRight遍历是从数组最后一位开始遍历,初始也是给的最后数组索引位匿名函数的形参prev。返回值是索引位 [0] 匿名函数的返回值。
好了,本文到此就结束了~~
期待下一次更新吧!!
好吧,我是真想睡了。写到这已经是将近凌晨4点半了。但我还是坚持写完,整篇没看到我皮,是不是不适应??不是不想皮,是实在太困了。
接下来是用js原码自己封装7种ES5数组方法——–
、
、
、
1.myForEach
Array.prototype.myForEach = function (fn) {
var i, _self = this, len = _self.length, target = arguments[1];
for(i = 0; i < len; i++) {
fn.apply(target, [_self[i], i, _self]);
}
}
既然是数组方法,那就要封装在Array.prototype上。Array.prototype.myForEach = function () {fn},这里的函数表达式执行就是arr.myForEach ()。按道理这么基础的东西,我本不应该再讲,但考虑到,我同学在听课的时候已经被绕晕了,所以是讲一下。这里的 fn 是形参,那执行就是arr.myForEach (function () {}), fn 就是这个里面的实参(匿名函数了)。可能你们会有疑惑,那dom作为第二个实参,在这怎么没看到与之对应的形参呢?我只看到一个形参!!!来来,接下来给你看一下第二个形参在哪里。不应该叫形参了,得叫实参列表
这里通过argument[1]来获取第二个实参,也就是dom。而之前说的foreach第二个实参将成为匿名函数的this,就是在这了apply将匿名函数fn的this指向dom,并传入三个形参,变量名i, _self, len, 分别是指遍历的索引,被遍历的数组本身也就是arr, len是数组的长度arr.length,而这里面的for相信你们也能看懂,让匿名函数fn执行len-1次。希望你们不要再被 myForEach 和 fn 给绕晕,我已经尽我最大的能力来讲了。下面的我就不再这么讲了
2.myFilter
// 封装myFilter
Array.prototype.myFilter = function (fn) {
var i, _self = this, len = _self.length, target = arguments[1], newArr = [];
for(i = 0; i < len; i++) {
fn.apply(target, [_self[i], i, _self]) ? newArr.push(_self[i]) : '像刘宝宝这么帅的人不多了';
}
return newArr;
}
有没有发现myFilter 和myForEach长竟然如此相像,先不要惊讶,因为在后面你会发现,这7个方法都长的差不多。
这里myFilter 比myForEach多了一个newArr数组,和一个return。这是因为filter是有返回值的,这个返回值也得是一个数组。之前说过filter每次遍历的匿名函数fn都会返回一个值给filter判断,这里for循环内fn.apply就是一次执行,执行后的返回值用作三目运算符判断,返回值为true,newArr添加当前数组索引位_self[i]。
然而这里后面这串字符串才是本文的重点。来来,圈起来,这是重点考试肯定是要考的。
emm,既然是筛选,那么为false就不要它执行任何操作就好了,但是三目运算符是你fasle也要有个表达式,那我就填个空就好了,但是这样不能表现出我的文笔功底,才会引出这么有哲学的一句话。令人反思啊。。。
最后遍历完了,就要返回数组,return newArrr。
3.myEvery
// 封装myEvery
Array.prototype.myEvery = function (fn) {
var i, _self = this, len = _self.length, target = arguments[1], flag = true;
for(i = 0; i < len; i++) {
if(!fn.apply(target, [_self[i], i, _self])){
flag = false;
break;
}
}
return flag;
}
是的,我是个诚实的宝宝。你看是不是长得相差不大?
myEvery返回的是true或false。所以开头声明一个flag=true,接下来我只需要遍历出匿名函数fn不是true,让flag=false就好了,当然既然只要是有一个不为true,every就要返回false,那你匿名函数fn返回给我false。我干脆就不遍历了,让你出去吧。免得还让我做无用功,break退出块级作用域,块级作用域不知道的我这里也不说了。最后返回flag为false,当然你匿名函数给我的返回值全是true,那我根本没走if里,自然flag就是true了。
4.mySome
// 封装mySome
Array.prototype.mySome = function (fn) {
var i, _self = this, len = _self.length, target = arguments[1], flag = false;
for(i = 0; i < len; i++) {
if(fn.apply(target, [_self[i], i, _self])){
flag = true;
break;
}
}
return flag;
}
这一块呢,我就不讲了。你只要看懂上面的,下面的你一定能看懂。
mySome返回布尔值。是的,就这么简短~~不服?不服你来打我啊!!
5.myMap
// 封装myMap
Array.prototype.myMap = function (fn) {
var i, _self = this, len = _self.length, target = arguments[1], newArr = [];
for(i = 0; i < len; i++) {
newArr.push(fn.apply(target, [_self[i], i, _self]));
// 这里可以使用深度克隆,让返回值是引用值也区分开来
}
return newArr;
}
myMap也没什么好讲的,就是声明一个数组,匿名函数操作完添加,返回数组。
myMap返回数组。是的,还是就这么简短~~不服?不服你又能怎样呢!!
6.myReduce
// 封装myReduce
Array.prototype.myReduce = function (fn) {
var i, _self = this, len = _self.length, initValue = arguments[1];
for(i = 0; i < len; i++) {
initValue = fn(initValue, _self[i], i, _self);
}
return initValue;
}
myReduce也没什么好讲的。。。。。。但是我的求生欲告诉我这里还是要讲的。
myReduce的实参列表arguments[1]第二个实参不再是改变匿名函数(fn) this指向,而是直接作为匿名函数fn的第一个实参传入,传进去干嘛呢?之前说了reduce索引位是0的时候,他是没有上一返回值的,所以讲第二个实参传入作为初始值,也就是索引位是0的的上一个返回值。 initValue = fn(initValue, _self[i], i, _self);这样写是让初始值initValue(也就是当前索引返回值)赋值为下一个索引位的匿名函数fn使用(prev)。到for循环遍历完initValue也就被赋值成最后一位索引匿名函数fn的返回值;
6.myReduceRight
// 封装myReduceRight
Array.prototype.myReduceRight = function (fn) {
var i, _self = this, len = _self.length, initValue = arguments[1];
for(i = len-1; i >= 0; i--) {
console.log(i)
initValue = fn(initValue, _self[i], i, _self);
}
return initValue;
}
myReduceRight也讲讲吧。
myReduceRight是反序遍历,那for循环也就要反向遍历,for(i = len-1; i >= 0; i–)。
好了,讲完了~~
最后,本文到这里就结束了。首先,有疑惑或者我哪里讲的不对,麻烦大家指正,我写文章纯属是来了兴趣就写上一篇,担心给大家带来误导。其次,感谢大家的支持,上次文章的阅读量给了我很大的激励,两天就200多阅读量,这使我膨胀了,我膨胀到自己都不认识了,我要飞上天和太阳肩并肩~~