前端面试十一

98、比较typeof()和instanceof()   通过Object.prototype.toString.call()可以判断所有变量的类型

相同点:JavaScript中typeof 和 instanceof 常用来判断一个变量是否为空,或者是什么类型的

typeof:返回值是一个字符串,用来说明变量的数据类型

(1) typeof 一般只能返回如下几个结果:number,boolean,string,function,object,undefined

(2) 对于 Array,Null,Object 等特殊对象使用 typeof 一律返回 object,这正是 typeof 的局限性

instanceof:判断一个变量是否属于某个对象的实例

(1) 一般用于Array   Object   null的判断

var a = new Array(); 
alert(a instanceof Array);  // true
alert(a instanceof Object)  // true

这是因为Array是Object的子类,所以要进一步判断是Array的话还要进行下面的操作

var a = [1,2];
if(Object.prototype.toString.call(a) == '[object Array]'){
    console.log('1');
}

var b = {};
if(Object.prototype.toString.call(b) == '[object Object]'){
    console.log('2');
}

99、实现数组的混序:利用array中的sort函数,然后再里面定义一个回调函数,利用Math.random()产生一个随机数,当大于某个数的时候返回1,否则返回-1

function mixArr(arr){
	var len = arguments.length;
	if( !len){
		return '请输入参数';
	}else if( len!=1 ){
		return '输入的参数为一个';
	}else if(!Array.isArray(arr)){
		return '输入的参数必须为数组';
	}
	arr.sort(function() {
		var rand = (Math.random() > 0.5) ? 1 : -1;
		return rand;
	});
	return arr;
}
var arr = [5,1,8,3];
arr = mixArr(arr);
console.log(arr);
function randomNum(min,max){
	return Math.round(Math.random()*(max-min)+min)
}

function mixArr(arr){
	var len = arguments.length;
	if( !len){
		return '请输入参数';
	}else if( len!=1 ){
		return '输入的参数为一个';
	}else if(!Array.isArray(arr)){
		return '输入的参数必须为数组';
	}
	len = arr.length;
	for(var i =0;i<len;i++){
		var index = randomNum(0,i);
		var temp = arr[index];
		arr[index] = arr[i];
		arr[i] = temp;
	}
	return arr;

}
var arr = [1,2,34,8,5];
console.log(mixArr(arr))

100、有一个长度为100的数组,值为其所在位置的索引

//方法一
// var a = new Array(100);
// a = a.join(",").split(",").map(function(item, index) {
//     return index;
// });
// console.log(a);

//方法2
var a = [];
for(var i=0;i<100;i++){
	a.push(i);
}
console.log(a);

102、字符串转驼峰写法:利用split(‘-’)将字符串转为数组,然后对从第二个元素开始,用arr[i].charAt(0).toUpperCase()+arr[i].slice(1)先将每个元素的第一个元素转为大写,然后在将后面的元素进行拼接

function changeToHump(str){
	if( !arguments.length ){
		return '请输入字符串变量';
	}else if(typeof(str) !== 'string'){
		return '变量是字符串';
	}else if(!str.length){
		return '字符串变量不能为空';
	}
	var arr = str.split('-');
	var len = arr.length;
	if(len > 1){
		for(var i=1;i<len;i++){
			arr[i] = arr[i].charAt(0).toUpperCase() + arr[i].slice(1);
		}
		str = arr.join('');
	}
	return str;

}
var str = 'aa-bb';
str = changeToHump(str);
console.log(str);

103、js面试代码题

104、输出日期的格式为‘2018-5-31’

var d = new Date();
var year = d.getFullYear();
var month = d.getMonth() + 1;  //月数是从0-11的,所以需要加上1
month = month<10 ? '0'+month : month;
var day = d.getDate();
day = day<10 ? '0'+day : day;
var date = year + '-' + month + '-' + day;
console.log(date);

105、写一个函数对特殊字符进行转义

 function escapeHtml(str) {
	return str.replace(/[<>"&]/g, function(match) {
	    switch (match) {
	        case '<': return '<'; break;
	        case '>': return '>'; break;
	        case '&': return '&';break;
	        case '\"': return '"';break;
	    }
    });
 }
 var str='ss<>s"&s';
 str = escapeHtml(str);
 console.log(str);

105、用js实现随机选取10–100之间的10个数字,存入一个数组,并排序

// 用js实现随机选取10–100之间的10个数字,存入一个数组,并排序
//start--开始的数字,end--结束的数字,num---要取得的数字的长度
function productRandNumAndSort(start,end,num){
	var diff = end-start;
	var arr = [];
	var len = num;
	var randNum;
	while(len--){
		randNum = Math.round((Math.random()*diff) + start);
		arr.push(randNum);
	}
	arr = quickSort(arr);
	return arr;
}

function quickSort(arr){
	if(!Array.isArray(arr)){
		return '输入参数必须为数组';
	}
	var len = arr.length;
	if(len<2){
		return arr;
	}
	var midIndex = Math.floor(len/2);
	var midEle = arr.splice(midIndex,1);
	var left = [];
	var right = [];
	for(var i=0;i<len-1;i++){
		if(midEle>arr[i]){
			left.push(arr[i]);
		}else{
			right.push(arr[i]);
		}
	}
	return quickSort(left).concat(midEle,quickSort(right));
}
var new1 = productRandNumAndSort(10,100,10);
console.log(new1);

106请实现,鼠标点击页面中的任意标签,console该标签的名称(注意兼容性)

 <script type="text/javascript">
    window.onload = function(){
      var obj = document.getElementsByTagName('body')[0];
      obj.onclick = function(event){
        //在IE中获取到事件需要使用window.event
        var eve = event || window.event;
        //在IE中需要使用eve.srcElement获取到事件源对象
        var targetObj = eve.target || eve.srcElement;
        console.log(targetObj.nodeName.toLowerCase());
      }
    }
 </script>

106、对String对象的方法扩展,使其能删除前后的空格

// 对 string 对象进行扩展,使其具有删除前后空格的方法
String.prototype.trim = function(){
	var reg = /(^\s*)|(\s*$)/g;	
	return this.replace(reg,'');
}

var str='  dfd  ';
str = str.trim();
console.log(str);

107、对cookie的封装

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>alert标签名</title>
</head>
<body>
    <script>
        setCookie('age','1');
        var age = getCookie('age');
        console.log(age);
        delCookie('age');
         age = getCookie('age');
        console.log(age);
        //设置cookie
        function setCookie(name, value, days) {
            days = days || 30;
            var exp = new Date();
            exp.setTime(exp.getTime() + days * 24 * 60 * 60 * 1000);//toUTCString()将根据世界时,将日期对象转换为字符串
            document.cookie = encodeURIComponent(name) + "=" + encodeURIComponent(value) + ";expires=" + exp.toUTCString();
        }
        //获取cookie
        function getCookie(name) {
            var arr, reg = new RegExp("(^| )" + name + "=([^;]*)(;|$)");
            if (arr = document.cookie.match(reg)){
              //getCookie('name')
              //结果: ["name=jiang;", "", "jiang", ";", index: 0, input: "name=jiang; name=jiang", groups: undefined]
              console.log(arr);
               return decodeURIComponent(arr[2]);
            }
            else return null;

        }
        //删除cookie
        function delCookie(name) {
            var exp = new Date();
            exp.setTime(exp.getTime() - 1);
            var value = getCookie(name);
            if (value != null){
               document.cookie = encodeURIComponent(name) + "=" + encodeURIComponent(value) + ";expires=" + exp.toUTCString();
            } 
        }
    </script>
