刷面试题之<<携程 地面业务 前端面试经历>>
在简书看到这篇面试题,结合作者的答案和个人的理解做了一下,因个人水平有限(不谦虚,确实很菜),如果个人做的有什么不对的欢迎指出来,共同交流
作者:诗和元芳
链接:https://www.jianshu.com/p/e6e...
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
笔试
题目一
<div id="d"> <div id = "a"></div> <div id = "b"></div> <div id = "c"></div> </div> <script> var a = [document.getElementById('a'),document.getElementById('b'),document.getElementById('c')]; var b = [document.getElementById('d').getElementsByTagName('div')]; </script>
问a 和 b 的区别
我们把结果打印出来看一下:
a
b
看MDN官方对Element.getElementsByTagName()
这个说法的解释:
“Element.getElementsByTagName()
方法返回一个动态的包含所有指定标签名的元素的HTML集合HTMLCollection
。指定的元素的子树会被搜索,不包括元素自己。返回的列表是动态的,这意味着它会随着DOM树的变化自动更新自身。所以,使用相同元素和相同参数时,没有必要多次的调用Element.getElementsByTagName()
.”
很多人(包括我和作者)都会以为这题差别是在b返回的是二维数组,其实都是想当然了。
a返回的是一个包含三个dom元素的数组,而b返回的是只有一个HTMLCollection
元素的数组。这是完全的两种结构且HTMLCollection
并不继承自Array
.
- 1、
HTMLCollection
是及时更新的,当文档中的DOM变化时,它是会随之变化的 - 2、
HTMLCollection
可以用HTMLCollection.item()
、HTMLCollection.namedItem()
这种方式来获取内部元素。
题目二 数组去重
请手打一个数组去重的方法,题目是[1,2,3,2].distinct() = [1,2,3]。
关于数组去重的方法有很多,网上资料太多不再赘述,作者用的是一种hash去重的方法。
Array.prototype.distinct = Array.prototype.distinct || function(){ var len = this.length, i = 0, hash = {}, myArr = []; for(; i < len; i ++){ if(!hash[this[i]]){ hash[this[i]] = true; myArr.push(this[i]) } } return myArr; }
这种方法可以达到题目中的要求,但并不是一种严谨的数组去重方式。
假设我们的的数组变成[1,2,3,2,'1']
,这种方法就无法达到去重的效果了,这种方式无法区分数字1和字符串'1',还需要通过类型判断。
关于数组去重,最简单的是ES6的:
Array.prototype.distinct = Array.prototype.distinct||function(){ console.log(this); return Array.from(new Set(this)); //return [...(new Set(this))]; }
题目三,考察闭包
function mo(){ var x = 0; return function(){ console.log(++x) } } var a = mo(); var b = mo(); a(); a(); b();
答案是1,2,1。
a 和 b是不同的两个函数,分别保持着各自对不同的x的引用。
题目四
var p = []; var A = new Function(); A.prototype = p; var a = new A; a.push(1); console.log(a.length); console.log(p.length);
答案:1,0。
这题要理解的一点是:
a.__proto__ === p
按照<<高级程序设计>>
中讲解的new的过程:
- 创建一个新对象
- 将构造函数的作用域赋给新对象(因此this就指向了这个新对象)
- 执行构造函数中的代码(为这个对象添加属性)
- 返回新对象
我们模仿一下new这个过程发生的事:new A
=>
(funciton(){ //第一步,把__proto__指向p(A.prototype) //第二步,按照p(A.prototype)的样子创造一个对象(克隆) var a = Object.create(A.protype); A.call(a); return a; })()
new 如果在继承对象是没有参数的情况下,是可以不加后面的括号的,编译器会自动替你加上的。但是要记住new的没有括号优先级是低于.的,有括号是高于();
题目五,数组降维
[1,2,3,[4,5,[6,7]]]
下面是作者提供的两种写法:
function fn(arr){ var tempArr = Array.from(arr.join('-')); //join()会自动添加逗号 for(var i = 0;i<tempArr.length;i++){ if(isNaN(tempArr[i])){ tempArr.splice(i,1) } } return tempArr; }
function fn(arr){ var myArr = []; var fn2 = function(arr2){ for(var i = 0;i<arr2.length;i++){ if(Array.isArray(arr2[i])){ fn2.call(null,arr2[i]) } else{ myArr.push(arr2[i]); } } } fn2(arr); return myArr; } fn([1,2,3,[4,5,[6,7]]]);
这里再提供一种简洁的写法:
let flatten = arr => arr.reduce((pre,cur)=>{ Array.isArray(cur)?pre.push(...flatten(cur)):pre.push(cur); return pre },[])
面试
函数声明和函数表达式有什么区别
答:解析器有一个函数声明提升的过程,读取并将函数声明添加到执行环境中,Javascript在第一遍会声明函数并将它们放到源代码的顶部。使其在执行任何代码之前可用。
函数声明既然会提升,优先级是什么,和var比呢
答:函数提升优先级比变量提升要高,且不会被变量声明覆盖,但是会被变量赋值覆盖,也会被后面的同名函数替换。
console.log(typeof(foo)); //function function foo(){} var foo = 5;
foo = 5; console.log(typeof(foo)); //number function foo(){}
函数声明的提升为什么会比var高,编译器干了什么事情
引擎在执行javascript
代码之前,会先将其进行编译,编译阶段会找到所有声明,并找到合适的作用域将它们关联起来。
Javascript在第一遍会声明函数并将它们放到源代码的顶部
函数声明提升的是整个函数,变量声明提升的仅仅是var a
;这部分。
函数声明会将变量声明覆盖,而变量声明只能当做重复声明被忽略。所以函数声明的提升比var高。(纯个人理解,如有不对欢迎指出
)
es6 装饰器用过没,是干什么用的(应该是es7的,反正我也没答出来,答出来肯定还继续深的问)
装饰类、装饰方法或属性。装饰器可以联想到装饰器模式:向一个现有的对象添加新的功能,同时又不改变其结构的设计模式,它是作为现有的类的一个包装。
es6中的扩展运算符...的实现原理
调用默认的iterator接口
<-------------------以下内容明天接着刷---------------------------->
es6中的解构中的...和上面的区别
答:说真的,我也不知道啥区别,不是一样用吗?
[...org,name] = [1,2,3,4] 这样的话,org里是个啥
答:开始问的放后面是啥,我想了半天,没敢说,我说难道后面的就没抓到,全被org抓走了?其实不是,直接报错了,...是rest的意思,既然是rest,那就只能放在最后啊
更新说明我还不会用,区别应该是问的es7中的解构。原理就是es6直接采用for of,也就是说,所有总有迭代器的对象都能使用扩展运算符,在es6里说不能放前面的,但是在es7里如果用于对象是可以放前面的。懂了这个,上面几题就都该会了
for of 和for in区别
答:for in 是键值对形式,for of 是输出value形式,然后for of只要是配置了迭代器,都能遍历。
this的指向问题
答:这个正常的都说了
箭头函数中的this
答:这里说的不好,我只说了用了箭头函数的话,this就被传进来了。就不需要绑定进来了。
箭头函数中的this默认指向在定义它时,它所处的对象,而不是执行时的对象, 定义它的时候,可能环境是window
什么是闭包(这里答的不太好,虽然都会用啊处理的,我用类似如下代码举例,貌似面试官并不满意...)
function aa(){ var a = 1; return function bb(){ console.log(a); } } aa()()
什么是作用域
答:在进入脚本标签编译阶段的时候就定义了各类作用域,外面的变量就在全局作用域,函数内部的就在函数的作用域里,作用域外的函数不能访问某个作用域里面的东西
什么是作用域链
答:在各级执行上下文创建阶段的时候,就确定了各级作用域,串起来就是作用域链了。比如闭包的时候外部函数出栈之后,内部函数还保留了对外部函数某个变量的引用,就是通过作用域链找过去的
什么是原型
答:prototype,好像我也说不出啥,就举个函数的写原型的例子。
js有哪些设计模式(我说有工厂模式,构造函数模式,原型模式,面试官说这是面向对象,不过我觉得有啥区别,面向对象编程不久是要用这些设计模式吗)
这些模式都是咋写的,我手写给他看
为什么要用原型
答:共享原型里的东西给下属继承的对象,这样在new的时候不用重新创建,节省内存空间
eval的缺点
答:我说了用法,就是可执行string,然后缺点没说,没怎么研究,只是看了一眼用法类型的
with的缺点
答:没用过,在网上看到过,说不要用,然后我就不用,应该会导致性能问题吧
在严格模式下能不能用eval
答:不能
es5和es6严格模式的区别
答:不知道
什么是柯里化,举React和Redux的例子
答:解释了柯里化是啥,举不出来例子,没看过源码啊或者是对react了解的不够多
什么是纯函数
答:不产生任何交互的函数?答的不好,我说reducer应该就是个纯函数,猜对了而已。
纯函数是指 不依赖于且不改变它作用域之外的变量状态 的函数纯函数的概念链接
页面和服务器之间的交互有哪几种(ajax,websocket,不满意)
ajax的Type有几种(我只知道post和get)
这题我可能没有理解...
get和post的区别(我说的表象区别,url参数,安全性,大小限制,面试官不是很满意)
与 POST 相比,GET 更简单也更快,并且在大部分情况下都能用。
然而,在以下情况中,请使用 POST 请求:
无法使用缓存文件(更新服务器上的文件或数据库)
向服务器发送大量数据(POST 没有数据量限制)
发送包含未知字符的用户输入时,POST 比 GET 更稳定也更可靠
HTTP请求头上都有什么信息
答:UA,HOST,其他的我也没说,就说还有几个没注意..
如何统计页面上的按钮被点击了多少次
答:和服务器交互?反正也只能存服务器啊...
单页面应用和多页面应用的区别
答:一个跳页面是内部的,一个跳页面是href整页刷新的。
哪种更容易被SEO到,优化SEO
答:猜测是多页面,页面多呗...优化的不知道
上两题补充链接
cookie和localstorage区别(我说大小,安全,有效期,貌似又不是很满意)
JSON.stringify(大OBJ)的时候会有什么问题
答:性能损失。
script标签顺序怎么控制,涉及到依赖必须要有先后
答:在HTML层就控制顺序呗,显然不是他要的答案
除了直接引用script标签,如果不用import require这样的东西,怎么调用其它页面的js
答:真不知道,a.js里除了用script标签引用b.js
import和require的区别
答:import是静态加载,一旦你import,内容就进来了,require是动态加载,用的时候才加载,后面又问了一些,后面就不会了
函数节流怎么实现
答:简单是settimeout,时间超出就执行,没超出再进来的话就直接clear掉。面试官觉得太简单了。
canvas 绘制原理
答:不知道
如何使用canvas拖动一个小球撞击另外一个小球,然后另外一个小球被撞击出去
答:canvas一年半没用铁定不记得了(总共也就2年经验),但是说了一下实现思路
canvas 里有很多小球时如何优化性能
答:不知道
微信小程序跳转页面最多能跳转多少层 不会
RN中listview 的key值的实现原理 不会
然后还问了一些关于项目上的问题,以及如何设计一个搜索组件,需要考虑到哪些问题。
应该就这些,没想起来的想起来再补充,感觉答的不好,很多都不记得了。
三分之一的题目 比如实现原理啊,缺点啊,为什么啊,会有什么问题啊,此类问题要么不会,要么答的不好
总共就这么多,我要继续巩固基础了~!!!!!!!