面试题¶
统计信息:字数 18112 阅读37分钟
01. 作用域和值类型引用类型的传递1¶
- 变量类型:全局变量,函数内部变量
- 简单类型直接复制,复杂类型复制引用;如果改变复杂类型内容,那么其他地方也会改变
02. 作用域和值类型引用类型的传递2¶
- 函数的参数如果是引用类型,改变属性时,会改变原来的对象;如果让这个参数直接指向新的对象,那么新的对象和原来的对象就不相关了。
- 扩展:这里this指向什么(谁调用的这个方法,this就指向这个对象)?ES6中如何处理This(bind,箭头函数可以把this保留在当前类中,而不是类的实例中)
03. 封装函数进行字符串驼峰命名的转换¶
- 给定一个类名使用连字符连接,然后转换成驼峰命名输出。考点是字符串的API。演示中使用一个数组,实际上完全不必要,直接一个循环即可实现。
// code 不好的算法(0.093 ms) function foo(str) { var arr = str.split('-'); for (let i = 1; i < arr.length; i++) { arr[i] = arr[i].charAt(0).toUpperCase() + arr[i].substr(1, arr[i].length - 1); } return arr.join(''); } // code 好的算法(0.062 ms) function foo(str) { while (str.indexOf('-') > -1) { let index = str.indexOf('-'); str = str.substr(0, index) + str.charAt(index + 1).toUpperCase() + str.substr(index + 2, str.length + 1); } return str; } console.time(123); foo('hello-world-miki-mikohjk-fghdfty-rtyfghjk'); console.timeEnd(123);
04. 冒泡排序¶
冒泡排序法;快速排序法-这两个应该熟练背写
// code 冒泡排序法
function bubbleSort(arr) {
for (let i = 0; i < arr.length; i++) {
for (let j = 0; j < arr.length - i; j++) {
if (arr[j] > arr[j + 1]) {
let temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
return arr;
}
// code 快速排序法
function quickSort(arr) {
if (arr.length < 2) {
return arr;
}
let init = arr[0];
let left = [];
let right = [];
for (let i = 1; i < arr.length; i++) {
if (arr[i] < init) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
let res = [];
left = quickSort(left);
right = quickSort(right);
res.push(...left, init, ...right);
return res;
}
var a = [2,1,3,4,5,2,3,4,5,5,2,4,1,2,3,4,1,0,6,8,9];
var b = quickSort(a);
console.log(b);
05. 反转数组¶
使用数组的原生API实现,或者循环一轮可以实现;
// code 反转数组
var arr = [1,2,3,4,5];
function reverseArr(arr) {
for (let i = 0; i < arr.length / 2; i++) {
var temp = arr[i];
arr[i] = arr[arr.length - i - 1];
arr[arr.length - i - 1] = temp;
}
return arr;
}
// code 好的算法
function reverse(arr) {
arr.reverse();
}
06. 去掉数组中重复性的数据¶
可以使用ES6,或者使用对象存储; 这里给出的答案是双重循环,性能不好
function deleteSame (arr) {
return Array.from(new Set(arr));
}
07. 实现1物理像素¶
这个考察移动端CSS的实现 可以全局更改font-size,需要把图标,文字大小使用 px 配置,把DIV的长度和宽度,使用 rem 配置。这样,图标或者文字可以实现1物理像素。
<meta name="viewport" content="width=device-width, initial-scale=1, user-scaleable=no"/>
// code 全局更改font-size
window.onload = function() {
var dpr = window.devicePixelRatio;
var scale = 1 / dpr;
var width = document.documentElement.clientWidth;
var metaNode = document.querySelector('meta[name="viewport"]');
mataNode.setAttribute('content', 'width=device-width, initial-scale=' + scale + ', user-scaleable=no');
var htmlNode = document.querySelector('html');
htmlNode.style.fontSize = width * dpr + 'px';
}
// 这样在CSS中,1px 对应的就是真实的物理像素,1rem 已经转换成 16 * dpr 尺寸。对于iPhone678,这是2。对于plus 这是3。
/* code 单独设置某一条线段的宽度 */
#box {
width: 200px;
height: 200px;
positon: 'relative';
}
#box:before {
content: '';
position: 'absolute';
left: 0;
bottom: 0;
width: 100%;
height: 1px;
background: #000;
}
@media screen and (-webkit-min-device-pixel-ratio: 2) {
#box:before {
transform: scaleY(0.5);
}
}
@media screen and (-webkit-min-device-pixel-ratio: 3) {
#box:before {
transform: scaleY(0.333);
}
}
08. flex元素水平垂直居中¶
传统方法:使用position实现水平垂直居中;
方法一
.father {
position: relative;
}
.son {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
}
.father {
position: relative;
}
.son {
width: 100px;
height: 100px;
position: absolute;
top: 50%;
left: 50%;
margin-left: -50px;
margin-top: -50px;
}
.father {
position: relative;
}
.son {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
flex 方法也可以实现
.father {
display: flex;
align-items: center;
justify-content: center;
}
.son {
}
09. css实现三角形¶
原理:一个DIV,宽度是0,边框宽度很大,设置四个边不同的颜色,那么就是四个三角形。如果使用一个三角形,那么设置另外三个边是透明色即可。
#box {
width: 0;
height: 0;
border: 100px solid;
border-top-color: red;
border-bottom-color: transparent;
border-left-color: transparent;
border-right-color: transparent;
}
10. rem适配¶
设置 meta 标签,设置初始值是 initial = 1。然后通过 JS 获取这个节点,获取屏幕的宽度,然后设置当前根节点的缩放比是 1 / 屏幕的宽度。那么设置某个元素的宽度是 1rem 就是屏幕的宽度
<meta name="viewport" content="width=device-width, initial-scale=1, user-scaleable=no"/>
// code 全局更改font-size
window.onload = function() {
var dpr = window.devicePixelRatio;
var scale = 1 / dpr;
var width = document.documentElement.clientWidth;
var metaNode = document.querySelector('meta[name="viewport"]');
mataNode.setAttribute('content', 'width=device-width, initial-scale=' + scale + ', user-scaleable=no');
var htmlNode = document.querySelector('html');
htmlNode.style.fontSize = width * dpr + 'px';
}
11. 背景图片距离¶
问题:给定一个CSS,计算背景图片距离左侧边框的位置。
默认的背景图片距离左侧边框是0,padding 不考虑。下面情况特殊。
.box {
width: 100px;
height: 200px;
padding: 100px;
border: 80px solid black;
background-image: url('...png');
background-repeat: no-repeat;
background-origin: content-box; /* 这里设置了起始点是内容部分 */
background-position: -50px 0;
}
结果是:边框 + 左侧 padding - 偏移量 = 80 + 100 - 50 = 130px
12. js综合面试题¶
变量提升,函数执行顺序,异步过程等
function Foo() {
getName = function() {
alert(1);
}
return this;
}
Foo.getName = function() {
alert(2);
}
Foo.prototype.getName = function() {
alert(3);
}
var getName = function () {
alert(4);
}
function getName() {
alert(5);
}
// 执行下面的操作
Foo.getName(); // 2
getName(); // 4 这里为什么?
Foo().getName(); // 1
getName(); // 1
new Foo.getName(); // 2 new (Foo.getName)(); ?
new Foo().getName(); // 3 (new Foo()).getName?
new new Foo().getName(); // new ((new Foo()).getName)(); //3?
new 一个构造函数或者类,先执行一次这个函数,然后返回这个类的实例(这个例子中没有保存或者使用新建的实例)
13. 函数节流和防抖¶
函数节流:函数频繁触发时,每个一定周期执行一次(执行多次)。
函数防抖:函数频繁触发时不执行,需要等最后一次触发结束后,再执行函数(执行一次)。
function throttle(fn, delay) {
var lastTime = 0; // 上次触发函数的时间
var nowTime = Data.now();
if (nowTime - lastTime > delay) {
fn();
lastTime = nowTime;
}
}
// 处理this指向问题
function throttle(fn, delay) {
var lastTime = 0; // 上次触发函数的时间
return function () {
var nowTime = Data.now();
if (nowTime - lastTime > delay) {
fn.call(this);
lastTime = nowTime;
}
}
}
function debounce(fn, delay) {
var timer = null;
return function () {
clearTimeout(timer);
timer = setTimeout(() => {
fn();
}, delay);
}
}
14. 跨域¶
同源策略:浏览器发出请求,需要同源(协议、域名、端口号相同),如果违反同源协议会产生跨域。
解决方法:jsonp cors 服务器代理 (JSONP需要前后端协同,其他都是后端的方法)
var script = document.createElement('script');
function getData(data) {
console.log(data);
}
script.src = 'http://localhost:3000?callback=getData';
document.body.appendChild(script);
15. nodejs事件轮询机制¶
setTimeout setInterval process.nextTick 三个方法
执行次序是:process.nextTick setTimeout setImmediate
nodeJS 的事件轮训机制是 libuv 实现的(C)分成几个阶段(了解)
- timers 定时器阶段:计时并执行到点的定时器回调函数
- pending callbacks TCP操作错误回调函数
- idle prepare 准备工作
- poll 轮询阶段(轮询队列)如果队列不是空,那么去除第一个回调函数执行,直到执行完毕;如果设置过setImmediate 函数,那么进入下一个check阶段。
- Check 检查阶段:执行 setImmediate 设置的回调函数
- close callbacks 关闭阶段:执行close事件的回调函数
16. 从url输入网址¶
这个题目考察:网络知识+界面解析过程,下面是具体过程
-
域名解析(DNS解析):把域名解析成IP:这个依次从缓存中获取IP;如果一直递归不到,那么就会报错。
-
TCP三次握手
- 1、浏览器告诉服务器:我将要发送请求了,你是否方便
- 2、服务器告诉浏览器:我准备好了
- 3、浏览器告诉服务器:我准备发送了,你接受吧
-
发出请求:请求报文(HTTP)
-
接收响应:响应报文
-
渲染界面
- HTML下载完毕,使用HTML解析器构建DOM树
- style/link 标记,下载解析样式表,创建 CSSOM树
- 遇到 script 标签,使用JS计息期,处理JS代码,更改DOM或者CSSOM树
- 将DOM树和CSSOM树合成 render tree
- 计算页面布局,计算节点的位置
- 计算节点的颜色,然后渲染到屏幕上(回流,重绘)可能后几个步骤多次执行
-
TCP四次挥手
- 浏览器发给服务器:我请求报文发完了,你准备关闭
- 服务器发给浏览器:请求报文接受完了,我准备关闭
- 浏览器发给服务器:响应报文法文了,你准备关闭
- 服务器发给浏览器:响应报文接受晚了,你准备关闭
17. 闭包¶
闭包:闭包是一个密闭的容器,用来存储临时数据——closure
闭包形成的条件:函数嵌套;内部函数引用外部函数的局部变量。
function fn() {
var count = 1;
function fnInner() {
console.log(count);
}
}
// 这是一个简单的闭包(没有实际作用的闭包)
闭包的好处:外部函数返回内部函数。多次执行内部函数时,存放在外部函数中的局部变量不会被清除,可以临时存储数据。(延长外部函数局部变量的生命周期)
如果滥用闭包,可能造成内存泄漏。
function fun() {
var count = 1;
return () => {
console.log(count);
}
}
var fn = fun();
fn(); // 执行内部函数时。用到外部函数的局部变量
fn();
闭包的引用场景
function fun() {
var count = 1;
return function() {
count++;
console.log(count);
}
}
var fun2 = fun();
fun2();
fun2();
// 2 3
下面是复杂的案例
function fun(n, o) {
console.log(o); // 打印的是第二个参数
return {
fun: function(m) {
return fun(m, n);
// 第一个参数传入,第二个参数是外部参数的第一个参数
}
}
}
var a = fun(0); // 第二个参数没有传,打印 undefined
a.fun(1); // 执行内部函数 fun(1, 0) 打印结果是0
a.fun(2); // 执行内部函数 fun(2, 0) 打印结果是0
a.fun(3); // 执行内部函数 fun(3, 0) 打印结果是0
var b = fun(0).fun(1).fun(2).fun(3);
// undefined, 0, 1, 2
// 第一个函数执行是 undefined 和第一个一样
// 第二个函数,返回 function(1) { fun(1, 0) } 然后打印0
// 第三个函数,返回 function(2) { fun(2, 1) } 然后打印1
var c = fun(0).fun(1); //undefined/0
c.fun(2); // 1
c.fun(3); // 1
18. 变量提升 && 执行上下文¶
变量定义和函数定义,变量的声明会提升到顶部,变量的赋值不会提升。
函数会全部提升到顶部
console.log(a); // undefined
var a = 10;
console.log(a); // 10
fn();
function fn() {
console.log(1);
}
执行上下文(EC)execute context:代码执行前,根据上下文进行变量提升。确认 this 的指向;创建作用域链(全局作用域、函数作用域)
19. 宏任务和微任务-NodeJS¶
执行完一轮宏任务,看看是否有微任务;如果有微任务就执行。然后开始下一次轮训,执行下一个宏任务。
宏任务:setTimeout setInterval requestAnimationFrame:宏任务队列可以有多个,第一个宏任务队列只有一个任务-执行主线程的JS代码
微任务:new Promise().then(callback) process.nextTick:只有一个微任务队列。在上一个宏任务全部执行完毕,如果有微任务,就会立刻执行微任务中的全部队列。
所有,执行的流程是:第一个宏任务队列(主线程)+微任务队列(如果有就执行)+第二个宏任务队列+第三个宏任务队列。。。
console.log(1);
setTimeout(() => {
console.log(2);
}, 0);
setTimeout(() => {
console.log(3);
},100);
new Promise((resolve, reject) => {
console.log(4);
reject(); // 这个执行后,下面的resolve不会执行。
resolve();
}).then(() => {
console.log(5);
}).catch(() => {
console.log(6);
});
console.log(7);
// 147(第一个宏任务队列) 6(微任务队列)2(第二个宏任务队列)3(第三个宏任务队列)
20. 小程序快速入门¶
-
小程序:打包后代码不超过2M;主要依据微信官方开发者文档;开发工具也是微信开发工具(APPID)
-
开发者工具:模拟器+调试器+编辑器。
- project.config.json 设置项目的调试目录。
- app.js app.json app.wxss 这里是根组件的部分
- pages 是不同子页面;页面中结构+样式+行为一一对应。这里使用微信自定义的格式(View button text image block)。
JS 中处理事件
<view class="container"> <view class="userinfo"> <text class="user-metto">{{motto}}</text> </view> </view>
const app = getApp();
Page({
data: {
motto: 'Hello World',
userInfo: {},
hasUserInfo: false,
canIUse: wx.canIUse('button.open-type.getUserInfo')
},
bindViewTap: function() {
wx.navigateTo({
url: '../logs/logs'
})
},
})
具体的配置在微信官方文档中有
data 可以直接在WXML中通过模板获取到的数据
微信小程序已经做了完美适配,不需要考虑移动端适配,单位是 vpx
21. 小程序和mpVue对比简介¶
小程序有专门的框架,wepy,mpvue (mini program Vue)
这个语法和 VUE 基本一致(需要的时候再学习)
22. 比较React与Vues¶
相同点: - 组件化;使用 props 进行父子组件数据传递 - 虚拟DOM,不需要直接操作DOM - 数据驱动更新 - 服务器端渲染;移动端原生渲染(React Native)
不同点: - Vue 双向绑定;React 单向绑定 - 组件写法:React 是JSX,VUE 是单文件形式,HTML+CSS+JS在一个模板文件中 - state:React需要 setState 更新状态,VUE使用data属性管理 - 虚拟DOM:VUE根据依赖关系,不需要重新渲染全部组件;React中,状态改变会渲染全部的组件,可以通过 pureComponent or shouldComponentUpdate 控制组件的更新
23. Redux管理状态的机制¶
-
Redux:是一个专门做状态管理的JS库,不是React的插件,可以集中管理React中多个组件共享的状态和从后台获取的数据。
-
工作机制:Redux 分成三部分:store(包含state,存储数据,与react组件进行数据交流)、reducers(产生数据,更新store中的state)、action creator(事件驱动状态更新)。
-
主要方法:getState 获取state;dispatch(action)派发事件;store.subscribe(listener) 订阅事件。
-
扩展:react-redux,redux-thunk,Redux DevTools 等工具
24. 说说Vue组件间通信方式¶
通信对象:父子通信,隔代通信、兄弟通信
方式:props,自定义事件,消息的订阅发布,Vuex,slot 传递子元素。通常使用 Props 传递(可以传递一般属性或者回调函数),隔代传递或者兄弟传递不适合。自定义事件:订阅事件、触发事件;vuex 是VUE状态管理库,可以处理复杂的数据类型。
25. Vuex管理状态的机制¶
Vuex 是 VUE 的状态管理的插件;可以集中管理多个 VUE 多个组件共享的状态,以及从服务器获取的数据。
- state 负责存储状态($store.state mapState() getters)
- actions 负责处理事件($store.dispatch()/mapActions()),和后端交互数据(commit)
- mutations 在数据更新后,直接更新 state
26. 说说Vue的MVVM实现原理¶
模板解析(解析大括号表达式——解析指令)
数据绑定(实现更新显示)
observer 观察监视data中所有层级的属性,compile 编译模板;初始化界面,watcher更新界面。
- 注意:后面几部分是VUE或者小程序,实际不使用,所以需要的时候再说。