</body>
</html>

107、事件绑定和普通事件的区别

事件绑定和普通绑定有什么区别

1)普通事件是DOM0级事件,同一对象只支持单个事件,比如对同一对象添加多个onclick事件,那么只会响应最后一个onclick事件

2)事件绑定是DOM2级事件,同一对象可以添加多个事件,比如对同一对象使用多个addEventListener来监听click事件,执行顺序由前向后

3) IE事件处理程序,使用attachEvent()会在全局作用域执行,函数里面的this=window;对同一对象可以使用attachEvent()来添加多个事件处理器,执行顺序由后向前

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>alert标签名</title>
</head>
<body>
    <div id='hello'>dfdfdfd</div>
    <script>
        //使用普通事件
        // var btn = document.getElementById("hello");
        //   btn.onclick = function(){
        //       alert(1); //不执行
        //   };
        //   btn.onclick = function(){
        //       alert(2); //弹出
        //   }; 

        //绑定事件
        var btn = document.getElementById("hello");
            btn.addEventListener("click",function(){
                alert(1);  //弹出
            },false);
            btn.addEventListener("click",function(){
                alert(2);  //弹出
            },false);      
    </script>
</body>
</html>

108、说出下面代码的问题,并提供解决的方法

for(var i=0;i<5;i++){//5 5 5 5 5
       setTimeout(function(){console.log(i)},i*1000);
 }

 // 如果想要输出(0,1,2,3,4),改为立即执行函数
for(var i =0;i<5;i++){
    (function(){
        setTimeout(function(){
            console.log(i);
        },i*100)
    })(i)
}

//或者在里面调用一个函数
for(var j=0;j<5;j++){
    showData(j);
} 


function showData(data){
    setTimeout(function(){
        console.log(data);
    },1000);
} 

109、输出结果

var undefined;
console.log( undefined == null);//true
console.log( 1 == true ); //true
console.log(2 == true ); //false
console.log( 0 == false );//true
console.log(0 == ''  );//true
console.log(NaN == NaN );//false
console.log([] == false);//true
console.log([] == ![] );//true

110、输出ul li中的下标:立即执行函数+闭包      或者使用Li的属性index

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>alert标签名</title>
</head>
<body>
    <ul id='div1'>
        <li>这是第一条</li> 
        <li>这是第二条</li>
        <li>这是第三条</li>
    </ul>
    <script>
        var obj = document.getElementById('div1').children;
        var len = obj.length;
        var tempObj;
        //方法一:使用立即执行函数,和闭包
        // for(var i=0;i<len;i++){
        //     tempObj = obj[i];
        //     tempObj.onclick = (function(data){
        //         return function(){
        //             console.log(data);
        //         }
        //     })(i);
        // }

        //方法二:利用列表元素有属性index
        for(var i=0;i<len;i++){
            tempObj = obj[i];
            tempObj.index = i;
            tempObj.onclick = function(){
                console.log(this.index);
            };
        }

    </script>
</body>
</html>

111、字符串反转

//利用新字符串和charAt()
// function reverseStr(str){
// 	if(typeof(str) !=='string'){
// 		return 
// 	}
// 	var len = str.length;
// 	var newStr = '';
// 	if(len<2){
// 		newStr = str;
// 	}else{
// 		for(var i=len-1;i>=0;i--){
// 			newStr += str.charAt(i);
// 		}
// 	}
// 	return newStr;
// }

//方法二:先用split()转成数组,在用数组的reverse()反转,在用array的join()转成字符串
function reverseStr(str){
	if(typeof(str) !=='string'){
		return 
	}
	var len = str.length;
	if(len<2){
		return str;
	}
	str = str.split('').reverse().join('');
	return str;
}


var str = 'abcd';
str = reverseStr(str)
console.log(str);

112、将数字改成RMB的形式,如123456,应该是123,456

function transfer(num){
	if(typeof(num)!=='number'){
		return
	}
	num = num + '';
	var len = num.length;
	if(len<=3){
		return num;
	}
	//字符串反转
	num = num.split('').reverse().join('');
	count = 0;
	var newStr = '';
	for(var i=0;i<len;i++){
		newStr += num.charAt(i);
		count += 1;
		if( count%3 ==0 && i!== len-1){
			newStr += ',';
		}
	}
	// 在将字符串反转回来
	newStr = newStr.split('').reverse().join('');
	return newStr;
}

var num = 1234567;
num = transfer(num);
console.log(num);
//方法一:利用正则的正向肯定预查
//数字千分位的特点是,第一个逗号后面数字的个数是3的倍数,正则:/(\d{3})+$/;
// 第一个逗号前最多可以有1至3个数字,正则:/\d{1,3}/。加起来就是/\d{1,3}(\d{3})+$/,
// 分隔符要从前往后加,就要将前面的数字“87”替换成“87,”,为什么是87不是874?因为874后面只剩下5位数字,
// 在632后加一个分隔符后,将只剩下97,不符合千分位要求,所以第一个分隔符后面的数字位数必须是3的倍数。
// 要匹配数字87,又要保证87后面数字位数是3的倍数,并且要将匹配的87替换成“87,”,就要用到(?=exp)
str = '87463297';
// console.log( str.replace(/\d{1,3}(?=(\d{3})+$)/g,function(s){
//   return s+','
// }));


//方法二:先将字符串转成数组,利用reverse反转数组后每3个数字后添加一个分隔符“,”,到字符串末尾除外,之后转回字符串
// 利用字符串和数组方法
console.info( str.split("").reverse().join("").replace(/(\d{3})+?/g,function(s){
  return s+",";
}).replace(/,$/,"").split("").reverse().join("") )
// 换成美元的形式,保留两位小数位数
// 1233.0988 =》 $+1,233.10        -2134 => $-2,134.00
let num = 123.09898;

let flag = '+'; 
if( num<0 ){
   flag = '-';
}

let str1 = Math.abs(num).toFixed(2).toString();
let index = str1.indexOf('.');
let str = str1.slice(0,index); //整数部分

let result;

result =  str.split("").reverse().join("").replace(/(\d{3})+?/g,function(s){//+匹配1-多个,后面的?非贪婪匹配,即只匹配一个
  return s+",";
}).replace(/,$/,"").split("").reverse().join("");

