一、面试基础题
- 作用域和值类型引用类型的传递,代码如下所示:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<script type="text/javascript">
//第1题 作用域
var num1 = 55;
var num2 = 66;//100
function f1(num, num1) {
// var num =55;
// var num1 = 66;
num = 100;//100
num1 = 100;//100
num2 = 100;//100
console.log(num);//100
console.log(num1);//100
console.log(num2);//100
}
// 55 66
f1(num1, num2);
console.log(num1);//55
console.log(num2);//100
console.log(num);// 报错
//第2题 值类型和引用类型的传递
function Person(name, age, salary) {
this.name = name;
this.age = age;
this.salary = salary;
}
function f1(person) {
//var person = p;
person.name = "ls";
person = new Person("aa", 18, 10);
}
var p = new Person("zs", 18, 1000);
console.log(p.name);//zs
f1(p);
console.log(p.name);//ls
</script>
</body>
</html>
- 封装函数进行字符串驼峰命名的转换,代码如下所示:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<script type="text/javascript">
//已知有字符串foo='get-element-by-id',写一个function将其转化成驼峰表示法”getElementById”
//自定义函数
function toString(foo) {
// var foo = 'get-element-by-id';
//根据某个字符进行切割
var arr = foo.split('-');
//获取每个元素中的第一个字符并转换成大写
console.log(arr[1].charAt(0).toUpperCase() + arr[1].substr(1, arr[1].length - 1));
for(var i = 1; i < arr.length; i++) {
arr[i] = arr[i].charAt(0).toUpperCase() + arr[i].substr(1, arr[i].length - 1)
}
//根据某个字符将数组转成字符串
return arr.join('');
}
console.log(toString('get-element-by-id'));
</script>
</body>
</html>
- 冒泡排序的实现,代码如下所示:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<script type="text/javascript">
var arr = [32,4,67,82,21,11];
///轮数
for(var i = 0; i<arr.length-1;i++){
//次数
for(var j = 0;j<arr.length-1-i;j++){
//判断前一个大于后一个数时进行交换
if(arr[j]>arr[j+1]){
//借助第三方变量交换两个变量的值
var temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
console.log(arr);
</script>
</body>
</html>
- 反转数组,代码如下所示:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<script type="text/javascript">
var arr = [1,2,3,4,5,6,7,8];
for(var i = 0;i<arr.length/2;i++){
//arr[0] arr[arr.length-1-0];
//arr[1] arr[arr.length-1-1];
//arr[2] arr[arr.length-1-2];
//arr[3] arr[arr.length-1-3];
//借助第三方变量交换两个变量的值
var temp = arr[i];
arr[i] = arr[arr.length-1-i];
arr[arr.length-1-i] = temp;
}
console.log(arr);
</script>
</body>
</html>
- 去掉数组中重复性的数据,代码如下所示:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<script type="text/javascript">
/*
* 1.创建一个新数组,把原数组中的第一个元素插入到新数组中
* 2.遍历原数组中的每一个元素分别和新数组中的每一个元素进行比较
*/
//原数组
var arr = [8,11,20,5,20,8,0,2,4,0,8];
// 新数组
var t = [];//var t = [8,11];
t[0] = arr[0];
//arr中的每个元素
for(var i=0;i<arr.length;i++){
//t中的每个元素
for(var k = 0;k<t.length;k++){
//当原数组中的值和新数组中的值相同的时候,就没有必要再继续比较了,跳出内循环
if(t[k] == arr[i]){
break;
}
//拿原数组中的某个元素比较到新数组中的最后一个元素还没有重复
if(k == t.length-1){
//将数据插入新数组
t.push(arr[i]);
}
}
}
console.log(t);
</script>
</body>
</html>
1
物理像素,有两种方式,如下所示:
-
第一种方式,代码如下所示:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no" /> <title></title> <style type="text/css"> *{ margin: 0; padding: 0; } #box{ width: 8rem; height: 8rem; border: 1px solid #000000; } </style> </head> <body> <div id="box"></div> </body> <script type="text/javascript"> var box = document.getElementById('box'); //获取设备像素比 var dpr = window.devicePixelRatio; //比例 var scale = 1/dpr; //获取屏幕宽度 375 var width = document.documentElement.clientWidth; //获取meta标签 var metaNode = document.querySelector('meta[name="viewport"]') metaNode.setAttribute('content','width=device-width,initial-scale='+ scale +',user-scalable=no') //元素比例乘回来 var htmlNode = document.querySelector('html'); htmlNode.style.fontSize = width/16*dpr + 'px'; </script> </html>
-
第二种方式,代码如下所示:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no" />
<title></title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}
#box{
width: 200px;
height: 200px;
position: relative;
}
#box:after{
content: '';
position: absolute;
left: 0;
bottom: 0;
width: 100%;
height: 1px;
background: #000000;
}
@media screen and ( -webkit-min-device-pixel-ratio:2 ) {
#box:after{
transform: scaleY(0.5);
}
}
@media screen and ( -webkit-min-device-pixel-ratio:3 ) {
#box:after{
transform: scaleY(0.333333333333);
}
}
</style>
</head>
<body>
<div id="box"></div>
</body>
</html>
flex
元素水平垂直居中,如下所示:
- 新版本的垂直居中,代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style type="text/css">
* {
margin: 0;
padding: 0;
}
#wrap {
width: 500px;
height: 500px;
background: grey;
display: flex;
justify-content: center;
align-items: center;
}
#box{
width: 200px;
height: 200px;
background: deeppink;
}
</style>
</head>
<body>
<div id="wrap">
<div id="box"></div>
</div>
</body>
<script type="text/javascript">
window.onload = function () {
var box = document.getElementById('box');
}
</script>
</html>
-
老版本的垂直居中,代码如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style type="text/css"> * { margin: 0; padding: 0; } #wrap { width: 500px; height: 500px; background: grey; display: -webkit-box; -webkit-box-pack: center; -webkit-box-align: center; } #box{ width: 200px; height: 200px; background: deeppink; } </style> </head> <body> <div id="wrap"> <div id="box"></div> </div> </body> <script type="text/javascript"> window.onload = function () { var box = document.getElementById('box'); } </script> </html>
css
实现三角形,代码如下所示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style type="text/css">
* {
margin: 0;
padding: 0;
}
#box {
width: 0px;
height: 0px;
border: 100px solid transparent;
border-top-color: deeppink;
border-left-color: deeppink;
/*border-right-color: deeppink;*/
/*border-bottom-color: deeppink;*/
}
</style>
</head>
<body>
<div id="box"></div>
</body>
<script type="text/javascript">
window.onload = function () {
var box = document.getElementById('box');
}
</script>
</html>
rem
适配,代码如下所示:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no" />
<title></title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}
#box{
width: 8rem;
height: 8rem;
background: deeppink;
}
</style>
</head>
<body>
<div id="box"></div>
</body>
<script type="text/javascript">
//获取屏幕宽度
var width = document.documentElement.clientWidth;
//获取html
var htmlNode = document.querySelector('html');
//设置html字体大小
htmlNode.style.fontSize = width/16 + 'px';
</script>
</html>
- 背景图片的距离,代码如下所示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style type="text/css">
* {
margin: 0;
padding: 0;
}
#box{
width: 100px;
height: 200px;
background: pink;
padding: 100px;
border: 80px solid blue;
background-image: url("img/1.png");
background-repeat: no-repeat;
background-origin: content-box;
background-position: -50px 0;
}
/*答案:130px*/
</style>
</head>
<body>
<div id="box"></div>
</body>
</html>
- 对于函数节流与函数防抖的理解,如下所示:
- 函数节流,一个函数执行一次后,只有大于设定的执行周期后才会执行第二次。有个需要频繁触发函数,出于优化性能的角度,在规定时间内,只让函数触发的第一次生效,后面不生效。具体来说,记录上一次函数触发的时间,记录当前函数触发的时间,同步时间。
- 防抖函数,一个需要频繁触发的函数,在规定时间内,只让最后一次生效,前面的不生效。具体来说,记录上一次的延时器,清除上一次的延时器,重新设置新的延时器。
- 对于跨域的理解,解决跨域的方法有哪些,如下所示:
- 同源策略,是浏览器安全策略,协议名、域名和端口号必须完全一致。
- 跨域,违背同源策略就会产生跨域
- 解决跨域,
jsonp
、cors
服务器代理 - 对于跨域的具体实现,创建
script
标签,设置回调函数,数据请求回来会被触发的函数,设置script
的src
属性,设置请求地址,让script
生效
- 对于
node.js
的事件轮询机制的理解,可以借助libuv
库实现的,概括事件轮询机制,分为六个阶段,如下所示:
timers
定时器阶段,计时和执行到点的定时器回调函数pending callbacks
,某些操作系统的回调函数,比如TCP
错误类型idle,prepare
,准备工作poll
轮询阶段,轮询队列,如果轮询队列不为空,依次同步取出轮询队列中第一个回调函数执行,直到轮询队列为空或者达到系统最大的限制。如果轮询队列为空,如果之前设置过setImmediate
函数,直接进入下一个check
阶段。如果之前没有设置过setImmediate
函数,在当前poll
阶段等待,直到轮询队列添加回调函数,就去第一个情况执行。如果定时器到点了,也会去下一个阶段。check
查阶段,执行setImmediate
设置的回调函数close callbacks
关闭阶段,执行close
事件回调函数process.nextTick
能在任意阶段优先执行,process.nextTick() > setTimeout() > setImmediate()
- 对于从一个
url
地址到最终页面渲染完成,发生了什么,如下所示:
DNS
解析,将域名地址解析为IP
地址,如下所示:- 浏览器
DNS
缓存 - 系统
DNS
缓存 - 路由器
DNS
缓存 - 网络运营商
DNS
缓存 - 递归搜索,如
blog.baidu.com
,.com
域名下查找DNS
解析,.baidu
域名下查找DNS
解析,blog
域名下查找DNS
解析,出错了
- 浏览器
TCP
连接,TCP
三次握手,如下所示:- 第一次握手,由浏览器发起,告诉服务器我要发送请求了
- 第二次握手,由服务器发起,告诉浏览器我准备接受了,你赶紧发送吧
- 第三次握手,由浏览器发送,告诉服务器,我马上就发了,准备接受吧
- 发送请求,请求报文,
HTTP
协议的通信内容 - 接受响应,响应报文
- 渲染页面,如下所示:
- 遇见
HTML
标记,浏览器调用HTML
解析器解析完成Token
并构建DOM
树 - 遇见
style/link
标记,浏览器调用css
解析器,处理css
标记并构建CSSOM
树 - 遇见
script
标记,调用javascript
解析器,处理script
代码,绑定事件,修改DOM
树/CSSOm
树 - 将
DOM
树和CSSOM
树合并成一个渲染树 - 根据渲染树来计算布局,计算每个节点的几个信息,布局
- 将各个节点颜色绘制到屏幕上,渲染
- 值得注意的是,这几个步骤不一定按照顺序执行,如果
DOM
树或CSSOM
树被修改了,可能会执行多次布局和渲染,往往实际页面中,这些步骤都会执行多次的
- 遇见
- 断开连接,
TCP
四次挥手,如下所示:- 第一次挥手,由浏览器发起的,发送给服务器,我东西发送完了,请求报文,你准备关闭吧
- 第二次挥手,由服务器发起的,告诉浏览器,我东西接收完了,请求报文,我准备关闭了,你也准备吧
- 第三次挥手,有服务器发起,告诉浏览器,我东西发送完了,响应报文,你准备关闭吧
- 第四次挥手,由浏览器发起,告诉服务器,我东西接收完了,我准备关闭了,响应报文,你也准备吧
- 对于闭包的理解,如下所示:
- 闭包,密闭的容器,类似于
set
,map
容器,存储数据的。闭包是一个对象,存放数据的格式:key: value
- 闭包的形成的条件,函数嵌套,内部函数引用外部函数的局部变量
- 闭包的优点,延长外部函数局部变量的生命周期
- 闭包的缺点,容易造成内存泄漏
- 注意的是,合理的使用闭包,用完闭包要及时清除(销毁)
- 闭包的实例代码,如下所示:
// 闭包的应用场景 // function fun() { // var count = 1; // return function () { // count++; // console.log(count); // } // } // // var fun2 = fun(); // fun2(); // 2 // fun2(); // 3 // /* 说说它们的输出情况 */ function fun(n, o) { // var n = 1, o; console.log(o) return { fun: function (m) { // var m = 1; return fun(m, n) } } } var a = fun(0) a.fun(1) a.fun(2) a.fun(3) //undefined,0,0,0 var b = fun(0).fun(1).fun(2).fun(3).fun(50).fun(22) //undefined,0,1,2,3,50 var c = fun(0).fun(1) c.fun(2) c.fun(3) //undefined,0,1,1
- 对于变量提升与执行上下文的理解,如下所示:
- 变量提升,
JS
引擎在代码正式执行之前会做一个预处理的工作,收集变量和收集函数。依据是var
将var
后边的变量定义但是不赋值var username = undefined; function() {}
提前定义该函数 - 执行上下文,
execute context EC
,代码执行的环境,代码正式执行之前会进入到执行环境,对于它的工作,如下所示:- 创建变量对象,变量,函数及函数的对象,全局是
window
,局部是抽象的但是确实存在 - 确认
this
的指向,全局this
指向window
,局部this
指向调用其的对象 - 创建作用域链,父级作用域链 + 当前的变量对象
- 扩展,
ECObj = { 变量对象:{变量,函数,函数的形参}, scopeChain:父级作用域链 + 当前的变量对象,this:{ window || 调用其的对象}}
- 创建变量对象,变量,函数及函数的对象,全局是
- 对于宏任务和微任务的理解,如下所示:
- 宏任务,分类:
setTimeout setInterval requrestAnimationFrame
,如下所示:- 宏任务所处的队列就是宏任务队列
- 第一个宏任务队列中只有一个任务: 执行主线程的
js
代码 - 宏任务队列可以有多个
- 当宏任务队列的中的任务全部执行完以后会查看是否有微任务队列如果有先执行微任务队列中的所有任务,如果没有就查看是否有宏任务队列
- 微任务,分类:
new Promise().then(回调) process.nextTick
,如下所示:- 微任务所处的队列就是微任务队列
- 只有一个微任务队列
- 在上一个宏任务队列执行完毕后如果有微任务队列就会执行微任务队列中的所有任务
- 宏任务和微任务的代码,如下所示:
setTimeout(() => { console.log('setTimeout'); }, 0) new Promise((resolve, reject) =>{ for (var i = 0; i < 5; i++) { console.log(i); } resolve(); // 修改promise实例对象的状态为成功的状态 }).then(() => { console.log('promise实例成功回调执行'); })
- 比较一下
React
与Vue
之间的相同点与不同点,如下所示:
- 相同点,如下所示:
- 都有组件化开发和
Virtual DOM
- 都支持
props
进行父子组件间数据通信 - 都支持数据驱动视图, 不直接操作真实
DOM
, 更新状态数据界面就自动更新 - 都支持服务器端渲染
- 都有支持
native
的方案,React
的React Native
,Vue
的Weex
- 都有组件化开发和
- 不同点,如下所示:
- 数据绑定:
vue
实现了数据的双向绑定,react
数据流动是单向的 - 组件写法不一样,
React
推荐的做法是JSX
, 也就是把HTML
和CSS
全都写进JavaScript
了,即'all in js';
Vue
推荐的做法是webpack+vue-loader
的单文件组件格式,即html,css,js
写在同一个文件 state
对象在react
应用中不可变的,需要使用setState
方法更新状态;在vue
中,state
对象不是必须的,数据由data
属性在vue
对象中管理virtual DOM
不一样,vue
会跟踪每一个组件的依赖关系,不需要重新渲染整个组件树.而对于React
而言,每当应用的状态被改变时,全部组件都会重新渲染,所以react
中会需要shouldComponentUpdate
这个生命周期函数方法来进行控制React
严格上只针对MVC
的view
层,Vue
则是MVVM
模式
- 数据绑定:
- 对于
Redux
管理状态的机制的理解,如下所示:
- 对
Redux
基本理解,如下所示:redux
是一个独立专门用于做状态管理的JS库, 不是react
插件库- 它可以用在
react
,angular
,vue
项目中, 但基本与react
配合使用 - 作用是集中式管理
react
应用中多个组件共享的状态和从后台获取的数据
- 对
Redux
使用扩展,如下所示:- 使用
react-redux
简化redux
的编码 - 使用
redux-thunk
实现redux
的异步编程 - 使用
Redux DevTools
实现chrome
中redux
的调试
- 使用
- 对于
Vue
组件间通信方式的理解,如下所示:
- 通信种类,如下所示:
- 父组件向子组件通信
- 子组件向父组件通信
- 隔代组件间通信
- 兄弟组件间通信
- 实现通信方式,如下所示:
props
- vue自定义事件
- 消息订阅与发布
vuex
slot
- 方式1:
props
,如下所示:- 通过一般属性实现父向子通信
- 通过函数属性实现子向父通信
- 缺点是隔代组件和兄弟组件间通信比较麻烦
- 方式2:
vue
自定义事件,如下所示:vue
内置实现, 可以代替函数类型的props
,绑定监听:<MyComp @eventName="callback"
,触发(分发)事件:this.$emit("eventName", data)
- 缺点是只适合于子向父通信
- 方式3: 消息订阅与发布,如下所示:
- 需要引入消息订阅与发布的实现库, 如:
pubsub-js
,订阅消息:PubSub.subscribe('msg', (msg, data)=>{})
,发布消息:PubSub.publish(‘msg’, data)
- 优点是此方式可用于任意关系组件间通信
- 需要引入消息订阅与发布的实现库, 如:
- 方式4:
vuex
,如下所示:vuex
是vue
官方提供的集中式管理vue
多组件共享状态数据的vue
插件- 优点是对组件间关系没有限制, 且相比于
pubsub
库管理更集中, 更方便
- 方式5:
slot
,如下所示:- 专门用来实现父向子传递带数据的标签,子组件和父组件
- 注意的是,通信的标签模板是在父组件中解析好后再传递给子组件的
- 对于
Vuex
管理状态的机制的理解,如下所示:
Vuex
是一个专为Vue.js
应用程序开发的状态管理的vue
插件- 作用是集中式管理
vue
多个组件共享的状态和从后台获取的数据
- 对于
Vue
的MVVM
实现原理的理解,如下所示:
Vue
作为MVVM
模式的实现库的2
种技术,模板解析和数据绑定- 模板解析,实现初始化显示,解析大括号表达式和解析指令
- 数据绑定,实现更新显示,通过数据劫持实现