问答
数组方法里push、pop、shift、unshift 、join、split分别是什么作用。
- push和pop
介绍push和pop的作用前,先说一下栈:
栈是一种LIFO(Last-In-First-Out,后进先出)的数据结构,也就是最新添加的项最早被移除,这种数据结构可以限制插入和删除项。而栈中项的插入(叫做推入)和移除(叫做弹出),只发生在一个位置——栈的顶部。
那么ECMAScript为数组专门提供了push()和pop()方法,以便实现类似栈的行为。
push()方法可以接受任意数量的参数,把它们逐个添加到数组末尾,并返回修改后数组的长度。而pop()方法则是从数组末尾移除最后一项,减少数组的length值,然后返回移除的项。
举例:
var colors = new Array(); //创建一个数组
var count = colors.push("red","green");//推入两项
console.log(count);//2
count = colors.push("black");//推入另一项
console.log(count);//3
var item = colors.pop();//取得最后一项
console.log(item);//"black"
console.log(colors.length);//2
- shift和unshift
介绍shift和unshift前,也要说明另一种数据结构——队列数据结构:
与前面介绍的栈数据访问规则LIFO(后进先出)不同,队列数据结构的访问规则是FIFO(First-In-First-Out,先进先出),即队列在列表的末端添加项,从列表的前端移除项。
由于push()是向数组末端添加项的方法,因此模拟队列只需一个从数组前端取得项的方法。那么实现这一操作的数组方法就是shift(),它能够移除数组的第一个项目并返回该项,同时将数组长度减一。结合使用shift()和push()方法,就可以完全模拟队列了。
举例:
var color = nwe Array(); //创建一个数组
var count = color.push("red","green");//推入两项
console.log(count);//2
count = colors.push("black");//推入另一项
console.log(count);//3
var item = colors.shift(); //取得第一项
console.log(item);//"red"
console.log(colors.length);//2
那么顾名思义,unshift()与shift()的用途相反:它能在数组前端添加任意个项并返回新数组的长度。因此,同时使用unshift()和pop()方法,可以从相反的方向来模拟队列,即在数组的前端添加项,从数组的末端移除项。
举例:
var colors = new Array();//创建一个数组
var count = colors.unshift("red","green");//推入两项
console.log(count);//2
count = colors.unshift("black");//推入另一项
console.log(count);//3
var item= colors.pop(); //取得最后一项
console.log(item);//"green"
console.log(colors.length);//2
- join
join方法作用是把数组元素(对象调用其toString()方法)使用参数作为连接符连接成一个字符串,不会修改原数组内容
var colors = ["red","green","blue"];
console.log(colors.join(","));//red,green,blue
colsole.log(colors.join("||"));//red||green||blue
注意:如果不给join()方法传入任何值,或者给它传入undefined,则使用逗号作为分隔符。
IE7即更早版本会错误使用字符串"undefined"作为分隔符。
- split
split()方法可以基于指定的分隔符将一个字符串分割成多个子字符串,并将结果放在一个数组中。分隔符可以是字符串,也可以是一个正则表达式。split()方法可以接受可选的第二个参数,用于指定数组的大小,以便确保返回的数组不会超过既定大小。
var colorText = "red,green,blue,yellow";
var colors1 = colorText.split(",")//["red","blue","green","yellow"]
var colors2 = colorText.split(",",2)//["red","blue"]
上面这个例子中,colorText
是用逗号分隔的颜色名字符串。基于该字符串调用split(",")
会得到一个包含其中颜色名的数组,用于分隔字符串的分隔符是逗号。
以上回答参考JavaScript高级程序设计(第3版)
二 代码:
1.用 splice 实现 push、pop、shift、unshift方法
这里补充介绍下splice方法:
splice()的主要用途是向数组的中部插入项,但使用这种方法主要有如下3种:
- 删除:可以删除任意数量的项,只需指定2个参数:要删除的第一项的位置和要删除的项数。例如,splice(0,2)会删除数组中的前两项。
- 插入:可以向指定位置插入任意数量的项,只需提供3个参数:起始位置、0(要删除的项数)和要插入的项。如果要插入多个项,可以再传入第四,第五,以至任意多个项。例如,splice(2,0,"red","green")会从当前数组的位置2(也可以理解为从原始数组位置2前面)开始插入字符串"red"和"green"。
- 替换:可以向指定位置插入任意数量的项,且同时删除任意数量的项,只需指定3个参数:起始位置、要删除的项数和要插入的任意数量的项。插入的项数不必与删除的项数相等。例如,splice(2,1,"red","green")会删除当前数组位置2的项,然后再从位置2开始插入字符串"red"和"green"。
注意:splice()方法始终都会返回一个数组,该数组中包含原始数组中删除的项(如果没有删除任何项,则返回一个空数组)。
//push
function push(arr,val){
arr.splice(arr.length,0,val);
return arr.length;
}
//pop
function pop(arr){
return arr.splice(arr.length-1,1)[0];
}
//shift
function shift(arr){
return arr.splice(1,1)[0]
}
//unshift
function unshift(arr,val){
arr.splice(1,0,val);
return arr.length;
}
2.使用数组拼接出如下字符串
var prod = {
name: '女装',
styles: ['短款', '冬季', '春装']
};
function getTpl(data){
//todo...
};
var result = getTplStr(prod); //result为下面的字符串
<dl class="product">
<dt>女装</dt>
<dd>短款</dd>
<dd>冬季</dd>
<dd>春装</dd>
</dl>
解答代码:
var prod = {
name: '女装',
styles: ['短款', '冬季', '春装']
};
function getTplStr(data) {
var product = [];
product.push('<dl class="product">');
product.push('<dt>'+data.name+'</dt>');
for (var i = 0; i < data.styles.length; i++) {
product.push('<dd>'+data.styles[i]+'</dd>');
}
product.push('</dl>');
console.log(product.join(""));
};
var result = getTplStr(prod);
第一次写该代码的时候,对常量和变量的表达方式没有区分,把
'<dt>'+data.name+'</dt>'
写作'<dt>+data.name+</dt>'
,导致循环失败,所以一定要注意在JS中变量是不加引号,只有常量需要加引号。
3.写一个find函数,实现下面的功能
var arr = ["test",2,1.5,false]
find(arr,"test")//0
find(arr,2)//1
find(arr,0)//-1
解答代码:
//ES5方法
var arr = ["test",2,1.5,false];
function find(arr,val){
return arr.indexOf(val);
}
//非ES5方法:
var arr = ["test",2,1.5,false];
function find(arr,val) {
for (var i = 0; i < arr.length; i++) {
if (arr[i] === val) {
return i;
}
}
return -1;
}
4.写一个函数filterNumber,把数组arr中的数字过滤出来赋值给新数组newarr,原数组arr不变。
arr = ["a", "b", 1, 3, 5, "b", 2];
newarr = filterNumeric(arr); // [1,3,5,2]
解答代码:
//ES5方法
var arr = ["a","b",1,3,5,"b",2];
var newarr;
function filterNumeric(a){
var filterResult = a.filter(function(item,index,array){
return (typeof item === "number");
})
console.log(filterResult);
};
newarr = filterNumeric(arr);
//非ES5方法:
var arr = ["a","b",1,3,5,"b",2];
var newarr;
function filterNumeric(a){
var filterResult = [];
for (var i = 0; i < a.length; i++) {
if (typeof a[i] === "number") {
filterResult.push(a[i]);
}
}
console.log(filterResult);
}
newarr = filterNumeric(arr);
typeof
运行后的结果是需要带引号的。
5.对象obj有个className属性,里面的值为的是空格分割的字符串(和html元素的class特性类似),写addClass、removeClass函数,有如下功能:
var obj = {
className: 'open menu'
}
addClass(obj, 'new') // obj.className='open menu new'
addClass(obj, 'open') // 因为open已经存在,所以不会再次添加open
addClass(obj, 'me') // me不存在,所以 obj.className变为'open menu new me'
console.log(obj.className) // "open menu new me"
removeClass(obj, 'open') // 去掉obj.className里面的 open,变成'menu new me'
removeClass(obj, 'blabla') // 因为blabla不存在,所以此操作无任何影响
代码:
var obj = {
className: 'open menu'
}
function addClass(obj,val){
var arr = obj.className.split(" ");
for (var i = 0; i < arr.length; i++) {
if (arr[i] === val) {
return obj.className;
}
}
arr.push(val)
return obj.className = arr.join(" ");
}
function removeClass(obj,val){
var arr = obj.className.split(" ");
for (var i = 0; i < arr.length; i++) {
if (arr[i] === val) {
arr.splice(i,1);
return obj.className = arr.join(" ");
}
}
return obj.className;
}
6.写一个camelize函数,把my-short-string形式的字符串转化成myShortString形式的字符串,如:
camelize("background-color") == 'backgroundColor'
camelize("list-style-image") == 'listStyleImage'
解答代码:
function camelize (str){
var arr = str.split("-");
if(arr.length === 1) return str;
for (var i = 1; i < arr.length; i++) {
arr[i] = arr[i][0].toUpperCase()+arr[i].slice(1);
}
return arr.join('');
}
一定要加上
if(arr.length === 1) return str;
,否则在输入没有用-
键链接的字符串的时候,会报错。
7.如下代码输出什么?为什么?
arr = ["a", "b"];
arr.push( function() { alert(console.log('hello hunger valley')) } );
arr[arr.length-1]() // ?
输出结果为:弹框显示出undefined
,控制台打印出hello hunger valley
原因:arr.push(function)(){...}
这句代码会把函数function(){...}
添加到数组的末尾,而arr[arr.length-1]
运行的就是数组的最后一项,所以最终结果就是将函数fucntion(){...}
运行一遍,console.log(...)
返回undefined
,所以弹框显示undefined
。
8.写一个函数filterNumericInPlace,过滤数组中的数字,删除非数字。要求在原数组上操作
arr = ["a", "b", 1, 3, 4, 5, "b", 2];
//对原数组进行操作,不需要返回值
filterNumericInPlace(arr);
console.log(arr) // [1,3,4,5,2]
解答代码:
//方法1
arr = ["a", "b", 1, 3, 4, 5, "b", 2];
function filterNumericInPlace(arr){
for (var i = 0; i < arr.length; i++) {
if(typeof arr[i] == "number") {
continue;
}else {
arr.splice(i,1);
i--;
}
}
return arr;
}
//方法2
function filterNumericInPlace(arr){
for (var i = arr.length-1; i >= 0; i--) {
if (typeof arr[i] !== 'number') {
arr.splice(i, 1);
}
}
}
1.使用
!==
比使用continue....else
更简洁一点,这里2种都写上提供不同的思路。
2.在方法1没有添加i--
这行代码前,运行结果出现了问题,显示为["b", 1, 3, 4, 5, 2]
,原因是循环了第一次split
后,原代码的"a"
被删除了,导致"b"
的索引位置由1变为0,而第二次循环是从i=1
开始,"b"
就躲开了这次循环,所以使用i--
让第二次循环从i=0
开始,让"b"也能被检索到。
3.另一个思路从数组末尾开始循环,这种方法只会影响已经检索过的元素在数组中的索引位置,所以不需要额外的i++
。
9.写一个ageSort函数实现数组中对象按age从小到大排序
var john = { name: "John Smith", age: 23 }
var mary = { name: "Mary Key", age: 18 }
var bob = { name: "Bob-small", age: 6 }
var people = [ john, mary, bob ]
ageSort(people) // [ bob, mary, john ]
解答代码:
var john = { name: "John Smith", age: 23 }
var mary = { name: "Mary Key", age: 18 }
var bob = { name: "Bob-small", age: 6 }
var people = [ john, mary, bob ]
function ageSort(arr){
arr.sort(function(value1,value2){
return value1.age - value2.age;
})
}
ageSort(people);
sort()
方法是可以接受一个比较函数作为参数,以便与指定哪个值位于哪个值的前面。
比较函数接受2个参数,如果第一个参数应该位于第二个之前则返回一个负数,如果两个参数相等则返回一个0,如果第一个参数应该位于第二个之后则返回一个正数。
10写一个filter(arr,fuc)函数用于过滤数组,接受2个参数,第一个是要处理的数组,第二个参数回调函数(回调函数遍历接受每一个数组元素,当函数返回ture时保留该元素,否则删除该元素)。实现如下功能:
function isNumeric (el){
return typeof el === 'number';
}
arr = ["a",3,4,true, -1, 2, "b"]
arr = filter(arr, isNumeric) ; // arr = [3,4,-1, 2], 过滤出数字
arr = filter(arr, function(val) { return typeof val === "number" && val > 0 }); // arr = [3,4,2] 过滤出大于0的整数
解答代码:
function filter(arr, func) {
for (var i = arr.length - 1; i >= 0; i--) {
if (!func(arr[i])) {
arr.splice(i, 1);
}
}
}
//尝试使用es5方法失败
function filter (arr,func){
arr.forEach(function(item,index,array){
if(!func(item)){
array.splice(index,1);
}
})
}
尝试使用了ES5的
forEach()
方法,但是出现了和代码8一样的问题,即如果有2个相邻的项为同样的数据类型,它只会删除第一个项,尝试修改index值也无效。查询资料得知,forEach()
方法是按照升序为数组中的值执行一次函数,而且是没法中断或者跳出forEach
循环,除非发生异常。
思考了一下我的理解就是,假如使用forEach
删除数组中的第一个项,那么第二个项的index就变为1,forEach
接着循环index值为2的项,这样流跳过了原数组中的第二个项。问题就出现在这个index值是作为数组的固有属性,是无法人为的去改变。
所以,使用for
循环比forEach
在某些场景更灵活,兼容性也更好。
字符串
11.写一个ucFirst函数,返回第一个字母为大写的字符。
ucFirst("hunger") == "Hunger"
代码:
function ucFirst(str){
var initial = str.substr(0,1);
return initial.toUpperCase()+str.substr(1);
}
12.写一个函数truncate(str,maxlength),如果str的长度大于maxlength,会把str截断到maxlength长,并加上...,如:
truncate("hello, this is hunger valley,", 10)) == "hello, thi...";
truncate("hello world", 20)) == "hello world"
解答代码:
function truncate(str, maxlength){
if(str.length > maxlength){
return str.substr(0,maxlength)+"...";
}else{
return str;
}
}
数字函数
13.写一个函数,获取从min到max之间的随机整数,包括min不包括max
解答代码:
function getRandomInt(min, max){
return Math.floor(Math.random()*(max-min))+min;
}
14.写一个函数,获取从min到max之间的随机整数,包括min包括max
解答代码:
function getRandomInt(min, max){
return Math.floor(Math.random()*(max-min+1))+min;
}
15.写一个函数,获取一个随机数组,数组中的元素为长度len,最小值为min,最大值为max(包括)的随机整数。
解答代码:
function randStr (len,min,max) {
var arr = [];
for(var i=0; i<len; i++){
var result = Math.floor(Math.random()*(max-min+1))+min;
arr.push(result);
}
return arr;
}
16.写一个函数,生成一个长度为 n 的随机字符串,字符串字符的取值范围包括0到9,a到 z,A到Z。
解答代码:
function randStr (n) {
var str = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
var result = "";
for(var i=0; i<n; i++){
result += str[Math.floor(Math.random()*str.length)];
}
return result;
}