result = '$' + flag + result + str1.slice(index);
console.log(result);

112、随机产生5个不同的数字

      function productVariNum(num){
            var arr =[];
            for(var i =0;i<num;i++){
                arr[i] = Math.floor(Math.random()*10) +1;
                for(var j=0;j<i;j++){
                    if(arr[i] == arr[j]){
                        i--;  break;
                    }
                }
            }
            return arr;
            
        }

        var arr = productVariNum(5);
        console.log(arr);
          
function productVariNum(num){
    var arr =[];
    var temp;
    for(var i =0;i<num;i++){
        temp = Math.floor(Math.random()*10) +1;
     	if(arr.indexOf(temp) === -1){
     		arr.push(temp);
     	}else{
     		i--;
     	}
    }
    return arr;
    
}

var arr = productVariNum(5);
console.log(arr);

113、阻止冒泡

document.getElementById('#name').onclick = function(e){
	var obj = e||window.event;
	obj.preventDefault();
	obj.stopPropagation();
	//todo
}

114、统计数组中单词个数

//方法1:使用for in遍历实现
// function count(arr){
// 	if(!arguments.length){
// 		return
// 	}else if(!Array.isArray(arr)){
// 		return 
// 	}
// 	var obj = {};
// 	if(!arr.length){
// 		return
// 	}
// 	for(var key in arr){
// 		if(obj[arr[key]]){
// 			obj[arr[key]]++;
// 		}else{
// 			obj[arr[key]] = 1;
// 		}
// 	}
// 	return obj;

// }
// arr = [];
// var obj = count(arr);
// console.log(obj);

var arr = ["apple","orange","apple","orange","pear","orange"];
//方法2:使用reduce(callback(必需-返回值,必需-当前元素,可选-当前元素的索引),给返回值的初始值)函数遍历数组的每一个元素
//返回值的类型是自己设定 ,不改变原数组
function getWordCnt(arr){
    //以下应掏空
    return arr.reduce(function(prev,next,index,arr){
        prev[next] = (prev[next] + 1) || 1; //这句是重点,刚开始都是undefined的时候undefined+1会是NaN
        return prev;
    },{});
}
console.log(getWordCnt(arr));

//方法3:array.map(function(currentValue,index,arr), thisValue),不改变原数组
function countWord(arr){
	var obj = {};
	arr.map(function(item){
		obj[item] = ( obj[item] + 1 ) || 1;
	});
	return obj;
}

console.log(getWordCnt(arr));

115、给定字符串 str,检查其是否包含连续重复的字母(a-zA-Z),包含返回 true,否则返回 false

//用了一个新的对象来作为判断的辅助
// function containsRepeatingLetter(str){
// 	var flag = false;
// 	if(!arguments.length){
// 		return
// 	}else if(typeof(str) !=='string'){
// 		return
// 	}
// 	var len = str.length;
// 	var obj = {};
// 	for(var i=0;i<len;i++){
// 		if( !obj[str.charAt(i)] ){
// 			obj[str.charAt(i)] = 1;
// 		}else{
// 			return flag=true;
// 		}
// 	}
// 	return flag;
// }



//方法2:使用正则
function containsRepeatingLetter(str) {
    //以下应掏空
    return /([a-zA-Z])\1/.test(str); // \1指代第一个括号的匹配项
}

var str = 'bdaa';
console.log(containsRepeatingLetter(str));

116、函数中的对象属性arguments和形参的关系,他们是相互关联的,其中一个改变了,另一个也改变

function f(a, b, c){
    console.log(a);  
    console.log(arguments.length);   
    var a = 200;
    console.log(arguments[0]);      
    arguments[0] = "haorooms";
    console.log(a);                  
    console.log(c);                  
    c = 2016;
    console.log(arguments[2]);       
}

f(1, 2)

输出结果:

1
2
200
haorooms
undefined
undefined

116、实现console.log(add(1,2))和console.log(add(1)(2))结果都是3

 function add(){
          var sum = arguments[0];
          var len = arguments.length;
          if(!len){
            return '';
          }else if(len == 1){
            var autoAdd = function(a1){
              sum += a1;
              return autoAdd;
            }
            autoAdd.toString = function(){
              return sum + '';
            }
            autoAdd.toValue = function(){
              return sum - 0;
            }
            return autoAdd;
          }else{
            var anotherSum = 0;
            for(var i=0;i<len;i++){
              anotherSum += arguments[i];
            }
            return anotherSum;
          }
        }

        console.log(add(1));

这种方法利用打印和相加会分别调用函数的toString()和valueOf(),这里可以兼容大部分的输入形式。

下面考虑一种不兼容的书写形式,也就是最简单的实现形式:

//仅仅考虑两个参数的情况
function add(){
  var len = arguments.length;
  if(!len){
    return;
  }else if(len ==1){
    var sum = arguments[0];
    return function(a1){
      return sum+a1;
    }
  }else if(len ==2){
    return arguments[0] + arguments[1];
  }
}

console.log(add(1,2));
console.log(add(1)(2));

117、对js中的柯里化函数currying的理解

定义

函数柯里化currying又称部分求值,它首先接受一些参数,接受这些参数之后不回立即求值,而是继返回一个函数,刚才传入的参数在形成的闭包中被保存起来,待到真正求值的时候,之前传入的所有参数都会一次性用于求值

currying的只有一个参数为函数(定义功能,即用所有参数完成的事情),返回值是一个函数其作用有两个-------1)当不满足计算条件的时候将参数保存起来然后调用arguments.callee;2)否则通过fn.apply(this,缓存数据的变量)计算结果

例子:计算月花销,计算的条件是函数没有传参的时候,否则将参数保存起来

// 功能函数
function add(){
	let sum = 0;
	let len = arguments.length;
	for(let i = 0;i<len;i++){
		sum += arguments[i];
	}
	return sum;
}

// fn---是功能函数
function currying(fn){
	var args = [];
	return function(){
		if(arguments.length=== 0){
			return fn.apply(this,args);
		}else{
			[].push.apply(args,arguments);
			arguments.callee;
		}
	}
}

let ss = currying(add);
ss(1);
ss(2);
console.log(ss());

把接受多个参数的函数,变换成一个接受单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数,返回结果是一个新函数的技术

简单的理解就是:逐渐传参,逐渐减小函数的使用范围,逐渐求解;

可以预先设定形参的个数(这种场景不太具由普遍性)

有参数的时候,返回一个函数;没有参数的时候,计算结果,达到延迟计算的目的

