前端面试题汇总(字符串系列一)

856 阅读6分钟

一、写一个方法把字符串大小写进行切换

(大写转小写,小写转大写)

比如:

//如 abCdEFG-> ABcDefg

考点与思路:

toUpperCase与toLowerCase 、

字符串的遍历方式有两种,

  • 一种是先转换成数组在遍历最后在转换成字符串,
  • 另外一种是直接遍历字符串

实现方式一:

通过for of遍历,累加字符串

var str="abCdEFG"
function strChange(str){
    var strRes="";
    for(var i of str){
        strRes += i.toUpperCase() === i ? i.toLowerCase() : 
                  i.toLowerCase() === i ? i.toUpperCase() : 
                  i

    }
    return strRes
}

strChange(str)
// "ABcDefg"


实现方式二

var str="abCdEFG";
function strChange(str){	
              return str
		.split("")
		.map((item,index)=>{
		    const upperItem = item.toUpperCase();
                    return item === upperItem ? item.toLowerCase() : upperItem
		 })
                .join("")
}

以上代码,先把字符串转为数组,然后遍历,return 大写转小写,小写转大写的字符串数组,然后join("")转为字符串


注意点:

@三目运算符多个判断条件的写法优于多级的if...else if ...else if ......

条件?语句:
条件?语句:
条件?语句:都不满足上述条件则执行最后这个语句

a==0?-1:
a==1?0:
1


a=2; 
//2
a=0;
//-1
a=1;
//0

epression1 ? sentence1 :  
expression2 ? sentence2 :
expression3 ? sentence3 :
... 


只要任意一个expressionN的判断为真,那么sentenceN立即执行,这个判断结束,后面的任何判断不再执行。而如果我们写成if-else 

 if(expression1){
      sentence1;
   } else if(expression2){ 
      sentence2;
   } else if(expression3){
      sentence3;
   } ...

这样书写逻辑看起来比较心累,所以在jquery和zepto源码中,我们会大量看到三目运算符的应用

@函数内部for循环中的return,直接跳出循环,也就失去了循环的意义


function fn(){
    for(var i=0;i<10;i++){
       return i+12
    }
}
fn()
// 12  

上面的例子,我们发现,循环一次也没有执行,直接跳出了循环,要使用循环的功能,就必须在循环外return 

@for循环中的break与continue

break  跳出整个循环

function fn(){
    for(var i=0;i<10;i++){
        console.log(i)        if(i===3){break;}
    }
}
fn();
//输出 0  1  2   函数返回undefined

以上代码无论break语句放到console.log的上面和下面执行结果是一样的


continue  跳过本次循环

function fn(){
    for(var i=0;i<10;i++){
        console.log(i)
        if(i===3){continue;}
    }
}
fn();
// 输出0到9   函数返回undefined

console.log(i)放到前面和后面输出的结果是不一样的。

放到后面才不会输出3,才能真正的跳过等于3这一次循环

@for...of中的break,continue,return 

break

var arr1 = [1,2,100,9,8,7,10,12,20];

for (var n of arr1) {
    if (n > 10) {
        break;
    }
    console.log(n);
}
// 输出1 2 

break 直接跳出循环

continue

var arr1 = [1,2,100,9,8,7,10,12,20];

for (var n of arr1) {
    if (n > 10) {
        continue;
    }
    console.log(n);
}
// 输出1 2 9 8 7 10

跳过每一次大于10的循环(不满足条件的循环)

return

function forOfFn(arr1){
    for (var n of arr1) {
    	if (n > 10) {
       	   return;
    	}
        console.log(n);
    }
    return "fn"
}
forOfFn([1,2,100,9,8,7,10,12,20])
// 输出 1 2  函数返回 undefined

function forOfFn(arr1){
    for (var n of arr1) {
    	if (n > 10) {
       	   break;
    	}
        console.log(n);
    }
    return "fn"
}
forOfFn([1,2,100,9,8,7,10,12,20])
// 输出 1 2  函数返回 "fn"
此例中return 和break的效果看上去是一样的。其实是不一样的。
break只是跳出循环,循环后面的脚本会继续运行。
而return 不仅仅是跳出循环,而是跳出整个函数。

循环知识点-总结

  • break、continue、return 只能在for循环和for...of循环中使用。(for...in暂未测试)
  • map,filter,forEach,some,every的方式都不可以使用 break,continue,return
  • break语句在循环中的任何位置都生效。
  • continue语句只在所使用循环值的上方才有效。
  • 在js中return 语句只能放在function中


二、获取当前url查询字符串中的参数

@ 考点:

  • location.search参数获取
  • 参数截取slice或者substr、subString
  • 字符串转数组split,先以&符来切割转换
  • 遍历,在以=来切割小单元每一个字符串
  • 然后存储到对象中。o[key]=value

var str = 'https://www.baidu.com:443/s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1'

@ 通过for循环或者map,forEach来实现

function getParam(){
    //通过 location.search获取包括?号在内的后面的字符串,截取从索引1开始的字符串,去除?号
	var searchStr = window.location.search.slice(1) || '?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1'.slice(1);//示例使用
	var paramsArr = searchStr.split("&");
 	var resO={};//存储参数对象
	for(var i=0;i<paramsArr.length;i++){
		var item = paramsArr[i].split("="); // 把a=1这样的每一项字符串,在切割为数组,item = ["a",1]
		resO[item[0]] = decodeURIComponent(item[1]);
	}
 	return resO;
}
getParam();
// {ie: "utf-8", f: "8", rsv_bp: "1", rsv_idx: "1"}

@ 通过map,和forEach是一样的,这两个api内部也是通过for循环来实现的,for循环性能最好.

