开头,首先谈一谈前几天去 携程 的面试吧,刚开始笔试的题目,坦白说都是比较简单的,开始大概是十道选择题,都是基础的理论题,一般基础还可以的人肯定都会写!接下来是几道程序的运行题,主要考一些变量提升,闭包形式的运行结果,this指向问题,随便举几个例子吧:
1. 首先说的这个题目其实相当经典的,很多地方都遇到过:
function Foo() {
var i = 0;
retunr function() {
console.log(i++);
}
}
var f1 = Foo(), f2 = Foo();
f1(); f1(); f2();
复制代码
其实这一题感觉就是考虑变量提升吧,输出结果依此就是 0 1 0;简单来说就是,第一次执行f1() 函数后,函数内部的 i 值就发生了改变,下一次执行f1(), i在原来的基础上做了++ , 所以就输出了 1,当执行f2函数的时候,重新执行了一次函数,所以输出还是原来的0, 我就是这么理解的,所以不多做解释啦....
2. 函数内部的变量问题:
var bb = 5;
function aa(bb) {
bb = 10;
alert(bb);
}
aa(bb);
alert (bb);
输出的结果分别就是 10 和 5;
复制代码
刚开始其实我考虑了一下,函数执行输出结果在函数内部改变,所以输出为10 肯定没问题,其实很多人在这个地方考虑的是函数内部的值改变,会不会将外部的值也改变,参考一下函数变量的作用域,你就会发现其实并没有改变!所以alert 的还是 bb = 5;
3. 记忆中还考了一个obj 中的 this 指向问题,也很经典其实!简单写一个类似的吧:
var name = 'john';
var obj = {
name: 'Bob',
prop:{
name: 'joyjoy',
getName:function() {
return this.name;
}
}
}
console.log(obj.prop.getName()); // joyjoy
var test = obj.prop.getName;
console.log(test()); // john;
复制代码
其实,对this 指向去研究过的童鞋,应该很好理解这个问题,第一次去直接访问obj.prop.getName,那么作用域很显然就是在obj.prop 这个对象中,那么肯定会直接访问这个对象中的name,所以打印的是joyjoy; 当实例化一个新的function 的时候,
二轮的时候,我一脸懵逼的去了,他们的CTO吧应该是,写了一堆原生的问题,以下面两个经常遇见的举例:
一. 数组去重:
1.首先最简单的写法:
Array.from(new Set());
但是这种方法一般面试官都不会买账的,然后我想了另外一种方法去实现,如下:
复制代码
2. 新建一个对象,去判断:
例如:
let arr = [1,2,3,4,'4',5,5,6];
let obj = {};
let newArr = [];
for(let i = 0;i<arr.length;i++) {
if(!obj[arr[i]]) {
newArr.push(arr[i]);
obj[arr[i]] = true;
}
}
// 输出的newArr 为 [1,2,3,4,5,6]
复制代码
通过键值对的形式,也可以实现这个问题,当时面试官质疑了我这种方法,说万一里面number类型,字符串类型都存在,会被默认去重,就像上述数组中的 4 和 '4' 一样;回来想想确实是的,再仔细想了想,还是不知道咋解决,于是便再重新想了一个方法: (ps: 下面这个想法,高度紧张,卡住了,因为他依旧没有解决这个问题啊);
3.双重for 循环去实现:
var arr=[1,2,3,4,4,4,5,5,5,5,5,6];
var resultArr = [];
for(var i = 0;i< arr.length;i++) {
for(var j = i+1;j<arr.length;j++) {
if(arr[i] == arr[j]) {
j = ++i;
}
}
resultArr.push(arr[i]);
}
// 输出 [1,2,3,4,5,6]
复制代码
这个方法内部判断条件,其实可以换成splice 在 原数组上直接修改的,具体的就不介绍啦;
想了很多,好像就只有 new Set() 可以区分上面的数值和字符串问题,但我又不了解new Set() 的具体机制和我的第二种方法有啥区别,就很尴尬了;
二: 数组的扁平化处理
刚开始说这个的时候,其实我不懂啥意思,然后试探性的问了一下,是不是就是降维,可能脑海深处突然蹦出来这个思维逻辑吧,但并没有啥卵用,我这个大菜鸟大概都忘了这些东西了,哈哈。。。然后随便写了几行代码,一团糟呢,现在想起来。下面给几个思路:
1. 回来想到最简单的方法,用arr.join() 方法去实现这玩意儿;
var arr = [1,2,3,4,[5,6],[7,8,]];
var newArr = Array.from(arr.join());
for (var i = 0;i<newArr.length;i++) {
if(isNaN(newArr[i])){
newArr.splice(i,1);
}
}
emmm....这就是我想到的最简单的办法啦。。。嘿嘿!
复制代码
2. 其实可能面试官更想要的就是网上的那循规蹈矩的三种方法吧: 简单提一下吧
第一种: 双重 for 循环:
var result = [];
for(var i=0;i<arr.length;i++){
for(var j=0;j<arr[i].length;j++){
result.push(arr[i][j]);
}
}
第二种: 用concat代替内层循环
var result = [];
for(var i=0;i<arr.length;i++){
result = result.concat(arr[i]);
}
第三种:用apply代替外层遍历
var result = [].concat.apply([],arr);
复制代码
当然了,上面这三个方法就初步实现了简单的二维数组降维,多重数组降维,还需要通过递归的方法去实现的:
一个简单的实现思路:
var arr = [2, 3, [2, 2],
[3, 'f', ['w', 3]], { "name": 'Tom' }
];
let result = [];
arr.forEach(function(val, index) {
if(Array.isArray(val)) {
val.forEach(arguments.callee);
} else {
result.push(val);
}
})
console.log(result); // 这个就解决了多重数组得降维了....
ps: 稍微解释一下 arguments.callee, 首先显而易见得就是 callee 就是arguments 中得一个属性,他是一个指针,
指向拥有arguments 这个对象得函数,常用于递归函数,不过现在一般不使用啦!!!!
复制代码
在携程得这段面试,除了这些,还有一些较为原理得原型链啊,闭包啥的,比较底层得知识,显而易见得就是凉凉了啊.... 感觉携程常年招人,也不缺人呢!
再说一些别的地方遇到得面试题吧,首先数组和字符串得一大堆用法,就不一一细说了,最想说得就是 sort 排序得功能了:
一: sort 排序:
1. 首先,sort() 方法会对数组元素进行排序,并返回这个数组;但是sort() 在默认不传比较函数的时候,会将数组中得所有元素通过toString() 方法 转化,在进行字符串比较,所以在默认对数字数组进行排序的时候,会出现问题,所以引入了比较函数:
var arr = [22,1,12,45,36];
arr.sort(function(a, b){
return a - b;
})
// 默认为升序;相反得如果return b - a; 就是相反得降序;
复制代码
2.数组得多条件排序,实例如下:
var array = [{id:10,age:2},{id:5,age:4},{id:6,age:10},{id:9,age:6},{id:2,age:8},{id:10,age:9}]; array.sort(function(a,b){
if(a.id === b.id){
return b.age - a.age // 如果id的值相等,按照age的值降序
}else{
return a.id - b.id // 如果id的值不相等,按照id的值升序
}
})
输出结果:
[{"id":2,"age":8},{"id":5,"age":4},{"id":6,"age":10},{"id":9,"age":6},{"id":10,"age":9},{"id":10,"age":2}]
复制代码
3. sort 实现乱序(这个方法不太好,就随口提一下):
Array.sort(function(){
return 0.5 - Math.random();
})
复制代码
二: 在简单的说一下 Promise 操作吧,反正去哪都会被问到.....
promise这个 构造函数 简单的 resolve 、 reject 以及 .all 和 .catch 这几个方法就不详细说了额,写一个简单得demo 去实现图片得资源加载吧...
ps: 首先我们都知道 .all 和 .race 的区别就 .all 就是判断哪一个异步函数执行得慢,就以哪一个函数为准去执行回调, 而.race 恰恰相反, 谁执行得快,就以哪一个函数为准执行回调!所以我们就可以写两个函数分别去实现加载图片和显示延时,具体代码如下:
//请求某个图片资源
function requestImg(){
var p = new Promise(function(resolve, reject){
var img = new Image();
img.onload = function(){
resolve(img);
}
img.src = 'xxxxxx';
});
return p;
}
//延时函数,用于给请求计时
function timeout(){
var p = new Promise(function(resolve, reject){
setTimeout(function(){
reject('图片请求超时');
}, 5000);
});
return p;
}
Promise
.race([requestImg(), timeout()])
.then(function(results){
console.log(results);
})
.catch(function(reason){
console.log(reason);
});
复制代码