function curryIt(fn) {
    if(typeof fn !== 'function') throw new Error("curryIt():fn must be function");
    var len = fn.length; //获取函数形参数量个数
    var slice = Array.prototype.slice;  //或者写成[].slice
    var arg = slice.call(arguments,1); //将函数的形参转化为数组,因为arguments是伪数组,不能调用Array的API 
    return function() {
        arg = arg.concat(slice.call(arguments));
        if(arg.length < len) { //当等于函数需要的形参数量时候调用
            //输入参数与预设参数的个数不一样的时候,调用函数本身
            return arguments.callee;
        }else{
            //输入参数与预设参数一致的时候,计算结果
            return fn.apply(null,arg);  //null这个对象是用于确定this的值的,这里为null,则fn中的this就指向全局对象window
        }
    }
}

var fn = function(a, b, c) {
    return a + b + c
};
console.log(curryIt(fn)(1)(2)(3)); //6

118、实现Cal(2).add(1).reduce()2.multiple(4).divide(3)

// Cal(2).加(1).减(2).乘(4).除(3),链式操作
// 在类中定义公共属性result
// 在prototype上定义方法,方法返回this对象)
function Cal(num1){
	this.result = num1;
}

Cal.prototype.add = function(num2){
	this.result = this.result + num2;
	console.log('result:'+ this.result);
	return this;
}

Cal.prototype.reduce = function(num3){
		this.result = this.result - num3;
		console.log('result:'+ this.result);
		return this;
	}

Cal.prototype.multiple = function(num4){
		this.result = this.result * num4;
		console.log('result:'+ this.result);
		return this;
	}

Cal.prototype.divide = function(num5){
		this.result = this.result/num5;
		console.log('result:'+ this.result);
		return this;
}

console.log(new Cal(2).add(1).reduce(2).multiple(4).divide(3));

118、对回文的理解,从前读到后面和从后面读到前面的结果一样

下面如:'abba'和'abdba'如果认为是回文的话,可以用下面这种形式去判断

function isHuiwen(str){
	if(typeof(str) !== 'string'){
		return false
	}
	if(str.length<2){
		return false
	}
	if(str.split('').reverse().join('') === str){
		return true;
	}else{
		return false;
	}
	
}
var str = ''
console.log(isHuiwen(str));
str = 'abba';
console.log(isHuiwen(str));
str = 'abdba';
console.log(isHuiwen(str));

但是如果 认为'abdba不是回文,那么就用下面这种形式:对元素从头到尾遍历一次,使用lastIndexOf()确定right的索引值

//输入:cabbeaf
//4
String.prototype.roundWord = function() {
    var i = 0,
        str = this,
        count = 0, //回文计数
        left, right = str.length - 1,
        flag = false;

    if (str.length <= 0) throw new Error("roundWord(): arguments str/this must be string");

    while (i < str.length) { 
        charOne = str.charAt(i);         //searchvalue要检索的字符串,fromindex开始检索的位置
        // left = str.indexOf(charOne, i);  //stringObject.indexOf(searchvalue,fromindex)
        left = i;
        if (!flag) {
            right = str.lastIndexOf(charOne); //lastIndexOf()找不到元素,则返回-1
        } else {
            right = str.lastIndexOf(charOne, right);
        }

        if (left !== right && left < right) { //头尾有相同字符,通过对字符出现的索引进行比较
            // if (++count >= max) max = count;//left < right,证明有找到匹配的元素,否则就为-

            count++;
            flag = true; //开始有回文
        }
        i++;

    }
    return count*2;
};

var str = "cababeacf123";
console.log(str.roundWord()); //6
str = "cabbeaf";
console.log(str.roundWord()); //4


119、求输出结果

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>alert标签名</title>
</head>
<body>
    <div id='hello'>dfdfdfd</div>
    <script>
        var out = 25,
        inner = {
            out: 20,
            func: function () {
                var out = 30;
                return this.out;
            }
        };
        console.log((inner.func, inner.func)());
        console.log(inner.func());
        console.log((inner.func)());
        console.log((inner.func = inner.func)());
    </script>
</body>
</html>

结果:

25
20
20
25

120、arr =[1,2,3,4,5,6,7,8],从数组每次取出3个数,如:123 456 781 234依次循环下去

可以使用队列:先进先出,就是每次执行的时候,先将数组前三个元素弹出保存在一个变量上,然后再将这三个元素push进数组里面

var arr = [1,2,3,4,5,6,7,8];
var temp;
var TempVar = '';
var num = 3;
var count = 0;
function loop(){
  TempVar = '';
  for(var i=0;i<num;i++){
    temp = arr.shift();
    TempVar += temp
    arr.push(temp);
  }
  count++;
  console.log('第'+count+'次输出: '+ TempVar);
  setTimeout(loop,1000);
}

loop();

121、题目描述:输入:[2,3,4,[55,77,[6,43,2]],33]
输出:[2, 3, 4, 55, 77, 6, 43, 2, 33]  

//正则,分割字符串
//将数组转成字符串,利用字符串的正则表达式和replace()将[]去掉,然后再转成数组,在将里面的元素转成数字
var arr=[2,3,4,[55,77,[6,43,2]],33];  
var arrs = arr.toString();  
var res = arrs.replace(/[]/g,"");      //去掉所有的[]    字符串  
var arr2 = res.split(",")
console.log(arr2);           //数组 
arr2 = arr2.map(function(item){
    return item = parseInt(item);
});
console.log(arr2);    
//递归 + 闭包
var arr=[2,3,4,[55,77,[6,43,2]],33];  

function toArr(arr){
  let result = [];
  inner(arr);
  
  function inner(arr){
    let len = arr.length;
    for(let i=0;i<len;i++){
      if( {}.toString.call(arr[i]) === '[object Array]' ){
         inner(arr[i]);
      }else {
        result.push(arr[i]);
      }
    }
  }
  
  return result;
}    

console.log(toArr(arr));




122、规避javascript多人开发函数重名问题

1)使用命名空间:命名空间可以被认为是唯一标识符下代码的逻辑分组;防止与全局命名空间下的其他对象或变量产生冲突、有助于组织代码、有更强的可维护性和可读性。

由于javascript中只有函数作用域,那么把自定义的函数写到一个函数体内,这样函数内的变量、对象、函数就像在一个命名空间内一样和外部隔离(在立即执行函数里面,定义一个函数,并且定义他的原型,将其实例化给window的一个自定义属性)

           (function(){
                var _NS=function(){

                }
                _NS.prototype.alert=function(){
                    console.log('test');
                }
                window.NS=new _NS();
            })();

调用的时候直接调用NS.alert()这样就不会和window.alert()函数重名了

2)尽量少在全局环境下定义变量

3)加上前缀

4)模块化开发

123、this的典型应用场景

1)作为对象方法调用,这时候要小心,如果在一个函数的里面有定义了一个函数并且内部有this对象,这时候通常会将this保存在that里面

var test = {
    a:0,
    b:0
    get:function(){
        return this.a;
    }
}
var point = { 
 x : 0, 
 y : 0, 
 moveTo : function(x, y) { 
      var that = this;    //that指向point对象
     // 内部函数
     var moveX = function(x) { 
     that.x = x; 
     }; 
     // 内部函数
     var moveY = function(y) { 
     that.y = y; 
     } 
     moveX(x); 
     moveY(y); 
     } 
 }; 
 point.moveTo(1, 1); 
 point.x; //==>1 
 point.y; //==>1