function getParam(){
    //通过 location.search获取包括?号在内的后面的字符串,截取从索引1开始的字符串,去除?号
	var searchStr = window.location.search.slice(1) || '?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1'.slice(1);//示例使用
	var paramsArr = searchStr.split("&");
 	var resO={};//存储参数对象
	paramsArr.forEach((item)=>{
		var itemArr = item.split("=");
		resO[itemArr[0]] = decodeURIComponent(itemArr[1]);
	})
		
	
 	return resO;
};
getParam();
// {ie: "utf-8", f: "8", rsv_bp: "1", rsv_idx: "1"}

@小提示:

字符串中substr(start,length)、substring(start,end)、substr(start,end)

  • substr(start,length) 方法可在字符串中抽取从 start 下标开始的指定数目的字符。
  • substring(start,end) 方法用于提取字符串中介于两个指定下标之间的字符。
  • substring 与 slice()substr() 方法不同的是,substring() 不接受负的参数
  • substr(start,end)返回一个新的数组,包含从 start 到 end (不包括该元素)的 arrayObject 中的元素。
  • 此三个方法,第二个参数是可选的
  • slice方法字符串数组都有

要删除指定位置的字符,或者在指定位置添加字符串,需要先将字符串转为数组。使用数组的方法splice()

  • splice(index, num,item1,item2...) 方法向/从数组中添加/删除项目,然后返回被删除的项目。该方法会改变原始数据。 前两个参数是必需的。


@ 通过正则来实现

正则是操作字符串的绝佳工具


function getUrlParam(key) {
  let searchText = location.search.substring(1) || '?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1'.substring(1);
  let reg = new RegExp('(^|&)' + key + '=([^&]*)(&|$)');
  let matchObj = searchText.match(reg);
  console.log("matchObj:",matchObj,"matchObj[2]:",matchObj[2])
  if (matchObj) return decodeURIComponent(matchObj[2]);
  return null;
}

getUrlParam('ie');//  "utf-8"

// matchObj: (4) ["ie=utf-8&", "", "utf-8", "&", index: 0, input: "ie=utf-8&f=8&rsv_bp=1&rsv_idx=1", groups: undefined]
// matchObj[2]: utf-8

输出的是一个object对象,类似于数组,object对象第一个参数就是匹配成功的那个字符串,第二个参数是reg中第一个小括号里面的内容即(&),第三个参数是第二个小括号([^&]*)里面的,以此类推。


三、获取字符串,数字,数组,对象等长度的方法

@ 获取对象的长度  方法一

length不是谁想有,想有就能用的。对象{}就没有长度。那么如何获取对象的长度呢,对象的Key呀,获取取来就是一个数组,就有length了呀。

Object.keys({a:1,b:2}).length;// 2
{a:1,b:2}.length;// Uncaught SyntaxError: Unexpected token ':'

@ 获取对象、数组、字符串的长度 方法二 统一封装

/*获取对象、数组的长度、元素个数
   *@param obj 要计算长度的元素,可以为object、array、string
  */
  function count(obj){
    var objType = typeof obj;
    if(objType == "string"){
      return obj.length;
    }else if(objType == "object"){
      var objLen = 0;
      for(var i in obj){
        objLen++;
      }
      return objLen;
    }
    return false;
  }

count({a:1,b:2}); // 2
count([1,2]);  // 2
count("aaa"); // 2
count(123); // false

@ 获取数字的长度 

转为字符串后取长度,就有了length属性

var nums= 123456;
console.log(nums.toString().length); // 6
console.log((nums+"").length); // 6


四、写一个方法把下划线命名转成驼峰命名

//如:ab_cd_ef ->  AbCdEf

考点分析

  • 字符串字母小写转大写toUpperCase
  • 字符串特殊字符split传参直接切割掉
  • 字符串是以特殊字符开头的话split后第一项是空字符串,的容错处理
  • 字符串不转数组处理的话,就只能用replace进行替换

@ 正则表达式实现

开始本打算用正则表达式来实现,但是这样有一个弊端。无法正常解答_aff_bff_cd_ef

function formatHump(str) {
  return str
    .replace(/^(\w)/, (match, $1) => $1.toUpperCase())
    .replace(/(\w)(_)(\w)/g, (match, $1, $2, $3) => {
      console.log(match);
      return $1 + $3.toUpperCase();
    });
}

console.log(formatHump('ab_cd_ef_ghi'));//AbDeDef

@ 常规方法实现

可以满足'_aff_bff_cd_ef'特殊字符串替换

function formatHump(str) {
  return str
    .split('_')
    .map((item) => item[0] && item[0].toUpperCase() + item.slice(1))//substr(1)和subString(1)都行,本人更喜欢用slice(1)
    .join('');
}
console.log(formatHump('_aff_bff_cd_ef'));
// AffBffCdEf

'_aff_bff_cd_ef'.split('_')
// ["", "aff", "bff", "cd", "ef"]

分析:

首先搞清楚输入输出,把字符串转为数组,以_为界进行切割。拿到数组进行遍历,把每一项的第一个字母转为大写并拼接后面的小写字符串。如果这个数组中的第一项是_下划线,那么,转数组时,第一项将是一个空字符串。需要单独处理,如果第一项中的第一个字母,那么在转大写并拼接后面的字符串。后面的字符串即使不存在,进行截取时也不会报错,就不用考虑了。





字符串操作-总结

  • 字符串操作,需要遍历时,考虑for...of,或者 转为数组处理好后再转为字符串
  • 用下标来获取数组中的指定索引的项,用下标来获取字符串中的指定下标的字符。