JS: Accumulate from 0系列之数组&字符串方法

作者 Kylewh 日期 2017-03-12
JS
JS: Accumulate from 0系列之数组&字符串方法

Array

length:你问我有多长?

数组的长度是一个可修改的属性,而作为一个类数组的必要条件也是具有一个可以读写的length属性。

var arr = [1,2,3,4,5];
console.log( arr.length ); //5
//如果设置长度超过原有的长度,则新的位置的值为undefined
arr.length = 6;
console.log( arr ); //[1, 2, 3, 4, 5, undefined]
arr.length = 4;
//如果长度设置小于原有长度,则超过的部分消失。
//我们可以利用这个属性进行数组后几位的删除,而无需使用splice
console.log (arr); //[1, 2, 3, 4]
//length也是个很好的lastElement索引,但是要记住-1,因为数组索引以0开始。
console.log(arr[arr.lentgh-1]);

利用length的特征(可读写),以及数组元素的可枚举性,我们来伪造一个伪数组:

var FakeArr = function(){}
Object.defineProperty(FakeArr.prototype, 'length', {
writable: true, //enumerable在这种定义方式下默认为false
value: 5
});
var fakeArr = new FakeArr();
for (var i = 0; i < 5; i++) {
fakeArr[i] = i;
}
//尝试一下数组的push
[].push.call(fakeArr,5);
for( i=0, length = fakeArr.length; i<length; i++ ){
console.log(fakeArr[i]); //0,1,2,3,4,5
}
console.log (fakeArr);
//结果:
/*
[object Object] {
0: 0,
1: 1,
2: 2,
3: 3,
4: 4,
5: 5,
length: 6
}
我们成功了!
*/

从数组得到一个字符串

所有对象都具有toString方法和valueOf方法,数组也不例外:

var arr = [0,1,2,3,4];
console.log(arr.toString() ); //"0,1,2,3,4" 这是一个由所有值拼接而成的以逗号分隔的字符串。
//效果等同于split(',)
console.log(arr.toString.join(','));

模拟栈和队列

学过数据结构的都知道这俩货是啥吧? 前者是先进后出,后者是先进先出;

对于栈,先进后出也就是后进先出,相当于我们总是在向一个管道里推入东西,而每次我们也只能从内容物的顶部取出东西。

  • 在数组里模拟推入的方法叫做push,它可以接受任意数量的参数,并把他们逐个添加到数组尾部,并且返回推入完毕后的数组长度
  • 模拟从内容物的顶部取出东西的方法叫做shift,它移除数组的第一项并返回它。
var arr2 = [0,1];
console.log( arr2.push(2) ); //3
console.log( arr2.pop() ); //2

对于队列,想象有一个管道,我们往里面塞入球体,那么最先进去的球一定是第一个从管道尾部被拿出来的。

  • 这个放入球的过程在数组里叫做unshift,它跟push一项可以接受任意数量个参数,并且将它们插入到数组的头部,返回数组的length
  • pop则是用来模拟从管道尾部拿球的方法,从数组末尾移除最后一项(注意这次不是任意数量了,而且也不接收参数),而且返回被弹出的最后一项。
var arr3 = [2,3,4,5];
console.log( arr3.unshift(0,1) ); //6
console.log( arr3.shift() ); //0

数组有点乱,排个序吧?

第一个是让所有元素逆序的方法:reverse,还有可以对数组按某种规则排序的sort方法。

var arr4 = [6,5,4,3,2,1];
arr4.reverse();
console.log(arr4); //[1,2,3,4,5,6]
var arr5 = [2,6,3,5,6,8,9,1];
//sort接收一个比较函数参数,比较函数接收两个参数,如果第一个参数应该位于第二个之前则返回一个负数,如果第二个参数应该位于第一个之前则返回一个整数,两个参数相等则返回0.
//我们想正序排列,后一个参数减去前一个参数为负数
arr5.sort((a,b)=> a-b);
console.log(arr5);//[1, 2, 3, 5, 6, 6, 8, 9]

复制一段当新数组之slice

它能够基于当前数组中的一个或者多个创建(浅复制)一个新数组。slice方法接收一个或两个参数,起始项和结束位置,记住,不包括结束位置的元素。如果没有最后一个参数,则默认结束位置为数组的length.这个方法返回起始位置到结束位置之间的项。

var arr6 = [ 0,1,2,3,{'key':'value'}];
var arr7 = arr6.slice(4) // [object]
console.log(arr7);
/*
[[object Object] {
key: "value"
}]
*/
var arr8 = arr6.slice(0,4); //[0,1,2,3]
//浅复制
console.log ( arr6[4] === arr7[0] ); //true

强大的splice

它有三种作用: 删除,插入,替换。

  • 可以删除任意数量的项,第一个参数是要删除的第一项的位置,第二个参数要删除的数量,这个方法始终返回被删除的项目组成的数组.
var arr9 = [1,2,3,4,5,6];
console.log( arr9.splice(0,3) );//[1, 2, 3]
console.log( arr9 ); //[4, 5, 6]
  • 可以向任意位置插入任意数量的项目,提供三个参数,起始位置,0(不删除),和要插入的项目
var arr10 = ['学挖掘机''哪''家', '强?'] ;// 哈哈哈arr10.splice(4,0,' 出门右转','找','蓝','翔!'); //[]
console.log(arr10.join('')); "学挖掘机哪家强? 出门右转找蓝翔!"
  • 可以向指定位置插入任意数量的项,同时删除任意数量的项,参数: 起始位置,删除的项目,插入的项目