2)作为函数调用 

 function makeNoSense(x) { 
     this.x = x; 
 } 

3)作为构造函数调用 

function Point(x, y){ 
    this.x = x; 
    this.y = y; 
 }

4)在call和apply中使用

124、网页中实现一个计算当年还剩多少时间的倒数计时程序,要求网页上实时动态显示"××年还剩××天××时××分××秒"

<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <title>TEst</title>
</head>
<body>

    <span id="target"></span>


<script type="text/javascript">
    // 为了简化。每月默认30天
    function getTimeString() {
        var start = new Date();
        //new Date(1,0,1)这三个参数是年月天
        var end = new Date(start.getFullYear() + 1, 0, 1); //月数从0开始
        var elapse = Math.floor((end - start) / 1000);

        var seconds = elapse % 60 ;
        var minutes = Math.floor(elapse / 60) % 60;
        var hours = Math.floor(elapse / (60 * 60)) % 24;
        var days = Math.floor(elapse / (60 * 60 * 24)) % 30;
        var months = Math.floor(elapse / (60 * 60 * 24 * 30)) % 12;
        var years = Math.floor(elapse / (60 * 60 * 24 * 30 * 12));

        return start.getFullYear() + '年还剩' + years + '年' + months + '月' + days + '日'
            + hours + '小时' + minutes + '分' + seconds + '秒';
    }
//textContent和innerText是浏览器兼容性问题
    function domText(elem, text) {
        if (text == undefined) {

            if (elem.textContent) {
                return elem.textContent;
            } else if (elem.innerText) {
                return elem.innerText;
            }
        } else {
            if (elem.textContent) {
                elem.textContent = text;
            } else if (elem.innerText) {
                elem.innerText = text;
            } else {
                elem.innerHTML = text;
            }
        }
    }

    var target = document.getElementById('target');

    setInterval(function () {
        domText(target, getTimeString());
    }, 1000)
</script>

</body>
</html>

126、兴趣爱好:主要是回答和你从事的岗位相关的答案,比如前端岗位,则关注一些公众号

127、实现对一个数组的混序,也就是洗牌

//里面定义一些公用的方法
//取一个包括min  max的值
function getRandomInt(min, max) {
  //Math.random()返回值0-1,包括0不包括1
  //加1表示取到上限值
  return Math.floor(Math.random() * (max - min + 1) + min)
}
//实现洗牌函数
function shuffle(arr) {
  let len = arr.length
  for (let i = 0; i < len; i++) {
    //获取随机的index
    let j = getRandomInt(0, i)
    //进行元素间的交换
    let t = arr[i]
    arr[i] = arr[j]
    arr[j] = t
  }
  return arr
}

128、vue中的混入对象mixins

vue中的混入对象mixins的定义

 是一种分发 Vue 组件可复用功能的非常灵活的方式混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被混入该组件本身的选项

mixins:Array(Object)

选项合并:当组件和混入对象含有同名选项时,这些选项将以恰当的方式混合

1)同名钩子函数将混合为一个数组,因此都将被调用(组合)

2)混入对象的钩子将在组件自身钩子之前调用(调用顺序)

3)数据对象data在内部会进行浅合并 (一层属性深度),在和组件的数据发生冲突时以组件数据优先(冲突)

4)两个对象键名冲突时,取组件对象的键值对

129、vue中的vuex插件 

vuex插件用于组件之间共享一些数据,具体可以参考网址:点击打开链接

地址:https://blog.csdn.net/tangxiujiang/article/details/80645416

130、javascript中的深拷贝和浅拷贝

编程实现使用JavaScript实现一个深拷贝方法

递归实现深拷贝----Object.prototype.toString.call()函数实现深度拷贝

function getType(obj){
   //tostring会返回对应不同的标签的构造函数
   var toString = Object.prototype.toString;
   var map = {
      '[object Boolean]'  : 'boolean', 
      '[object Number]'   : 'number', 
      '[object String]'   : 'string', 
      '[object Function]' : 'function', 
      '[object Array]'    : 'array', 
      '[object Date]'     : 'date', 
      '[object RegExp]'   : 'regExp', 
      '[object Undefined]': 'undefined',
      '[object Null]'     : 'null', 
      '[object Object]'   : 'object'
  };
  if(obj instanceof Element) {
       return 'element';
  }
  return map[toString.call(obj)];
}

function deepClone(data){
       var type = getType(data);
       var obj;
       if(type === 'array'){
           obj = [];
           for(var i = 0, len = data.length; i < len; i++){
               obj.push(deepClone(data[i]));
           }
       } else if(type === 'object'){
           obj = {};
           for(var key in data){
               obj[key] = deepClone(data[key]);
           }
       } else {
           //不再具有下一层次
           //函数不需要再进行遍历
           obj = data;
       }
       return obj;
}

下面的方法可以实现深浅拷贝,用一个参数来表示

const isArray = data =>  Object.prototype.toString.call(data)==='[object Array]';
const isObject = data => Object.prototype.toString.call(data)==='[object Object]';
const isNullOrUndefined = data => Object.prototype.toString.call(data)==='[object Undefined]' || !!Object.prototype.toString.call(data)==='[object Null]'

function underscoreToHump(data, isDeep=true) {
  let ret; //store compute result
  let deep = isDeep;
  if(isNullOrUndefined(data)){
    throw Error('param is error');
  }


     if( isArray(data) ){
        ret = [];
      }else if( isObject(data) ){
        ret = {};
      }

      for(let key in data){
        let val = data[key];
        let arrayFlag;

        if(deep && ( arrayFlag=isArray(val) ||  !!isObject(val) )){
          if(arrayFlag){
            arrayFlag=false;
            ret[key] = [];
          }else{
            ret[key] = {};
          }
          ret[key] = underscoreToHump(val, deep);
        }else{
          ret[key] = data[key];
        }
      }
 
  return ret;
}




const testData = {
    a_v: 123,
    a_y: [1, 2, 3, 4], 
    a_d: {
        s: 2,
        s_3: 3
    },
    a_f: [{
        a_g: 5
    }],
    a_a_d: 1
}
const result = underscoreToHump(testData,false)
result.a_y.push(1);
console.log(result)
console.log(testData)

JavaScript有两种数据类型:基础数据类型、引用数据类型

基础数据类型:按值访问,可以直接操作保存在变量中的实际值

引用类型:如Array,Object不能直接操作对象的堆内存空间;引用类型的值都是按引用访问的

深拷贝和浅拷贝的区别
浅拷贝(shallow copy):对于基础类型的复制是按值复制;对于引用类型是按照地址复制,新旧对象共享一块内存; 
深拷贝(deep copy):复制并创建一个一摸一样的对象不共享内存,修改新对象,旧对象保持不变。

