严格模式
主要的改变就是不允许其删除不能删除的属性,也不允许删除变量和函数,不能对只读的属性赋值,不能使用转义字符,不能重复定义,不能不定义(以前不定义的话会被绑到全局window上),不能把this绑到全局对象上,不允许使用with,保留eval和argument的关键字
作用就是为以后的js规范化做好铺垫,同时消除js中一些不合理的地方,提高编译器的效率(等于是用复杂一点的开发过程换了性能)
数组去重
你可以使用indexOf或者includes,前者无法判断NaN,undefined,后者可以
let a = [0,2,2,2,2,3,4,5,6,7,8,9];
let newa = [];
for (let i of a) {
if(newa.indexOf(i) == -1)newa.push(i);
}
console.log(newa);
你应当知道,indexOf对大小写敏感,若是让你写个对大小写不敏感的去重且根据先后顺序保留大小写呢
let a = "acjjisodajiACkKLSod";
let newa = [];
for (let i of a) {
if(newa.indexOf(i.toUpperCase()) == -1 && newa.indexOf(i.toLowerCase()) == -1){
newa.push(i);
}
}
console.log(newa.join(""),a);
前两个的算法复杂度都比较高,最为推荐的是排序双指针(两种sort方式都可行)(两种都会返回数组副本,你可以稍微改动就能不改变原数组了),缺点也很明显,要排序
let a = "acjjisodajiACkKLSod";
let newa = [];
a = a.split("").sort().join("");
//a = [...a].sort();
for(let i = 0,j = 0; i < a.length ; i++){
if(a[i] != a[j]){
newa.push(a[i]);
j = i;
}
}
console.log(newa.join(""),a);
不允许排序的情况下,最推荐的是,优化双层循环,对字符串肯定只能使其转化为数组,数组本身不用开辟新空间,除非要求修改副本,想法很简单,从i的第一个值开始检查其后所有值是否有重复的,把除第一个值之外的所有值咔嚓掉
let a = "acjjisodajiACkKLSod";
let newa = [...a];
//a = [...a].sort();
for(let i = 0; i < newa.length ; i++){
for(let j = i+1 ; j < newa.length ; j++){
if(newa[i] == newa[j]) newa.splice(j,1);
}
}
console.log(newa.join(""));
还有一种是set,不谈了,复杂度不清楚,不过大概率我看是没人会让你用的
let newa = [...new Set(a)];
最后一种用空间换时间,标记数组,按照说法是时间复杂度最低的一个,就连判大小写重复都是最快的一个,但是很明显内存也是耗得最多的一个
let a = "acjjjjjisodajiACkKLSod48115112";
let newa = [];
let amark = {};
for(let i of a){
if(!amark[i]){
amark[i] = true;
newa.push(i);
}
}
console.log(newa.join(""));
深拷贝与浅拷贝
浅拷贝只拷贝第一层,不会进行递归拷贝,也就是修改拷贝对象里的数组,也会修改到原对象里的数组,因为是在第一层直接拿下来的,指针是一样的,内存地址也是一样的
(官方的话是 对于引用类型数据,复制后仍然会被修改)
深拷贝则是递归拷贝,将里面的引用类型数据打开进行深层复制,彻底断开指针。
浅拷贝的原生实现:
let a = {b:1,c:2,d:3};
let newa = {};
for(let i in a){
if(a.hasOwnProperty(i))newa[i] = a[i];
}
console.log(newa);
注意hasOwnProperty 判断该值是否是自身的属性而非来自于父类(继承)
深拷贝的原生实现:
let a = {b:1,c:2,d:3,e:{f:1,j:2},p:[1,2,3]};
let newa = {};
function deepCopy(objold,objnew = {}){
for(let i in objold){
let checkType = typeof(objold[i]);
let checkConstructor = objold[i].constructor ? objold[i].constructor : null ;
if(!objold.hasOwnProperty(i))continue;
if(checkType != "object")objnew[i] = objold[i];
else{
console.log(checkConstructor,checkType)
if(checkConstructor === Array){
objnew[i] = [];
deepCopy(objold[i],objnew[i]);
}else{
objnew[i] = {};
deepCopy(objold[i],objnew[i]);
}
}
}
return objnew;
}
deepCopy(a,newa);
console.log(newa);
你应当知道,typeof引用类型可都是object的噢,判断是数组还是对象要用其构造函数类型或者instanceof
神奇的解构赋值全是浅拷贝,包括Object.assign()或者concat
最后如果你学过immutable.js,你可以向面试官秀肌肉啦,immutable.js的本质其实就是深拷贝噢,只不过他特别神奇的把对象内所有的引用类型都拆开了,修改哪个引用类型,就拷贝谁,再把修改后对象的替换原对象并把整个对象返回,避免了对整个对象的拷贝和直接的在原对象上的修改,保证了原对象的不变性。
某些字符串处理/判断问题
回文判断:
两种做法,切半或者逆序(所以切半到底有什么屁用。)
let str = "strrts";
str.split("").reverse().join("") === str ? console.log(true) : console.log(false);
let str = "strrts";
str.split("").splice(0,str.length/2).reverse().join("") === str.split("").splice(str.length/2,str.length/2).join("") ?
console.log(true) : console.log(false);
中横线转驼峰
let str = "str-rts-lll";
let newStr = "";
str.split('-').forEach((item,idx)=>{
if(idx == 0)newStr = newStr + item;
else{
let strTemp = item.charAt(0).toUpperCase() + item.substring(1);
newStr = newStr + strTemp;
}
})
console.log(newStr);
重复计数(其实跟去重一样啦)
let str = "str-rts-lll";
let strmark = {};
for(let i of str){
if(!strmark[i]){
strmark[i] = 1;
}else{
strmark[i]++;
}
}
let maxAt,maxNum;
for(let i in strmark){
if(!maxAt)maxAt = i,maxNum = strmark[i];
else{
if(strmark[i] > maxNum){
maxAt = i;
maxNum = strmark[i];
}
}
}
console.log(maxAt,maxNum);
不借助外界交换变量值
let a = 1;
let b = 3;
[a,b] = [b,a];
console.log(a,b);
let a = 3;
let b = 1;
a = a^b;
b = a^b;
a = a^b;
console.log(a,b);
1-10000有几个零或者其他?
let Reg = /0+/;
let ansNum = 0;
for(let i = 1 ; i < 10001 ; i++){
let str = i + "";
if(!Reg.test(str))continue;
ansNum = str.split('0').length - 1 + ansNum;
}
console.log(ansNum);
let Reg = /0+/;
let b = new Array(10000).fill('')
.map((item,idx) => {return idx+1})
.filter((item) => {
return Reg.test(item);
})
.reduce((count,item) => {
return String(item).split('0').length - 1 + count;
},0)
console.log(b);
两种方法的速度按照说法第一种是要快一些的,毕竟第二种既要map(本身就比for慢)又要过滤,最后还要累加
去前后空格
let str = " hello "
let Reg = /(^\s*)|(\s*$)/g;
console.log(str.replace(Reg,""));
大致就是匹配开头的任何空白字符。匹配结尾的任何空白字符,并全局匹配。最后进行替换
数组降维打击
let a = [[1,3],[3,4]];
[b,c] = a;
console.log(b.concat(c));
let a = [[1,3],[3,4]];
let b = Array.prototype.concat.apply([],a);
console.log(b);
当然你非要用call也可以,只是call不能接受数组参数,你要用循环迭代噢
重复字符串
let str = "str";
const repeatStr = (str = "",num = 0) => {
return new Array(num+1).join(str);
}
console.log(repeatStr(str,3));
用str替代分隔符,所以要num+1,毕竟分隔符的个数是num-1;
最后一个,地址参数截取
let html = "www.baidu.com/?xx=xxxx&xxxx=xxxxxxx&xxxxx=xxxxxx";
//let url = window.location.href;
let str = html.substring(html.lastIndexOf("?") + 1);
let obj = {};
str.split("&").forEach((item,idx)=>{
let itemMap = item.split("=");
let map,value = "";
if(itemMap.length == 0)return;
name = decodeURIComponent(itemMap[0]);
if(itemMap.length != 1){
value = decodeURIComponent(itemMap[1]);
}
obj[name] = value;
})
console.log(obj);
要嘛正则呢
/([^?&=]+)=([^?&=]*)/g
非要就长这样子,意思就是前面的name部分必须要,后面的value部分可有可无,但是都不能以?/&/=开头,在replace里面用$1,$2抽就行了