var arr11 = ['php','是坠吼的语言!'];
arr11.splice(0,1,'JavaSctipt','才'); //['php'];
console.log(arr11.join('')); //"JavaSctipt才是坠吼的语言!"

你在找人吗?

indexOf,lastIndexOf,英语学的怎么样? 不用解释了把,上代码。

var arr12 = ['a','a','b','c','d'];
console.log( arr12.indexOf('a') ); //返回匹配的第一个位置
console.log( arr12.lastIndexOf('a') );//1

给我一个个搜-迭代

every 给定一个检查函数,如果数组每一项都使得这个函数返回true,则返回true. 对应的还有some,只要检查函数有一个返回true,就返回true.

var arr13 = [0,1,2,3,4,5];
console.log( arr13.every(ele => ele <= 10) ); //true
console.log( arr13.some(ele => ele>=5) ); //true

filter这次是用返回true的元素组成一个数组,记住,是新数组!

var arr14 = [10,11,12,0,1,2];
var arr15 = arr14.filter(function(ele){
return ele>=10;
});
console.log(arr15);//[10, 11, 12]

forEach每一项都做点什么,不返回值.传入的函数具有三个参数,当前项,索引,数组本身。

var arr16 = ['你','我',''];
arr16.forEach(function(ele,idx,arr){
if(ele === ''){
arr[idx]+='不猜!?';
}else {
arr[idx]+='猜';
}
});
console.log(arr16.join(''));//你猜我猜不猜!?
//记住,这个函数如果你对数组本身不进行操作是不会改变数组的,除非你故意改变它。

map,这个函数跟forEach很像,但是它用每次调用函数的返回值形成一个新数组.使用上面的例子来看看

var arr16 = ['你','我',''];
var arr17 = arr16.map(function(ele,idx,arr){
if(ele === ''){
return ele+='猜不猜!?';
}else {
return ele+='猜';
}
});
console.log(arr17.join(''));//"你猜我猜猜不猜!?"
//看出区别了吗?
//一个是做事,然后闪人。 --forEach
//一个是做事,然后返回做事的结果组成的数组 --forMap

reduce大法好

这也是一个很强大的方法,传入一个函数,接收4个参数:前一个值,当前值,项的索引,和数组对象. 除了函数外,还有一个可选的初始值参数。最后返回一个最终返回值。

//我们可以很轻易的求出一组数组的所有项目之和
var arr18 = [1,2,3,4,5,6,7,8,9] //心算下(1+9)*9/2 = 45
var result = arr18.reduce((a,b)=>a+b);
console.log(result);//45

我们可以实现常见的Array Flatten

var arr = [1,[2,[3,4]],[5,6,[7,8]]];
var flatten = array => array.reduce( (acc,val) => acc.concat(Array.isArray(val)? flatten(val) : val), []);
console.log( flatten(arr) ); // [1, 2, 3, 4, 5, 6, 7, 8]

我们还可以用它来做点高大上的事(高阶函数应用里reduce相当强大)

var __reduce__ = fn => (...args) => args.reduce((a, b) => fn(a, b));
var add = (a,b)=>a+b;
var __add__ = __reduce__(add);
console.log( __add__(1,2,3,4,5,6,7,8,9) ); //45
//想当于将传入的函数改造成内置reduce的函数,对参数进行两两操作。
/*
还有请注意一点: If initialValue isn't provided, reduce will execute the callback function starting at index 1, skipping the first index. If initialValue is provided, it will start at index 0.
*/

以上方法除了回调函数参数之外,还可以传入一个参数作为this的绑定

本质是对象

为什么要单独列出这一条?看如下代码:

var aFn = [];
var f1 = function(fn) {
console.log(this.length);
console.log(this);
arguments[0]();
}
var f2 = function() {
console.log(this.length);
}
aFn.push(f1,1,2);
aFn[0](f2); //输出什么?
/*
答案: 先输出3,再输出数组本身, 再输出1.
输出3 & 输出数组本身: this指向了数组本身,因为数组是一个对象啊!它充当了f1这个函数的调用者,所以函数内部的this指向了数组本身.
输出1: wait~!?哪来的1? 当然是arguments的length啊!!
这里又是一个trick,arguments是一个数组对象,是形参的一个映射。
arguments[0]存储着参数,而第一项则是f2,f2的this则指向了它的调用者:arguments对象
刚好arguments对象是具有length的. so....!! 是不是很阴险?
*/

String

string.charAt(pos)

返回只含一个字母的字符串

string.indexOf(searchString, position)

返回字符串里面的字母所在的位置下标,实际上把把 string 看成一个 char[],更容易理解,postion代表从哪个位置开始查找,对应有一个lastIndexOf,从设定位置之前向前搜索。

string.slice(start, end)

返回子字符串,参数可以为负数

string.match(regexp)

返回比配字符串,假如有正则表达式为/xxx/g则,返回一个数组,否则返回第一次匹配到的字符串

string.replace(searchValue, replaceValue)

替换字符串,searchValue 可以为正则表达式。

str.substring(indexStart[, indexEnd])

返回字符串的一部分,开始位置和停止位置,不包括停止位置,如果不传入第二个参数,就将字符串的长度作为结束位置.

str.substr(start [, length])

英语好的应该看出差别了吧,第二个参数是项数,不设这个参数同样将字符串长度作为结束位置.