1、数组

浅拷贝:相当于使两个数组指针指向相同的地址,任一个数组元素发生改变,影响另一个

深拷贝:两数组指针指向不同的地址,数组元素发生改变时不会相互影响

浅拷贝实现:赋值运算符

var a = [1, 2, 3]
    b = a
console.log("a: ",a)
console.log("b: ",b)

b[0] = 99;
console.log("---- after changed array b ----- ")
console.log("a: ",a)
console.log("b: ",b)

深拷贝实现:concat()   slice()    map()   reduce()   forEach()    

存在的问题:只能处理一层的深度拷贝

//1、slice()方法
//一维数组
// var a = [1, 2, 3, 4, 5, 6]
//     b = a.slice(0)
// console.log("a: ",a)
// console.log("b: ",b)

// b[0] = 99;
// console.log("---- after changed deepClone array b ----- ")
// console.log("a: ",a)
// console.log("b: ",b)
//运行结果
// a:  [ 1, 2, 3, 4, 5, 6 ]
// b:  [ 1, 2, 3, 4, 5, 6 ]
// ---- after changed deepClone array b ----- 
// a:  [ 1, 2, 3, 4, 5, 6 ]
// b:  [ 99, 2, 3, 4, 5, 6 ]

//二维数组
// var a = [[1, 2, 3], 4, [5, 6]]
//     b = a.slice(0)
// console.log("a: ",a)
// console.log("b: ",b)

// b[0][0] = 99;
// console.log("---- after changed deepClone array b ----- ")
// console.log("a: ",a)
// console.log("b: ",b)

//输出结果
// a:  [ [ 1, 2, 3 ], 4, [ 5, 6 ] ]
// b:  [ [ 1, 2, 3 ], 4, [ 5, 6 ] ]
// ---- after changed deepClone array b ----- 
// a:  [ [ 99, 2, 3 ], 4, [ 5, 6 ] ]
// b:  [ [ 99, 2, 3 ], 4, [ 5, 6 ] ]


//2、concat()方法
//一维数组
// var a = [1, 2, 3, 4, 5, 6]
//     b = a.concat([])
// console.log("a: ",a)
// console.log("b: ",b)

// b[0] = 99;
// console.log("---- after changed deepClone array b ----- ")
// console.log("a: ",a)
// console.log("b: ",b)
//运行结果
// a:  [ 1, 2, 3, 4, 5, 6 ]
// b:  [ 1, 2, 3, 4, 5, 6 ]
// ---- after changed deepClone array b ----- 
// a:  [ 1, 2, 3, 4, 5, 6 ]
// b:  [ 99, 2, 3, 4, 5, 6 ]

//二维数组
// var a = [[1, 2, 3], 4, [5, 6]]
//     b = a.concat([])
// console.log("a: ",a)
// console.log("b: ",b)

// b[0][0] = 99;
// console.log("---- after changed deepClone array b ----- ")
// console.log("a: ",a)
// console.log("b: ",b)
// 运行结果
// a:  [ [ 1, 2, 3 ], 4, [ 5, 6 ] ]
// b:  [ [ 1, 2, 3 ], 4, [ 5, 6 ] ]
// ---- after changed deepClone array b ----- 
// a:  [ [ 99, 2, 3 ], 4, [ 5, 6 ] ]
// b:  [ [ 99, 2, 3 ], 4, [ 5, 6 ] ]

//3、map()方法
//一维数组
// var a = [1, 2, 3, 4, 5, 6]
//     b = a.map(function(item){
//     	return item;
// 	})
// console.log("a: ",a)
// console.log("b: ",b)

// b[0] = 99;
// console.log("---- after changed deepClone array b ----- ")
// console.log("a: ",a)
// console.log("b: ",b)
//运行结果
// a:  [ 1, 2, 3, 4, 5, 6 ]
// b:  [ 1, 2, 3, 4, 5, 6 ]
// ---- after changed deepClone array b ----- 
// a:  [ 1, 2, 3, 4, 5, 6 ]
// b:  [ 99, 2, 3, 4, 5, 6 ]

//二维数组
// var a = [[1, 2, 3], 4, [5, 6]]
//     b = a.map(function(item){
//     	return item;
// 	})
// console.log("a: ",a)
// console.log("b: ",b)

// b[0][0] = 99;
// console.log("---- after changed deepClone array b ----- ")
// console.log("a: ",a)
// console.log("b: ",b)
// 运行结果
// a:  [ [ 1, 2, 3 ], 4, [ 5, 6 ] ]
// b:  [ [ 1, 2, 3 ], 4, [ 5, 6 ] ]
// ---- after changed deepClone array b ----- 
// a:  [ [ 99, 2, 3 ], 4, [ 5, 6 ] ]
// b:  [ [ 99, 2, 3 ], 4, [ 5, 6 ] ]

//3、forEach()方法
//一维数组
// var a = [1, 2, 3, 4, 5, 6],b=[]
//     a.forEach(function(item){
//     	b.push(item);
// 	})
// console.log("a: ",a)
// console.log("b: ",b)

// b[0] = 99;
// console.log("---- after changed deepClone array b ----- ")
// console.log("a: ",a)
// console.log("b: ",b)
//运行结果
// a:  [ 1, 2, 3, 4, 5, 6 ]
// b:  [ 1, 2, 3, 4, 5, 6 ]
// ---- after changed deepClone array b ----- 
// a:  [ 1, 2, 3, 4, 5, 6 ]
// b:  [ 99, 2, 3, 4, 5, 6 ]

//二维数组
var a = [[1, 2, 3], 4, [5, 6]],b=[]
    a.forEach(function(item){
    	b.push(item);
	})
console.log("a: ",a)
console.log("b: ",b)

b[0][0] = 99;
console.log("---- after changed deepClone array b ----- ")
console.log("a: ",a)
console.log("b: ",b)
// 运行结果
// a:  [ [ 1, 2, 3 ], 4, [ 5, 6 ] ]
// b:  [ [ 1, 2, 3 ], 4, [ 5, 6 ] ]
// ---- after changed deepClone array b ----- 
// a:  [ [ 99, 2, 3 ], 4, [ 5, 6 ] ]
// b:  [ [ 99, 2, 3 ], 4, [ 5, 6 ] ]

深拷贝实现:jQuery的extend函数,  

    // jQuery.extend([deep], target, object1, object2, object3...),
    // [deep]:默认false;值为true时深度合并对象,false的时候能进行一层深度拷贝
    // target:其他对象复制到该对象
    // object:被合并的对象

1、一维数组

var a = [1, 2, 3]
    b = $.extend(false, [], a)     //设置成true  和  false的结果一样
    // b = $.extend(true, [], a)
    console.log("a: ",a)  //[1,2,3]
    console.log("b: ",b)  //[1,2,3]

    b[0] = 99;
    console.log("---- after changed deepClone array b ----- ")
    console.log("a: ",a)  //[1,2,3]
    console.log("b: ",b)  //[99,2,3]

2、多维数组

将[deep]设为false

    var a = [[1, 2, 3], 4, [5, 6]]
    b = $.extend(false,[], a)
    console.log("a: ",a)
    console.log("b: ",b)

    b[0][0] = 99;
    console.log("---- after changed deepClone array b ----- ")
    console.log("a: ",a)
    console.log("b: ",b)

结果:这里将[deep]设为false,然后,改变前后的a  b  的值都一样,具体是因为什么,暂且不知道,希望大神指教

将[deep]设为true:可以看出a和b的值不一样,但是修改后的b影响修改后的b,两个的输出值一样

var a = [[1, 2, 3], 4, [5, 6]]
    b = $.extend(true,[], a)
    console.log("a: ",a)
    console.log("b: ",b)

    b[0][0] = 99;
    console.log("---- after changed deepClone array b ----- ")
    console.log("a: ",a)
    console.log("b: ",b)

结果:

总结:为了实现真正的Array的深拷贝,应该使用jQuery.extend(true,[],arr1,arr2,arr3...)

2、对象Object

浅拷贝:只拷贝对象的第一层属性,对于属性中包含的属性不会复制;由于JavaScript对象均以地址的方式存贮,所以浅复制导致多个对象的属性指向同一块地址

深拷贝:对对象的每一层属性进行递归复制,深层次的属性也不会指向同一块地址

浅拷贝:自定义函数

function shallowCopy(obj1, obj2) {
    for( var key in obj1){
        if (obj1.hasOwnProperty(key)) {
            obj2[key] = obj1[key]
        }
    }
}
function showKeys(obj) {
    for( var key in obj){
        if (obj.hasOwnProperty(key)) {
            console.log(key, ":", obj[key]);
        }
    }
}

var a = {
    sing:true, 
    dance:{today:false, tommorrw:true} 
} 
    b = {}
shallowCopy(a, b)
showKeys(a)
showKeys(b)

b.sing = false
b.dance.today = true
console.log("------------- after changed object b -------------- ")
showKeys(a)
showKeys(b)

输出结果:由于对象的浅拷贝只复制第一层属性,因此obj b第一层属性的改变不会影响复制源,而第二层属性仍指向同一块地址,因此obj b的dance属性的today属性作改变之后,同一块地址处的obj a的dance属性的today属性同时变化了

sing : true
dance : { today: false, tommorrw: true }
sing : true
dance : { today: false, tommorrw: true }
------------- after changed object b -------------- 
sing : true
dance : { today: true, tommorrw: true }
sing : false
dance : { today: true, tommorrw: true }

深拷贝:使用JQuery的extend(),[deep]设为true表示深复制,互不影响,false浅拷贝对于引用类型有影响

function showKeys(obj) {
    for( var key in obj){
        if (obj.hasOwnProperty(key)) {
            console.log(key, ":", obj[key]);
        }
    }
}

var a = {
    sing:true, 
    dance:{today:false, tommorrw:true} 
} 
    b = $.extend(true, {}, a)

showKeys(a)
showKeys(b)

b.sing = false
b.dance.today = true
console.log("------------- after changed object b -------------- ")
showKeys(a)
showKeys(b)

递归拷贝:

function deepClone(initalObj, finalObj) {    
  var obj = finalObj || {};    
  for (var i in initalObj) {        
    var prop = initalObj[i];        // 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况
    if(prop === obj) {            
      continue;
    }        
    if (typeof prop === 'object') {
      obj[i] = (prop.constructor === Array) ? [] : {};            
      arguments.callee(prop, obj[i]);
    } else {
      obj[i] = prop;
    }
  }    
  return obj;
}
var str = {};
var obj = { a: {a: "hello", b: 21} };
deepClone(obj, str);
console.log(str.a);

131、css预处理器:stylus

stylus:是css的一个预处理工具,对css的一个延伸和强化;是一种富有表现力的、动态的、强壮的css

1、stylus相对于SASS更简洁:冒号可以省略;用空格分隔属性名和多个属性值就可以生成css;可以拼接字符串;支持缩进语法;

body
  border 10px soli+'d' darken(red,10%)
等同于:
body {
   border: 1px solid #e60000;
}

2、动态性:Stylus由Javascript编译,其结构语句也和Javascript相差不多;Stylus较之LESS则要优越不少,不仅仅是可定义变量,如Javascript般的条件语句和循环语句也为Stylus带来各种可能,加上丰富的内置函数,可以轻松判断和操作各种变量。而利用这样的动态性,就可以写出非常强壮的CSS以满足不同环境和条件下的需要。定义函数、定义mixins、定义常量

详细介绍的文章

131、平时怎么学习的

1)做项目的时候会用到一些之前没有接触过的技术,这时候就会去看别人写的博客或者去stackoverflow查看

2)平时会买点书籍来补充自己的基础知识

3)去淘宝上买些视频或者慕课网上看些教学视频,然后动手去实现一些小DEMO

4)在github上看些开源项目,动手去实现里面的功能

131、d3和echarts的原理、区别

他们都是用于数据可视化中,实现图表的绘制

优缺点

D3:

1)太底层,学习成本大

2)兼容到IE9以上以及所有的主流浏览器

3)D3通过svg来绘制图形,可以自定义事件

Svg的优点

1)不依赖分辨率

2)基于xml绘制图形,可以操作dom、支持事件处理器

3)复杂度高,会减慢页面的渲染速度

Echarts:

1) 封装好的方法可以直接调用

2)兼容到ie6以及以上的所有主流浏览器

3)echarts通过canvas来绘制图形

Canvas:

1)依赖分辨率

2)基于js绘制图形、不支持事件处理器

3)能以png或者jpg的格式保存图片

应用场景

1)前端将数据通过图表的形式展示给用户,一般使用echarts----Echarts里面的方法封装比较好,用的时候直接调用就能实现效果

对于echarts的使用比较简单,引入echarts.js,然后就可以通过 echarts.init 方法初始化一个 echarts 实例并通过 setOption 方法设置图表实例的配置项以及数据,万能接口,所有参数和数据的修改都可以通过setOption完成,ECharts 会合并新的参数和数据,然后刷新图表。如果开启动画的话,ECharts 找到两组数据之间的差异然后通过合适的动画去表现数据的变化

2)对事件监听的话,一般使用D3---使用svg绘制图形,支持事件处理器。需要自己添加画布,绘制图形,绘制的每一个图形都为一个对象,可以添加相应的事件操作,操作dom

131、Servlet的生命周期

Servlet的生命周期指的是 Servlet从被Web服务器加载它被销毁的整个生命过程

1)初始化阶段,调用init()方法 
2)响应客户请求阶段,调用service()方法 
3)终止阶段,调用destory()方法

Servlet的工作原理

1)首先客户发送个请求,Servlet容器会创建特定于这个请求的ServletRequest对象和ServletResponse对象

2)然后调用Servlet的service()方法,Service()方法从ServletRequest对象获得客户请求信息,处理该请求,然后通过ServletResponse对象向客户返回响应信息

131、平衡二叉树的原理(AVL算法)

特点:它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树

131、服务端渲染和客户端渲染的区别

服务端渲染的定义---SSR

一些没有复杂逻辑的、简单的页面,由后端将html和数据拼接好,然后将其返回给浏览器,浏览器拿到这个html文件之后就可以直接渲染展示了

客户端渲染的定义----CSR

随着前端页面的复杂性提高,前端就不仅仅是普通的页面展示了,而可能添加了更多功能性的组件,复杂性更大,另外,彼时ajax的兴起,使得业界就开始推崇前后端分离的开发模式,即后端不提供完整的html页面,而是提供一些api使得前端可以获取到json数据,然后前端拿到json数据之后再在前端进行html页面的拼接,然后展示在浏览器上,这就是所谓的客户端渲染了,这样前端就可以专注UI的开发,后端专注于逻辑的开发。

两者本质的区别---html文件由谁拼接完成

如果html文件的拼接是由服务器端完成的,然后返回给客户端,就是服务器端渲染

如果html文件的拼接是由前端完成的,则就是客户端渲染

服务器端渲染的优缺点是怎样的?

优点:

  1. 前端耗时少。因为后端拼接完了html,浏览器只需要直接渲染出来
  2. 有利于SEO。因为在后端有完整的html页面,所以爬虫更容易爬取获得信息,更有利于seo。
  3. 无需占用客户端资源。即解析模板的工作完全交由后端来做,客户端只要解析标准的html页面即可,这样对于客户端的资源占用更少,尤其是移动端,也可以更省电。
  4. 后端生成静态化文件即生成缓存片段,这样就可以减少数据库查询浪费的时间了,且对于数据变化不大的页面非常高效 。

缺点:

  1. 不利于前后端分离,开发效率低。使用服务器端渲染,则无法进行分工合作,则对于前端复杂度高的项目,不利于项目高效开发。另外,如果是服务器端渲染,则前端一般就是写一个静态html文件,然后后端再修改为模板,这样是非常低效的,并且还常常需要前后端共同完成修改的动作; 或者是前端直接完成html模板,然后交由后端。另外,如果后端改了模板,前端还需要根据改动的模板再调节css,这样使得前后端联调的时间增加。
  2. 占用服务器端资源。即服务器端完成html模板的解析,如果请求较多,会对服务器造成一定的访问压力。而如果使用前端渲染,就是把这些解析的压力分摊了前端,而这里确实完全交给了一个服务器

客户端渲染的优缺点是怎样的?

优点:  

  1. 前后端分离。前端专注于前端UI,后端专注于api开发,且前端有更多的选择性,而不需要遵循后端特定的模板。
  2. 体验更好。比如,我们将网站做成SPA或者部分内容做成SPA,这样,尤其是移动端,可以使体验更接近于原生app。

缺点:

  1. 前端响应较慢。如果是客户端渲染,前端还要进行拼接字符串的过程,需要耗费额外的时间,不如服务器端渲染速度快。
  2. 不利于SEO。目前比如百度、谷歌的爬虫对于SPA都是不认的,只是记录了一个页面,所以SEO很差。因为服务器端可能没有保存完整的html,而是前端通过js进行dom的拼接,那么爬虫无法爬取信息。 除非搜索引擎的seo可以增加对于JavaScript的爬取能力,这才能保证seo。

使用服务器端渲染还是客户端渲染?

  不谈业务场景而盲目选择使用何种渲染方式都是耍流氓。比如企业级网站,主要功能是展示没有复杂的交互,并且需要良好的SEO,则这时我们就需要使用服务器端渲染;而类似后台管理页面,交互性比较强,不需要seo的考虑,那么就可以使用客户端渲染。

  另外,具体使用何种渲染方法并不是绝对的,比如现在一些网站采用了首屏服务器端渲染,即对于用户最开始打开的那个页面采用的是服务器端渲染,这样就保证了渲染速度,而其他的页面采用客户端渲染,这样就完成了前后端分离。

究竟如何理解前后端分离?

  实际上,时至今日,前后端分离一定是必然趋势,如今的网页功能越来越复杂,交互越来越多,将页面的渲染交给前端会比较好,使后台专注逻辑,前端专注UI。而我们目前接触到的前端工程化、编译(转译)、各种MVC/MVVM框架、依赖工具、npm、bable、webpack都给前端的工作性能和效率带来很大的提升

132、git的操作--主要是分支和合并(解决合并冲突)

http://www.ruanyifeng.com/blog/2015/12/git-cheat-sheet.html    详情

在当前目录新建代码库:git  init

下载一个项目和他的整个历史:git  clone   [url]

配置:git  config

增加:git  add     

删除:git rm     git mv

提交:git   commit

分支:git  branch

切换:git  checkout

合并:git   merge

:git  push

标签:git  tag

查看:git  show

显示变更文件:git  status

显示提交历史:git  log 6

132、 a^n 用小于 O(n) 的算法实现 -----用分治法和递归

每次重复相同操作,用递归

// 实现a^n,要求复杂度小于O(n)
// 用分治法和递归实现,时间复杂度为O(log(n))
function power(a,n){
	
	if(n===0){
		return 1;
	}else if(n===1){
		return a;
	}else if( parseInt(n%2) === 0 ){
		return power( a,n/2 ) * power( a,n/2 );
	}else if( parseInt(n%2) !== 0 ){
		return power( a, parseInt(n/2) ) * power( a, parseInt(n/2) ) * a ;
	}s
}

console.log( power(3,2) );

132、多端适配如何做?页面兼容平板、手机、PC等

1)flex布局:给flex容器设置:display:flex

容器属性

flex-direction:决定主轴的方向(项目的排列方向),row | row-reverse | column | column-reverse

flex-wrap:如果一条轴线排不下了,该如何换行,nowrap | wrap | wrap-reverse

flex-flow: flex-direction  flex-wrap的简写

justify-content:项目在主轴上的排列方式

align-items:项目在交叉轴上的对齐方式

项目属性

order:项目排列顺序,数值越小越靠前

flex-growth:项目放大比例,默认为0,即存在剩余空间也不放大

flex-shrink:项目缩放比率,默认为1,空间不足,进行缩小

flex-basie:在分配剩余空间之前,给项目分配的空间

flex:flex-growth   flex-shrink  flex-basie的缩写

2)媒介查询,@media screen  and (max-width:900px)  and (min-width:100px)----判断终端设备宽度在多少像素内,然后就执行与之对应的CSS样式

猜你喜欢

转载自blog.csdn.net/tangxiujiang/article/details/88719189