2021前端笔试题

笔试重点:查漏补缺
JavaScript题1: https://github.com/lydiahallie/javascript-questions/blob/master/zh-CN/README-zh_CN.md
JavaScript题2: https://blog.csdn.net/liuyan19891230/article/details/102385743

(一)
1、什么是闭包,为什么要使用闭包?

  • 在一个函数里面嵌套另一个函数,被嵌套的那个函数的作用域是一个闭包。

  • 作用:创建私有变量,减少全局变量,防止变量名污染。可以操作外部作用域的变量,变量不会被浏览器回收,保存变量的值。

2、下面这段代码输出结果为?想输出 0 1 2 3 4 怎么解决?

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

答案: 输出结果为 5个5
输出 0 1 2 3 4 5 的方法如下:
方法一:

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

方法二:

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

方法三:

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

3、解析事件委托?
事件委托是指将事件绑定到目标元素的父元素上,利用冒泡机制触发该事件。

ulEl.addEventListener('click', function(e){
    var target = event.target || event.srcElement;
    if(!!target && target.nodeName.toUpperCase() === "LI"){
        console.log(target.innerHTML);
    }
}, false);

4、有如下这段HTML代码,写一段 js 实现:点击一个列表时,输出对应的索引。

<ul>
	<li>1<li>
	<li>2<li>
	<li>3<li>
	<li>4<li>
</ul>

方法一:使用立即执行函数

var liList = document.getElementsByTagName('li')
for (var i = 0; i < liList.length; i++) { 
    (function(i){  
        liList[i].onclick = function(){ 
            console.log(i)  
        } 
    })(i)  
};

方法二:使用ES6的let

var liList = document.getElementsByTagName('li')
for (let i = 0; i < liList.length; i++) {
    liList[i].onclick = function(){
        console.log(i)
    }
};

方法三:forEach实现

var liList = document.getElementsByTagName('li')
var liArr = [].slice.call(liList)//将liList这个类数组转化成数组,forEach只能遍历数组
liArr.forEach(function(ele,i){  
	ele.onclick = function(){ console.log(i)  }  
})

方法四:call方法

var liList = document.getElementsByTagName('li')
function fun(idx){ 
    this.onclick = function(){ console.log(idx) } 
} 
for (var i = 0; i < liList.length; i++) { 
    fun.call(liList[i],i) 
};

5、数组方法 pop() push() unshift() shift() 的作用

shift():删除数组的第一个元素,返回第一个元素的值。
pop() : 删除数组的最后一个元素,返回最后一个元素的值。
push():向数组的末尾添加一个或多个元素,返回新数组的长度。
unshift() :向数组的头部添加一个或多个元素,返回新数组的长度。

6、var arr = [4,0,7,9,0,0,2,6,0,3,1,0];
要求将数组中的 0 项去掉,将不为 0 的值存入一个新的数组,生成新的数组。
答案:
方法一:根据要求步步体现

//定义个数组
var arr = [4, 0, 7, 9, 0, 0, 2, 6, 0, 3, 1, 0];
//创建个空数组,接收个新生成的数组
var mn=[]
//创建函数
function fn(arr,ma) {
    //循环添加随机元素
    for(var i=0;i<arr.length;i++) {
        //判断
        if(arr[i] == ma){
            //删除
              arr.splice(i,1);
              i--;
              //空数组,接收新生成的数据
            mn=arr;
        }
      }
}
fn(arr,0);
console.log('新数组',mn);

方法二:使用 forEach

var arr = [4,0,7,9,0,0,2,6,0,3,1,0];
var a = []
arr.forEach(item=>{
    if(item != 0){
        a.push(item)
    }
})
console.log('a',a)

方法三:使用 for 循环

var arr = [4,0,7,9,0,0,2,6,0,3,1,0];
var b = []
for(var i = 0; i < arr.length; i++){
    if(arr[i] != 0){
        b.push(arr[i])
    }
}
console.log('b',b)

7、如何解决 ajax 回调地狱问题?

Promise的使用场景:ajax请求,回调函数,复杂操作判断。 Promise是ES6为了解决异步编程所诞生的。
异步操作解决方案:Promise、Generator、定时器(不知道算不算)、还有ES7的async

8、谈谈你对 ES6 的理解?

  • 新增模板字符串(为 JavaScript 提供了简单的字符串插值功能)
  • 箭头函数
  • for-of(用来遍历数据—例如数组中的值。)
  • arguments 对象可被不定参数和默认参数完美代替。
  • ES6 将 promise 对象纳入规范,提供了原生的 Promise 对象。
  • 增加了 let 和 const 命令,用来声明变量。
  • 增加了块级作用域。
  • let 命令实际上就增加了块级作用域。
  • 还有就是引入 module 模块的概念

9、常使用的库有哪些?开发过什么应用或组件?
10、用一个 div 模拟 textarea 的实现
给 div 添加 contenteditable=true 即可。
步骤:

  • 给 div 添加一个HTML全局属性:contenteditable="true",使 div 元素变成用户可编辑的;
  • 给 div 添加样式 resize: vertical;,使 div 可以被用户调整尺寸。注意:别忘了设置 overflow:auto; 样式,因为 resize 样式不适用于 overflow:visible; 的块,不然 resize 不起效;
  • 增加一个属性:placeholder=“请输入……”;
  • 通过 CSS 选择器获取并显示 placeholder 的值;

代码实现:

<div class="textarea" contenteditable="true" aria-placeholder="this is placeholder">
.textarea{
    height: 200px;
    width: 300px;
    padding: 4px;
    border: 1px solid #888888;
    resize: vertical;
    overflow: auto;
}
.textarea:empty:before {
    content: attr(aria-placeholder);
    color: #bbb;
}

10-1、CSS限制显示字数,文字长度超出部分用省略号表示
(1) 文字超出一行,省略超出部分,显示’…’

.line-limit-length {
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap; /*文本不换行,这样超出一行的部分被截取,显示...*/
}

(2) 可以给定容器宽度限制,超出部分省略。

.product-buyer-name {
	max-width: 110px;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

11、怎么让 Chrome 支持小于 12px 的文字?
谷歌 Chrome 最小字体是 12px,不管你设置成 8px 还是 10px,在浏览器中只会显示 12px

针对谷歌浏览器内核,加 webkit 前缀,用 transform:scale() 这个属性进行缩放。

<style>
.span1 span{
    font-size:10px; /* 针对可以识别12px以下字体大小的浏览器 */
    -webkit-transform:scale(0.8);
}
.span2 span{
    font-size:12px;   
}
</style>
<p class="span1">
    <span >测试10px</span>
</p>
<p class="span2">
    <span >最小12px</span>
</p>

12、CSS垂直局居中的写法,请至少写出 2 种。
(1)已知容器尺寸

<div class="parent">
    <div class="child"></div>
</div>

方法一:利用定位(常用方法,比较推荐)

<style>
    .parent{ 
        width:500px; 
        height:500px; 
        border:1px solid #000; 
        position:relative 
    } 
    .child{ 
        width:100px; 
        height:100px; border:1px solid #999;
        position: absolute; 
        top: 50%; 
        left: 50%; 
        margin-top: -50px;
        margin-left: -50px; 
    }
</style>

方法二:利用 margin:auto;

<style>
    .parent { 
        width: 500px; 
        height: 500px; 
        border: 1px solid #000; 
        position: relative;
    } 
    .child { 
        width: 100px; 
        height: 100px; 
        border: 1px solid #999; 
        position: absolute; 
        margin: auto; 
        top: 0; 
        left: 0; 
        right: 0; 
        bottom: 0; 
    }
</style>

方法三:利用 display:table-cell

<style>
    .parent { 
        width: 500px; 
        height: 500px; 
        border: 1px solid #000; 
        display: table-cell; 
        vertical-align: middle; 
        text-align: center;
    } 
    .child { 
        width: 100px; 
        height: 100px; 
        border: 1px solid #999; 
        display: inline-block;
    }
</style>

方法四:利用 display:flex;设置垂直水平都居中

<style>
    .parent { 
        width: 500px; 
        height: 500px; 
        border: 1px solid #000; 
        display: flex; 
        justify-content: center; 
        align-items: center;
    } 
    .child { 
        width: 100px; 
        height: 100px; 
        border: 1px solid #999;
    }
</style>

(2)未知容器尺寸
方法一:

div {
 	position: relative;		/* 相对定位或绝对定位均可 */
 	width:500px;
 	height:300px;
 	top: 50%;
 	left: 50%;
 	margin: -150px 0 0 -250px;     	/* 外边距为自身宽高的一半 */
 	background-color: pink;	 	/* 方便看效果 */
}

方法二:
未知容器的宽高,利用 transform 属性

div {
 	position: absolute;		/* 相对定位或绝对定位均可 */
 	width:500px;
 	height:300px;
 	top: 50%;
 	left: 50%;
 	transform: translate(-50%, -50%);
 	background-color: pink;	 	/* 方便看效果 */
}

方法三:
利用 flex 布局
实际使用时应考虑兼容性

 .container {
 	display: flex;
 	align-items: center; 		/* 垂直居中 */
 	justify-content: center;	/* 水平居中 */
 }
 .container div {
 	width: 100px;
 	height: 100px;
 	background-color: pink;		/* 方便看效果 */
 }  

13、CSS实现宽度自适应 100%,宽高 16:9 的比例的矩形。
解释:通过百分比设置宽度,根据宽度的比例数值*9/16,计算出高度的百分比数值,设置内边距为高度的数值,最后用绝对定位 百分百填充 item。

做法:
第一步先计算高度,假设宽 100%,那么高为h=9/16=56.25%
第二步利用之前所说设置 padding-bottom 方法实现矩形

<div class="box">
    <div class="scale">
        <p class="item">16:9的矩形</p>
    </div>
</div>
<style>
    .box{
        width: 80%;
    }
    .scale{
        width: 100%;
        padding-bottom: 56.25%;
        height:0;
        position:relative;
    }
    .item{
        width: 100%;
        height: 100%;
        background-color: pink;
        position: absolute;
    }
</style>

14、 [] == [] 输出的结果是?为什么?

  • []单独用在==表达式中时,会隐含转型为""(空字符串)
  • 一个不是 null 或者 undefined 的对象(包括[])用在布尔表达式中,会被作为 true 处理。因此 [] 结果是 true,![] 的结果是false。
  • 所以:
    []==[] 等价于""==true
    []==![] 等价于 ""==false
  • 而空字符串在布尔表达式中作为 false 处理。
  • 因此:
    [] == [] 结果是 false
    [] == ![] 结果是 true

15、JavaScript 的基本数据类型都有什么?

  • 基本数据类型:Number、String、Boolean、undefined、Null、Symbol、ES10新增的BigInt(任意精度整数)七类。
  • 引用数据类型(Object类):常规名值对的无序对象 {a:1},数组[1,2,3],以及函数。

原始数据类型:

  • 布尔类型:布尔表示一个逻辑实体,可以有两个值:true 和 false。
  • Null 类型:Null 类型只有一个值: null。
  • Undefined 类型:一个没有被赋值的变量会有个默认值 undefined。
  • 数字类型:根据 ECMAScript 标准,JavaScript 中只有一种数字类型:基于 IEEE 754 标准的双精度 64 位二进制格式的值(-(253 -1) 到 253
    -1)。它并没有为整数给出一种特定的类型。除了能够表示浮点数外,还有一些带符号的值:+Infinity,-Infinity 和 NaN (非数值,Not-a-Number)。
  • BigInt 类型:BigInt类型是 JavaScript 中的一个基础的数值类型,可以用任意精度表示整数。使用 BigInt,您可以安全地存储和操作大整数,甚至可以超过数字的安全整数限制。BigInt是通过在整数末尾附加 n 或调用构造函数来创建的。
  • String字符串类型:JavaScript的字符串类型用于表示文本数据。它是一组16位的无符号整数值的“元素”。在字符串中的每个元素占据了字符串的位置。第一个元素的索引为0,下一个是索引1,依此类推。字符串的长度是它的元素的数量。
  • Symbols符号类型:符号(Symbols)是ECMAScript 第6版新定义的。符号类型是唯一的并且是不可修改的, 并且也可以用来作为Object的key的值(如下). 在某些语言当中也有类似的原子类型(Atoms).
    你也可以认为为它们是C里面的枚举类型。

引用数据类型:

  • Object对象: 在计算机科学中, 对象是指内存中的可以被 标识符引用的一块区域。

16、实现一个对象的浅拷贝和一个基本的深拷贝(可以不考虑函数)
浅拷贝:
demo数据:

// 对象的浅拷贝
var obj = {
    name: '张三',
    age: 33,
    sex: '男',
    hobby: {
        a: '1'
    }
}

方法一:使用循环遍历对象,将其键值赋值给一个新数组;

var newObj = {}; 
//  循环遍历数组
for (var k in obj) {
    newObj[k] = obj[k];
}
console.log(newObj);    //{name: "张三", age: 33, sex: "男", hobby: {…}} 

newObj.hobby.a = '2';
console.log(obj.hobby.a);    // 2
console.log(newObj.hobby.a); // 2

方法二:采用 Object.assign(新数组,拷贝的数组); 方法

var newObj = {};
Object.assign(newObj, obj);  
console.log(newObj);    // {name: "张三", age: 33, sex: "男", hobby: {…}}

newObj.hobby.a = '2';
console.log(obj.hobby.a);    //2
console.log(newObj.hobby.a);    //2

深拷贝:
demo数据格式:

// demo test
var objTest = { 
    name:"gino", 
    sex:"male", 
    family:{ 
        brother:"zhipeng", 
        mother:"SunAiyun",     
    }
};

var arrTest = ["a","b","c","d"];

方法一:
JSON.parse(JSON.stringify(objTest)) 拷贝对象
JSON.parse(JSON.stringify(arrTest)) 拷贝数组

方法二:

//深拷贝
function deepCopy(arr) {
  let copyArr = arr.constructor === Array ? [] : {}
  for (let i in arr) {
    if (typeof arr[i] === 'object') {
      copyArr[i] = deepCopy(arr[i]) // 引用类型的话进行递归操作
    } else {
      copyArr[i] = arr[i] // 值类型直接赋值
    }
  }
  return copyArr
}

方法三:使用递归:

function deepClone_1(obj){
    let objClone = Array.isArray(obj)?[]:{};
    if(obj && typeof obj==="object"){
        for(key in obj){
            if(obj.hasOwnProperty(key)){
                //判断ojb子元素是否为对象,如果是,递归复制
                if(obj[key]&&typeof obj[key] ==="object"){
                    objClone[key] = deepClone_1(obj[key]);
                }else{
                    //如果不是,简单复制
                    objClone[key] = obj[key];
                }
            }
        }
    }
    return objClone;
}    

console.log('对象深拷贝', deepClone_1(objTest))
console.log('数组深拷贝', deepClone_1(arrTest))

方法四:利用数组的 Array.prototype.forEach 和其他一些 ECMAScript 5中的 Object.* 函数

var deepClone_2 = function (obj){
    var copy = Object.create( Object.getPrototypeOf(obj) );
    var propNames = Object.getOwnPropertyNames(obj);
    propNames.forEach(function(name){
        if(typeof obj[name] === 'object') {
            let copyObj = deepClone_2(obj[name])
            copy[name] = copyObj
        } else {
            var desc = Object.getOwnPropertyDescriptor(obj, name);
            Object.defineProperty(copy, name, desc);
        }
    }); 
    return copy;
}

console.log('对象深拷贝', deepClone_2(objTest))
console.log('数组深拷贝', deepClone_2(arrTest))

方法五:

// 对象的浅拷贝
var obj = {
    name: '张三',
    age: 33,
    sex: '男',
    hobby: {
        a: '1'
    }
}
 
var newObj = {}; 
 
//递归循环  
function deepCopy_3(newObj, oldObj) {
    for (var k in oldObj) {
        if (oldObj[k] instanceof Array) {
            newObj[k] = [];
            deepCopy_3(newObj[k], oldObj[k]);
        }
        else if (obj[k] instanceof Object) {
            newObj[k] = {};
            deepCopy_3(newObj[k], oldObj[k]);
        } else {
            // 基本数据类型
            newObj[k] = oldObj[k];
        }
    }
}
 
// 对象深拷贝
deepCopy_3(newObj, obj);
console.log(newObj);    //   {name: "张三", age: 33, sex: "男", hobby: {…}}
 
newObj.hobby.a = '2';
console.log(obj.hobby.a);    //    1
console.log(newObj.hobby.a);    //    2

17、数据格式 JSON 转换为字符串,以及把字符串转换为 JSON 的方法。
(1)字符串 → JSON对象:

  • JSON.parse()方法
var a = '{"a":2}';
JSON.parse(a);  //{a:2}
  • eval() 方法:计算某一个字符串,并执行其中的 javascript 代码 【不推荐】
var s = '{a:2}';
eval('(' + s + ')'); //将json字符串转成json对象,需要在字符串外包裹一对括号(),   {a:2}

(2)json对象 → 字符串

  • JSON.stringify()方法 ,将非字符串转成字符串
var s = {'a':2};
JSON.stringify(s);  //"{"a":2}"

(3)将值转换成字符串(值 → 字符串)

  • 转换函数 toString(),弱类型转换,强制类型转换
    value.toString() 将数值转成对应进制的字符串
var n =8;
n.toString(几进制)

注意:不能将 null 和 undefined 转换成字符串

  • 弱类型转换:value + '' 将值转换成字符串
    2+ ''
  • 强制类型转换:String(value)

(4)字符串 → 值

  • 转换函数:parseInt() 和 parseFloat()
parseInt("1234blue");   //returns 1234
parseInt("22.5");     //returns 22
parseInt("blue");    //returns NaN

parseInt("AF", 16); //returns 175
parseInt("10", 2); //returns 2

parseFloat("1234blue");   //returns 1234.0
parseFloat("22.5");      //returns 22.5
parseFloat("blue");     //returns NaN
  • 强制类型转换
    Number(value)——把给定的值转换成数字(可以是整数或浮点数);
Number(false) 0
Number(true) 1
Number(undefined) NaN
Number(null) 0
Number( "5.5 ") 5.5
Number( "56 ") 56
Number( "5.6.7 ") NaN

补充:小数

  • 四舍五入
var num =2.4492425542;  
num = num.toFixed(2); // 输出结果为 2.45 
  • 不四舍五入 (不进位)
Math.floor(15.7784514000 * 100) / 100   // 输出结果为 15.77  
  • 当成字符串,使用正则匹配
Number(15.7784514000.toString().match(/^\d+(?:\.\d{0,2})?/))    
// 输出结果为 15.77,不能用于整数如 10 必须写为10.0000  

注意:如果是负数,请先转换为正数再计算,最后转回负数。

18、说出至少 4 种 vue 当中的指令和它的用法

  • v-if:判断是否隐藏
  • v-for:数据循环
  • v-bind:class:绑定一个属性
  • v-model:实现数据双向绑定

19、vue-router 怎么定义动态路由的?怎么获取传过来的动态参数?

  • 简单回答:
    定义:path:'a/:value'
    获取:this.$route.params.value
  • 全面了解:
    可以通过 query , param 两种方式
    区别: query 通过 url 传参,刷新页面还在 params 刷新页面不在了。
    (1)params 的类型:
    配置路由格式: /router/:id
    传递的方式: 在path后面跟上对应的值
    传递后形成的路径: /router/123
<!-- 动态路由-params -->
<!-- 在APP.vue中 -->
<router-link :to="'/user/'+userId" replace>用户</router-link>    
//在index.js中
{
	path: '/user/:userid',
	component: User,
},

跳转方法:

// 方法1:
<router-link :to="{ name: 'users', params: { uname: wade }}">按钮</router-link>
// 方法2:
this.$router.push({name:'users',params:{uname:wade}})
// 方法3:
this.$router.push('/user/' + wade)

可以通过 $route.params.userid 获取你说传递的值
(2)query 的类型
配置路由格式: /router,也就是普通配置
传递的方式: 对象中使用 query 的 key 作为传递方式
传递后形成的路径: /route?id=123

<!--动态路由-query -->
<!-- 方法一:直接在 router-link 标签上以对象的形式 -->
<router-link :to="{path:'/profile',query:{name:'why',age:28,height:188}}">档案</router-link>
// 方法二:或者写成按钮以点击事件形式  
<button @click='profileClick'>我的</button>   

//点击事件
profileClick(){
	this.$router.push({
		path: "/profile",
		query: {
			name: "kobi",
			age: "28",
			height: 198
		}
	});
}

跳转方法:

// 方法1:
<router-link :to="{ name: 'users', query: { uname: james }}">按钮</router-link>
// 方法2:
this.$router.push({ name: 'users', query:{ uname:james }})
// 方法3:
<router-link :to="{ path: '/user', query: { uname:james }}">按钮</router-link>
// 方法4:
this.$router.push({ path: '/user', query:{ uname:james }})
// 方法5:
this.$router.push('/user?uname=' + jsmes)

可以通过 $route.query 获取你说传递的值。

20、分别写出 webpack 打包给服务器和本地开发预览的命令代码。
npm run buildnpm run dev
21、下面代码
let a = [1,2,4]
let b = a.map(v=>{v = v* 2}) 写出 b 的值
在这里插入图片描述
原题中没有 return v,所以b的值是 [undefined,undefined,undefined]。
如果有 return v,则 b 的值是 [2,4,8],正常 map 写法。
在这里插入图片描述

22、请生成下面这段DOM结构,要求:使用标准的DOM方法或属性。

<div class="example">
	<p class="slogan">请编辑这段话!</p>
</div>

答案:
主要用到的函数:createElement,appendChild,createTextNode,setAttribute 这几个函数。
(1)创建元素的DOM方法 – createElement;
(2)设置元素属性的DOM方法 – setAttribute;
(3)添加到元素上的DOM方法 – appendChild;
(4)创建文字的DOM方法 - createTextNode;
(5)设置样式的DOM属性 – className(HTML DOM属性)

var oDiv=document.createElement("div");
var op=document.createElement("p");
oDiv.appendChild(op);
op.className="slogan";

var oText=document.createTextNode("请编辑这段话!");
op.appendChild(oText);
oDiv.setAttribute("id","example");

document.body.appendChild(oDiv);

23、请使用 Promise 封装 Ajax 操作
原始的 Ajax 操作如下:

var onSuccess = function(result){};  //成功的回调
var onFail = function(error){};  //失败的回调
var req = new XMLHttpRequest();
req.open("POST", "www.baidu.com", true);
req.onload = function(){
	if( req.readyState === 4 && req.status === 200 ){
		onSuccess(req.response);
	}else {
		onFail(req.statusText);
	}
}
req.onerror = function(){
	onFail(Error("网络异常"));
}

答案1:

//参考:https://www.nowcoder.com/questionTerminal/86311e67b9124788b68acdf002da55f8

const ajax = url => {
    return new Promise((resolve, reject) => {
        let req = new XMLHttpRequest();
        req.open("POST", url, true);
        req.onload = () => {
          if(req.readyState === 4 && req.status === 200){
            resolve(req.response);
          } else {
            reject(req.statusText);
          }
        }
        req.onerror = () => {
          reject(Error("网络异常"));
        }
    })
}

答案2:

return new Promise(function(resolve, reject){
    var req = new XMLHttpRequest();
    req.open("POST", "www.baidu.com", true);
    req.onload = function(){
      if(req.readyState === 4 && req.status === 200){
        resolve(req.response);
      } else {
        reject(req.statusText);
      }
    };
    req.onerror = function(){
      reject(Error("网络异常"));
    }
}); 

答案3:

// 参考:https://blog.csdn.net/weixin_45298413/article/details/108809108

const promise = new Promise((resove,reject)=>{
    var req = new XMLTttpRequest();
    req.open("post","http://www.baidu.com",true);
    req.onload = function(){
        if(req.readyState === 4 && req.status === 200){
            onSuccess(req.response);
        } else {
            onFail(req.statusText); 
        }
    }
    req.onerror = function(){
        onFail(Error("网络异常"));  
    }
})

延伸:
(1)利用Promise知识,用原生JS封装Ajax
参考:https://www.jianshu.com/p/76b32c84216a


//封装一个Promise函数
function queryData (url){
    //创建一个Promise实例
    var p = new Promise(function(resove,reject){
        var req = XMLHttpRequest();
        req.onreadystatechange = function(){
            if(req.readystate !== 4) return;
            if(req.readystate === 4 && req.status === 200) {
                //处理正常情况
                resove('req.responseText');
            }else{
                //处理异常情况
                reject('服务器错误');
            }
        }
        req.open('get',url);
        req.send();
    })
    return p;
}

//用实例方法调用封装函数
queryData('http://localhost:3000/data')
    .then(function(data){
        console.log('data1',data);
        //想要使用链式编程需要return返回,并且返回的数据将是下一次then方法参数函数的data参数
        return queryData('http://localhost:3000/data1')
    })
    .then(function(data){
        console.log('data2',data);
    })

(2)参考1:https://www.cnblogs.com/jwyblogs/p/11962866.html
参考2:https://www.nowcoder.com/questionTerminal/86311e67b9124788b68acdf002da55f8
参考3:https://segmentfault.com/a/1190000015114855?utm_source=tag-newest

(二)
⼀、HTML 与 CSS。

  1. 实现两列布局:左列宽度为 200px,右列占据剩余宽度。要求:
    可以不兼容旧浏览器;
    不能使⽤绝对定位等特殊定位⽅式;
    不能使⽤表格布局;
<div class="box">
    <div class="left"></div>
    <div class="right"></div>
</div>

方法一:float+margin-left

div{
  height:200px;
}
.left{
  float: left;
  width: 200px;
  /* background: blue; */
}            
.right{
  margin-left: 200px;
  /* background: red; */
}

方法二:使用 flex

.box{
    width:500px;
    height:200px;
    display: flex;
    /* background:black; */
}
.left{
    width: 200px;
    /* background:pink; */
}
.right{
    flex: 1;
    /* background:green; */
}

方法三:使用 flex + justify-content:space-between;


.box{
    width:500px;
    height:200px;
    /* background:black; */
    display: flex;
    justify-content:space-between;
}
.left{       
	width:200px;
    /* background: pink; */
}
.right{
    /* background:green; */
}

  1. 尺⼨未知的元素置于浏览器可⽤区域的正中央,且位置不随⻚⾯滚动⽽变化(⽆须兼容旧浏览器)。 有疑问,尺寸未知是是意思??
    写法一:
<style>
    .box{
        width: 200px;
        height: 200px;
        position: fixed;
        left: 50%;
        top: 50%;
        transform: translate3d(-50%,-50%,0);
        z-index: 99;
        /* background: pink; */
    }
</style>

<div class="box"></div>

方法二:

使用position属性的绝对定位
.yuansu{
	position: absolute;
	margin-left: -200px;
	margin-top: -80px;
	top: 50%;
	left: 50%;
	display: block;
	z-index: 2000;
}

⼆、JavaScript。

  1. 使⽤正则表达式检查某段字符串是否为 URL。URL 的认定标准(符合以下条件之⼀):
    以「协议://」开头,例如「http://」、「https://」、「ftp://」、「file://」等,但⽆需校验是否为有效协议;
    以「//」开头,即跟随⻚⾯协议。
string httpurl = String.Format(@"http://{0}(/{1}(\?{2}){{0,1}}){{0,1}}
  1. 假设某个数组的元素全是数字类型,请编写代码找出该数组中的重复元素。要求:
    代码中最多只能存在⼀次遍历(数组循环,indexOf、forEach、filter 等⽅法均视为遍历);
    不能使⽤ ES>=6 中的新增⽅法和对象。

方法一:
写法一:【正确】

function getRepeat(ele){
  var tmp = [];
  var d = {};
  for(let n of ele){
    if(!d[n]){
      d[n] = 0;
    }
    if(n in d && d[n] == 1){
      tmp.push(n);
    }
    d[n] += 1;
  }
  return tmp;
}
var a = [5, 4, 3, 2, 1, 2, 3, 2, 1];
console.log('取出重复元素',getRepeat(a))

写法二:【正确】

var a = [5, 4, 3, 2, 1, 2, 3, 2, 1];

Array.prototype.duplicate=function() {
  var tmp = [];
  var d = {};
  for(let n of a){
  	//一个个数测试,d保存的就是同一个数字出现的次数
    if(!d[n]){
        d[n] = 0;
    }
    //d[n] == 1的判断是为了确保相同数字只显示一个
    if(n in d && d[n] == 1){
        tmp.push(n);
    }
    //判断有没有重复,就算下它出现了几次
    d[n] += 1;
  }
  return tmp;
}
console.log('取出重复元素', a.duplicate())

方法二:(理解)

var a = [5,4,3,2,1,2,3,2,1,];
Array.prototype.duplicate=function() {
  var tmp = [];
  this.concat().sort().sort(function(a,b){
    if(a==b && tmp.indexOf(a) === -1) tmp.push(a);
  });
  return tmp;
}
console.log('取出重复元素', a.duplicate())

方法三:(理解)不符合本题要求

function remove(arr){
    var new_arr = [];
    var new_arr2 = [];
    arr.forEach((val, index)=> {
        //new_arr是对数组去重
        if(new_arr.indexOf(val) === -1) {
            new_arr.push(val);
        } else {
            //对重复的元素本身去重
            if(new_arr2.indexOf(val) === -1) {
                new_arr2.push(val);
            }
        }
    });   
    return new_arr2;   
}
var a = [1,2,4,4,3,3,1,5,3];
var b = remove(a);
console.log('b',b);
  1. a、b、c 都是执⾏异步操作的函数,请通过 Promise、async、await 实现:a、b 并⾏执⾏完毕后再执⾏ c。
    function a(callback) { setTimeout(callback, 10); }
    function b(callback) { setTimeout(callback, 20); }
    function c(callback) { setTimeout(callback, 30); }
<script>
async function foo(){
   await sleep(2000);
   await C();
}
const sleep = (timeout = 2000) => new Promise(resolve => {
	A();
	B();
 	setTimeout(resolve, timeout)
})	
function A(){
    var promise = new Promise(function(resolve, reject){
        setTimeout(function(){
            console.log(1);
            resolve();
        },3000)
    })
    return promise;
}
function B(){
    var promise = new Promise(function(resolve, reject){
        setTimeout(function(){
            console.log(2);
            resolve();
        },2000)
    })
    return promise;
}
function C(){
    var promise = new Promise(function(resolve, reject){
        setTimeout(function(){
            console.log(3);
            resolve();
        },1000)
    })
    return promise;
}
foo();
</script>

方法二:
参考:https://blog.csdn.net/qq_39456813/article/details/101191977

(三)
一、单选题
1、下列有关定位说法错误的是? A
A. absolute 可以基于 static 定位
「 生成绝对定位的元素,相对于static 定位以外的第一个父元素进行定位 」
B. relative 可以基于 absolute 进行定位。
C. fixed 固定悬浮在 视口内
D. 定位 position 不能继承到子元素
在这里插入图片描述
2、把鼠标移到按钮并点击时,会产生一串什么样的事件? D
A. active hover focus
B. hover focus active
C. focus hover active
D. hover active focus
3、在 JS 中 ES5 以下哪种情况会产生一个新的作用域? E
A. if 语句
B. 所有选项都会
C. for 语句
D. try……catch块
E. 函数
4、以下代码在控制台打印结果为? D

<script defer>
	console.log('inline script')
</script>
<script defer src="test1.js"></script>
<script src="test2.js"></script>

其中:

// test1.js
console.log('test1')
// test2.js
console.log('test2')

A. test1 inline script test2
B. inline script test1 test2
C. test1 test2 inline script
D. inline script test2 test1
在这里插入图片描述
5. 现有如下策略:

Content-Security-Policy: default-src 'self'; script-src *.script1.org; font-src test.org; media-src https:; script-src * .script2.org 

在该网页中,以下说法错误的是: C
A. 图片只能从本域获取
B. 多媒体文件必须通过 SSL方式获取
C. 可运行脚本不允许来自于 script2.org或其子域
D. 字体资源允许来自于 test.org 及其子域
解析:
参考:https://www.cnblogs.com/suizhikuo/p/12215229.html
Web 安全之内容安全策略(Content-Security-Policy,CSP)配置

二、多选题:
1、以下哪几个选项的输出结果相同? ABD
A.

for(var i = 0; i < 10; i++){
	setTimeout(function(i){
		console.log(i)
	}, 0, i)
}

B.

for(let i = 0; i < 10; i++){
	setTimeout(function(){
		console.log(i)
	}, 0)
}

C.

for(var i = 0; i < 10; i++){
	setTimeout(function(){
		console.log(i)
	}, 0)
}

D.

for(var i = 0; i < 10; i++){
	(function(i){
		setTimeout(function(){
			console.log(i)
		}, 0)
	}(i))
}

解析:
ABD 输出 0123456789
C 输出10个10
2、以下哪些说法是正确的?ABCD
A. 所有的引用类型(数组、对象、函数),都具有对象特性,即可自由扩展属性(null 除外)
B. 所有的引用类型(数组、对象、函数),__proto__ 属性值指向它的构造函数的 prototype 属性值
C. 所有的引用类型(数组、对象、函数),都有一个 __proto__ 属性,属性值是一个普通的对象
D. 所有的函数,都有一个 prototype 属性,属性值也是一个普通的对象

知识点:
原型的5个规则(引用类型指除了null的引用类型 )
(1)所有引用类型(数组,对象,函数),都具有对象特性,即可自由扩展属性

var obj = {}
obj.a=100;
var arr =[]
arr.a =100;
function fn(){}
fn.a=100;

(2)所有的引用类型,都有一个proto属性,属性值是一个普通对象

console.log(obj.__proto__);
console.log(arr.__proto__);
console.log(fn.__proto__);

(3)所有的函数都有一个prototype属性,属性值也是一个普通对象

console.log(fn.prototype)

(4)所有的引用类型(数组,对象函数),proto属性指向他的构造函数的prototype属性

console.log(obj.__proto__===Object.prototype)   

(5)当试图得到一个引用类型的某个属性时,如果对象本身没有这个属性,那么会去他的proto(即它的构造函数的prototype)中去寻找

function Foo(name){
	this.name= name;
}
Foo.prototype.alertName = function(){
	alert(this.name)
}
var f = new Foo('zs');
f.printName=function(){
    console.log(this.name)
}
f.printName();
f.alertName();      

三、填空题
1、请写出以下表达式的结果: typeof null
在这里插入图片描述

类型结果
Undefined“undefined”
Null“object” (见下文)
Boolean“boolean”
Number“number”
BigInt(ECMAScript 2020 新增)“bigint”
String“string”
Symbol (ECMAScript 2015 新增)“symbol”
Function 对象 (按照 ECMA-262 规范实现 [[Call]])“function”

2、请写出以下表达式的结果:

void 0 == null        //true
void 0 == undefined   //true
void 0 === null       //false
void 0 === undefined  //true

四、问答题
题目描述
填写一个函数,实现多个异步任务并行时限制最大并行数量的功能,可参考如下函数结构:

async function runParallel(max: number, tasks: (() => Promise<any>)[]) : Promise<any[]>

使用:

const tasks = [...Array(100).keys()].map(i => () => new Promise(res => setTimeout(res,1000)))

当并行 Promise 的数量达到10个时,就会等待其中某个 resolve 后,才开启队列中下一个 Promise。

五、编程题
1、URL参数提取
时间限制:C/C++ 1秒,其他语言 2 秒
空间限制:C/C++ 32768K,其他语言 65536K
语言限定:JavaScript(V8 6.0.0)
题目描述
实现一个parseURL函数,用于提取URL中的哈希以及查询参数。
输入描述
输一个含有哈希的URL,其中包含若干个查询参数
输出描述
输出有若干行,第一行为URL中的哈希,接下来的每一行为该URL的查询参数名称和值,用空格分隔,它们的顺序按照原本在URL中出现的顺序输出;
输入
http://163.com/role/search/#page?pageSize=20&pageNumber=10
输出
page
pageSize 20
pageNumber 10
方法一:

function parseQueryString(argu){
  var str = argu.split('?')[1];
  var result = {};
  var temp = str.split('&');
  for(var i=0; i<temp.length; i++)
  {
     var temp2 = temp[i].split('=');
     result[temp2[0]] = temp2[1];
  }
  return result;
}

var url = "http://www.taobao.com/index.php?key0=0&key1=1&key2=2.............";
var obj = parseQueryString(url);
console.log('obj',obj)

方法二:

function getQueryString(name) {
  var reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)')
  var currentUrl = window.location.search
  let r = ''
  if (currentUrl != '') {
    r = window.location.search.substr(1).match(reg) // 没有#的url
  } else {
    r = window.location.hash.split('?')[1].match(reg)
  }
  if (r != null) return unescape(r[2])
  return null
}
this.smallId = getQueryString('smallId')
this.supplierId = getQueryString('supplierId')
this.taskDetailId = getQueryString('taskDetailId')

2、实现一个 format 函数,快捷格式化字符串,不用老写 +号连接。
输入描述
输入至少有一行:
第一个行是一个字符串,其中包含一些占位符,占位符格式为:{0}、{1}、{2} 依次类推(花括号内可能含有空格,例如 {1} 是合法的 );

后续行(如果有)为上面的占位符按顺序对应的值。

输出描述
输出只有一行,为格式换后的字符串,将所有占位符替换为对应值;

注:输入输出相关代码已经写好,您只需实现 format 函数即可,该函数返回一个格式化后的字符串。

示例1 输入输出示例仅供调试。
输入
h1 {0},今天是 {1} 年 {2} 月 {3} 号星期 {4}
Tom
2015
6
20

输出
hi Tom,今天是2015年6月20号星期五
示例2 输入输出示例仅供调试。
输入
Hello {2}
a
b
示例3 输入输出示例仅供调试。
输入
{0} {2} {2},My {1} is {2}
Hello
name
World
输出
Hello World World,My name is World

JavaScript题:
参考:https://blog.csdn.net/liuyan19891230/article/details/102385743

(四)
一、选择题
1、下面代码的输出是什么? B

const person = {
	name:"Lydia",
	age:21
};
for(const item in person){
	console.log(item);
}

A. {name:“Lydia”},{age:21}
B. "name","age"
C.“Lydia”,21
D. [“name”,“Lydia”],[“age”,21]
解析:
在 for-in 循环中,我们可以通过对象的 key 来进行迭代,也就是这里的 name 和 age。在底层,对象的 key 都是字符串(如果他们不是 Symbol 的话)。在每次循环中,我们将 item 设定为当前遍历到的 key 所以一开始, item 是 name,之后 item 输出的则是 age。

for…in 语句用于遍历数组或者对象的属性,得到数组的下标,对象的属性名。
for…of 遍历(当前对象上的)每一个属性值,与 forEach一样

例如:

var arr=[{name:'张三'},{name:'李四'}];
var obj={name:'王五'};
for (var i in arr){
  console.log(i)   // 0 1 遍历得到下标
}
for (var i in obj){
  console.log(i)   // name 得到属性名
}

for (var i of arr){
  console.log(i)   // {name:'张三'} {name:'李四'}  得到数组的每一项值,与forEach一样
}

2、num 的值是什么? C
const num = parseInt(“7*6”, 10);
A. 42 B. “42” C. 7 D. NaN
解析:
只返回了字符串中第一个字母,设定了 进制 后 (也就是第二个参数,指定需要解析的数字是什么进制: 十进制、十六机制、八进制、二进制等等……), parseInt 检查字符串中的字符是否合法。一旦遇到一个在指定进制中不合法的字符后,立即停止解析并且忽略后面所有的字符。

* 就是不合法的数字字符。所以只解析到 “7”,并将其解析为十进制的 7, num 的值即为 7。

3、下面代码输出的是什么? A

function getInfo(member, year){
	member.name = "Lydia";
	year = "1998";
}
const person = { name: "Sarah" };
const birthYear = "1997";
getInfo(person, birthYear);
console.log(person, birthYear);

A. {name: "Lydia"}, "1997"
B. {name:“Sarah”}, “1998”
C. {name:“Lydia”}, “1998”
D. {name:“Sarah”}, “1997”
解析:
普通参数都是 值 传递的,而对象则不同,是 引用 传递。所以说, birthYear 是值传递,因为他是个字符串而不是对象。当我们对参数进行值传递时,会创建一份该值的 复制 。(可以参考问题46)

变量 birthYear有一个对 "1997"的引用,而传入的参数也有一个对 “1997” 的引用,但二者的引用并不相同。当我们通过给 year 赋值 “1998” 来更新 year 的值的时候我们只是更新了 year(的引用)。此时 birthYear 仍然是 “1997”。

而 person 是个对象。参数 member 引用与之 相同的 对象。当我们修改 member 所引用对象的属性时, person 的相应属性也被修改了,因为他们引用了相同的对象。person 的 name 属性也变成了 “Lydia”。

4、下面代码的输出是什么? A

const name = "Lydia";
age = 21;
console.log(delete name);
console.log(delete age);

A. false, true
B. “Lydia”, 21
C. true, true
D. undefined, undefined
解析:
delete 操作符返回一个布尔值:true 指删除成功,否则返回 false。 但是通过 var, const 或 let 关键字声明的变量无法用 delete 操作符来删除。

name 变量由 const 关键字声明,所以删除不成功:返回 false。而我们设定 age 等于 21时,我们实际上添加了一个名为 age 的属性给全局对象。对象中的属性是可以删除的,全局对象也是如此,所以 delete age 返回 true。

5、下面代码的输出是什么? D

const numbers = [1,2,3,4,5]
const [y] = numbers;
console.log(y);

A. [[1,2,3,4,5]]
B. [1,2,3,4,5]
C. [1]
D. 1
解析:
通过解构赋值来解析来自对象的数组或属性的值,比如说:[a, b] = [1, 2]; a的值现在是 1, b的值现在是 2。而在题目中: [y] = [1, 2, 3, 4, 5]; 也就是说, y等于数组的第一个值就是数字 1,我们输出 y, 返回 1.
6、下面代码输出什么? C

const value = {number: 10};
const multiply = (x = {...value}) => {
	console.log(x.number *= 2);
};
multiply();
multiply();
multiply(value);
multiply(value);

A. 20, 40, 80, 160
B. 20, 40, 20, 40
C. 20, 20, 20, 40
D. NaN, NaN, 20, 40
解析:
在ES6中,我们可以使用默认值初始化参数。如果没有给函数传参,或者传的参值为 “undefined”,那么参数的值将是默认值。上述例子中,我们将 value 对象进行了解构并传到一个新对象中,因此 x 的默认值为 {number:10} 。

默认参数在调用时才会进行计算,每次调用函数时,都会创建一个新的对象。我们前两次调用 multiply 函数且不传递值,那么每一次 x 的默认值都为 {number:10} ,因此打印出该数字的乘积值为 20。

第三次调用 multiply 时,我们传递了一个参数,即对象 value。*= 运算符实际上是 x.number=x.number*2 的简写,我们修改了 x.number 的值,并打印出值 20。

第四次,我们再次传递 value对象。x.number 之前被修改为 20,所以 x.number*=2 打印为 40

7、下面代码输出什么? D
[1,2,3,4].reduce((x,y)=> console.log(x,y));
A. 12 and 33 and 64
B. 12 and 23 and 34
C. 1undefined and 2undefined and 3undefined and 4undefined
D. 1 2 and undefined 3 and undefined 4
解析:
语法:
array.reduce(function(total, currentValue, currentIndex, arr), initialValue)

reduce 函数接收4个参数:
total:必需。初始值, 或者计算结束后的返回值。
currentValue:必需。当前元素。
currentIndex:可选。当前元素的索引。
arr:可选。当前元素所属的数组对象。

reduce 函数的返回值将会分配给累计器,该返回值在数组的每个迭代中被记住,并最后成为最终的单个结果值。

reduce 函数还有一个可选参数 initialValue, 该参数将作为第一次调用回调函数时的第一个参数的值。如果没有提供 initialValue,则将使用数组中的第一个元素。

在上述例子, reduce方法接收的第一个参数(Accumulator)是 x, 第二个参数(Current Value)是 y

在第一次调用时,累加器 x1,当前值 “y”2,打印出累加器和当前值:12

例子中我们的回调函数没有返回任何值,只是打印累加器的值和当前值。如果函数没有返回值,则默认返回 undefined。在下一次调用时,累加器为 undefined,当前值为“3”, 因此 undefined3被打印出。

在第四次调用时,回调函数依然没有返回值。累加器再次为 undefined ,当前值为“4”undefined4被打印出。

8、使用哪个构造函数可以成功继承 Dog 类? B

class Dog{
	constructor(name){
		this.name = name;
	}
}
class Labrador extends Dog{
	// 1
	constructor(name, size){
		this.size = size;
	}
	// 2
	constructor(name,size){
		super(name);
		this.size = size;
	}
	// 3
	constructor(size){
		super(name);
		this.size = size;
	}
	// 4
	constructor(name,size){
		this.name = name;
		this.size = size;
	}
}

A. 1 B. 2 C. 3 D. 4
解析:
在子类中,在调用 super 之前不能访问到 this 关键字。如果这样做,它将抛出一个 ReferenceError:1和4将引发一个引用错误。

使用 super 关键字,需要用给定的参数来调用父类的构造函数。父类的构造函数接收 name 参数,因此我们需要将 name 传递给 super

Labrador 类接收两个参数, name 参数是由于它继承了 Dogsize 作为 Labrador 类的额外属性,它们都需要传递给 Labrador 的构造函数,因此使用构造函数2正确完成。

9、如何能打印出 console.log 语句后注释掉的值? C

function* startGame(){
	const answer = yield "Do you love JavaScript?";
	if(answer !== "Yes"){
		return "Oh wow... Guess we're gone here";
	}
	return "JavaScript loves you back ❤️";
}
const game = startGame();
console.log(/* 1 */);  // Do you love JavaScript?
console.log(/* 1 */)   // JavaScript loves you back ❤️

A. game.next("Yes").value and game.next().value
B. game.next.value("Yes") and game.next.value()
C. game.next().value and game.next("Yes").value
D. game.next.value() and game.next.value("Yes")
解析:
generator 函数在遇到 yield关键字时会“暂停”其执行。首先,我们需要让函数产生字符串 Doyou loveJavaScript?,这可以通过调用 game.next().value 来完成。上述函数的第一行就有一个 yield 关键字,那么运行立即停止了, yield 表达式本身没有返回值,或者说总是返回 undefined, 这意味着此时变量 answerundefined

next 方法可以带一个参数,该参数会被当作上一个 yield 表达式的返回值。当我们调用 game.next("Yes").value 时,先前的 yield 的返回值将被替换为传递给 next() 函数的参数 "Yes"。此时变量 answer 被赋值为 "Yes", if语句返回 false,所以 JavaScriptloves you back❤️被打印。

10、下面代码输出什么? B

async function getData(){
    return await Promise.resolve("I made it!");
}
const data = getData();
console.log(data);

A. “I made it!”
B. Promise(< pending>)
C. Promise(< resolved>:“I made it!”)
D. undefined
解析:
异步函数始终返回一个promiseawait 仍然需要等待promise的解决:当我们调用 getData()并将其赋值给 data,此时 datagetData方法返回的一个挂起的 promise,该 promise 并没有解决。
如果我们想要访问已解决的值 "I made it!",可以在 data 上使用 .then() 方法:data.then(res=>console.log(res)) 这样将打印 "I made it!"

二、问答题
1、var、let const的区别?

var 定义的变量,没有块的概念,可以跨块访问, 不能跨函数访问。 let
定义的变量,只能在块作用域里访问,不能跨块访问,也不能跨函数访问。 const
用来定义常量,使用时必须初始化(即必须赋值),只能在块作用域里访问,而且不能修改。

    // 块作用域
    {
        var a = 1;
        let b = 2;
        const c = 3;
        // c = 4; // 报错
        var aa;
        let bb;
        // const cc; // 报错
        console.log(a); // 1
        console.log(b); // 2
        console.log(c); // 3
        console.log(aa); // undefined
        console.log(bb); // undefined
    }
    console.log(a); // 1
    // console.log(b); // 报错
    // console.log(c); // 报错
 
    // 函数作用域
    (function A() {
        var d = 5;
        let e = 6;
        const f = 7;
        console.log(d); // 5
        console.log(e); // 6  
        console.log(f); // 7 
 
    })();
    // console.log(d); // 报错
    // console.log(e); // 报错
    // console.log(f); // 报错

延伸:const定义的对象属性是否可以改变:可以改变
参考:https://blog.csdn.net/unionz/article/details/80032048

 const person = {
     name : 'jiuke',
     sex : '男'
 }
 person.name = 'test' 
 console.log(person.name)  //test

person对象的name属性确实被修改了
因为对象是引用类型的,person中保存的仅是对象的指针,这就意味着,const仅保证指针不发生改变,修改对象的属性不会改变对象的指针,所以是被允许的。也就是说const定义的引用类型只要指针不发生改变,其他的不论如何改变都是允许的。

2、vue中key值的作用是什么?
渲染列表时,key值需要一个唯一确定的id来赋值。

  • key是为每个vnode指定唯一的id,在同级vnode的Diff过程中,可以根据key快速的进行对比,来判断是否为相同节点。
  • 利用 key 的唯一性生成 map 对象来获取对应节点,比遍历方式更快,指定key后,可以保证渲染的准确性(尽可能的复用 DOM 元素。)
    参考资料:https://www.jianshu.com/p/a634eb3c19c2

3、简单介绍 Promise
参考:https://www.jianshu.com/p/c8ebbc7d4c01

Promise 是一个对象,保存着未来将要结束的事件,她有两个特征:

  • 对象的状态不受外部影响,Promise对象代表一个异步操作,有三种状态,pending进行中,fulfilled已成功,rejected已失败,只有异步操作的结果,才可以决定当前是哪一种状态,任何其他操作都无法改变这个状态,这也就是promise名字的由来
  • 一旦状态改变,就不会再变,promise对象状态改变只有两种可能,从pending改到fulfilled或者从pending改到rejected,只要这两种情况发生,状态就凝固了,不会再改变,这个时候就称为定型resolved,

Promise的基本用法:

let promise1 = new Promise(function(resolve,reject){
    setTimeout(function(){
        resolve('ok')
    },1000)
})
promise1.then(function success(val){
    console.log(val)
})

最简单代码实现promise

class PromiseM {
    constructor (process) {
        this.status = 'pending'
        this.msg = ''
        process(this.resolve.bind(this), this.reject.bind(this))
        return this
    }
    resolve (val) {
        this.status = 'fulfilled'
        this.msg = val
    }
    reject (err) {
        this.status = 'rejected'
        this.msg = err
    }
    then (fufilled, reject) {
        if(this.status === 'fulfilled') {
            fufilled(this.msg)
        }
        if(this.status === 'rejected') {
            reject(this.msg)
        }
    }
}

测试代码:

var mm=new PromiseM(function(resolve,reject){
    resolve('123');
});
mm.then(function(success){
    console.log(success);
},function(){
    console.log('fail!');
});

4、computed 和 watched 的区别是什么?
共同点:

  • 都是监听数据变化的方法,用来观察和响应Vue实例上的数据变动。
  • 可以将复杂的逻辑放入到计算属性和watch 中,从而使得代码更加整齐,分工明确。

不同点:

  • computed
    • 类型:{ [key: string]: Function | { get: Function, set: Function } }
    • 计算属性将被混入到 Vue 事例中。所有 getter 和 setter 的 this 上下文自动地绑定为 Vue 实例。但是如果使用了箭头函数,那么此时使用 this 就不会指向组件实例了。而且计算的结果是会被缓存的,除非依赖的响应式属性变化才会重新计算,这无疑在性能上更加友好。
    • 计算属性将变量的 get 属性重写成了定义的函数方法,实现了数据劫持。
    • 计算属性可以监听 data 和 props 里的值,只有当被监听的数据发生改变时,才会重新计算,从而得到一个新值。
    • 场景:一个数据受到多个数据的影响
  • watch
    • 类型:{ [key: string]: string | Function | Object | Array }
    • 那这里 key 是咱们要监听的数据,值可以是回调函数,方法名或者对象。同样的如果在watch 中使用箭头函数,则不要使用 this ,因为此时它不会指向 Vue 实例。
    • 会产生一个 watcher 对象,在监视的属性每次变动时都会触发回调
    • 监听 data 里的已知变量的变化
    • 场景:一个数据影响多个数据

5、请详细说下你对 Vue 生命周期的理解?
vue生命周期总共分为8个阶段创建前/后,载入前/后,更新前/后,销毁前/后。

  • 创建前/后:在beforeCreated阶段,vue实例的挂载完el还没有。
  • 载入前/后:在beforeMount阶段,vue实例的$el和data都初始化了,但还是挂载之前为虚拟的dom节点,data.message还未替换。在mounted阶段,vue实例挂载完成,data.message成功渲染。
  • 更新前/后:当data变化时,会触发beforeUpdate和updated方法。
  • 销毁前/后:在执行destroy方法后,对data的改变不会再触发周期函数,说明此时vue实例已经解除了事件监听以及和dom的绑定,但是dom结构依然存在。
export default {
    data(){
        return {
            ptext:"测试文本"
        }
    },
    beforeCreate(){
        console.log('enter beforeCreate')
    },
    created(){
        console.log('enter created')
    },
    beforeMount(){
        console.log('enter beforeMount')
    },
    mounted(){
        console.log('enter mounted')
    },
    beforeDestroy(){
        console.log('enter beforeDestroy')
    },
    destroyed(){
        console.log('enter destroyed')
    }
}

补充:谈谈你对vue的双向数据绑定原理的理解
vue.js 是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
具体步骤:

  • 第一步:需要observe的数据对象进行递归遍历,包括子属性对象的属性,都加上 setter和getter。这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到了数据变化
  • 第二步:compile解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图
  • 第三步:Watcher订阅者是Observer和Compile之间通信的桥梁,主要做的事情是:
    • 在自身实例化时往属性订阅器(dep)里面添加自己
    • 自身必须有一个update()方法
    • 待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调,则功成身退。
  • 第四步:MVVM作为数据绑定的入口,整合Observer、Compile和Watcher三者,通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据model变更的双向绑定效果。

简而言之,就是先转化成AST树,再得到的render函数返回VNode(Vue的虚拟DOM节点)

6、介绍一下你对浏览器内核的理解?
主要分成两个部分:渲染引擎(Render Engine)和JS引擎。

  • 渲染引擎:负责取得网页的内容(html,xml和图像等),整理讯息(例如假如css),以及计算网页的显示方式,然后输出到显示器或打印机。浏览器的内核的不同对于网页的语法解释会有不同,所以渲染的效果也不同。所有网页浏览器、电子邮件客户端以及它需要编辑、显示网络内容的应用程序都需要内核。
  • JS引擎:解析和执行JavaScript来实现网页的动态效果。

最开始渲染引擎和JS引擎并没有区分的很明确,后来JS引擎越来越独立,内核就倾向与只指渲染引擎。

补充:常见的浏览器内核有哪些?

  • Trident内核:IE浏览器以Trident作为内核引擎。(遨游,世界之窗,腾讯TT……都是IE)。
    Trident内核最慢。
  • Gecko内核:开放源代码,以C++编写的网页排版引擎,是跨平台的。
    FireFox是基于Gecko开发。
  • presto内核(Opera前内核)(已废弃):该款引擎的特点就是渲染速度的优化达到了极致,然而代价却是牺牲了网页的兼容性。
  • Webkit内核:(Safari内核,Chrome内核原型,开源),它是苹果公司自己的内核,也是苹果的Safari浏览器使用的内核。
    Webkit引擎包含WebCore排版引擎及JavaScriptCore解析引擎,均是从KDE的KHTML及KJS引擎衍生而来,它们都是自由软件,在GPL条约下授权,同时也支持BSD系统的开发。所有Webkit也是自由软件,同时开放源代码。在安全方面不受IE,FireFox的制约。
    所以,Safari浏览器在国内还是很安全的。

(五)
一、(选择题)

1、浏览器针对HTML文档起到了什么作用? B
A. 浏览器用于创建HTML文档
B. 浏览器用于查看HTML文档
C. 浏览器用于修改HTML文档
D. 浏览器用于删除HTML文档
2、默认情况下,使用P标记会形成什么效果?C
A. 在文字P所在位置中加入8个空格
B. P后面的文字会变成粗体
C. 开始新的一行
D. P后面的文字会变成斜体
4、哪个标记用于表示HTML文档的结束?B
A. /BODY
B. /HTML
C. /TABLE
D. /TITLE
5、META元素的作用是什么? B
A. META 元素用于表达HTML文档的格式
B. META元素用于指定关于HTML文档的信息
C. META 元素用于实现本页的自动刷新
6、哪一个标记用于使HTML文档中表格里的单元格在同行进行合并?D
A. cellspacing
B. cellpadding
C. rowspan
D. colspan
7、下列哪项技术可以用于帮助网页设计时使页面具有统一、专业的外观?C
A. HTML
B. DHTML
C. CSS
D. URL
8、哪条命令用于使一行文本折行,而不是插入一个新的段落?B
A. <TD>
B. <BR>
C. <P>
D. <H1>
9、下面关于HTML说法错误的是?B
A. HTML是一种标记语言
B. HTML可以控制页面和内容的外观
C. HTML文档总是静态的
D. HTML文档是超文本文档
10、我们发现为页面中的图像加入超链接后,默认情况下都带有一道黑框,有什么方法可以去掉它呢?B
A. bordercolor=“white”
B. border=“0”
C. style=“no border”
D. style=“0”
11、HTML中关于section元素使用正确的是?D
A. 通常将section元素用做设置样式的页面容器
B. 在没有标题的内容区块应该使用section元素
C. 推荐使用section元素代替article元素、aside元素、或nav元素
D. 如果你想把一块内容分成几块时,应该使用section元素
12、HTTP 404 状态码表示什么意思?B
A. 服务器拒绝请求
B. 服务器找不到请求的网页
C. 无法使用请求的内容特性响应请求的网页
D. 无法使用请求的内容特性响应请求的网页
13、下面不属于TCP协议拥塞控制部分的是? C
A. 快速重传
B. 慢启动
C. 带外数据
D. 快速恢复

解析: TCP的拥塞控制由4个核心算法组成:“慢启动”(Slow Start)、“拥塞避免”(Congestion voidance)、“快速重传 ”(Fast Retransmit)、“快速恢复”(Fast Recovery)

14、当按键盘A时,使用onKeyDown事件打印 event.keyCode 的结果是?A
A. 65
B. 13
C. 97
D. 37
15、下列()不属于CSS文本属性 D
A. font-size
B. text-transform
C. text-align
D. line-height
16、下列哪个CSS属性能够更改文本字体:font-family
17、下列哪个CSS属性能够设置盒子模型的填充为 10、20、30、40(顺时针方向)A
A. padding: 10px 20px 30px 40px
B. padding: 40px 30px 20px 10px
C. padding: 10px 40px 30px 20px
D. padding: 20px 10px 40px 30px
18、关于float描述错误的是?B
A. float:left
B. float:center
C. float:right
D. float:none
19、position:absolute 是相对谁定位()
A. body
B. 父元素
C. 拒收
D. 离它最近的一个已定位的父级元素(默认是body)
20、CSS解析顺序:例:div>p>span ()B
A. 从左向右解析
B. 从右往左解析
解析:

#taya div.ty span{ color:red }

读取顺序:span => div class:ty => id:taya

21、选择器的优先级()A
A. !important > 行内样式 > id 选择器 > 类选择器 > 标签选择器 > 通配符 > 继承
B. !important > id选择器 > 行内样式 > 类选择器 > 标签选择器 > 通配符 > 继承
22、在HTML中,下面关于 css 样式的说法正确的是()
A. CSS 代码严格区分大小写
B. 每条样式规则使用逗号隔开
C. CSS 样式无法实现页面的精确控制
D. CSS样式实现了内容与样式的分离,利于团队开发
23、position 的值,absolute 的含义是什么?
A. 生成绝对定位的元素,通常相对于浏览器窗口或 frame 进行定位
B. 生成绝对定位的元素,相对于最近一级的定位不是 static 的元素来进行定位
C. 生成相对定位的元素,相对于其在普通流中的位置进行定位
D. 没有定位,元素出现在正常流中
24、下列属性哪一个能够实现层的隐藏?C
A. display: fals
B. display:hidden
C. display:none
D. display:“”
25、下列的哪一个表达式将返回假?B
A. !(3 <= 1)
B. (4 >= 4) && (5 <= 2)
C. (“a” == “a”) && (“c” != “d”)
D. (2 < 3) || (3 < 2)
26、indexOf() 如果没有匹配项,返回?B
A. null
B. -1
27、js trim() 去除空格()B
A. 去除中间空格
B. 去除两端空格
C. 去除中间空格和两端空格
28、以下代码输入什么?C

console.log(1)
setTimeout(function(){
	console.log(2)
},0)
console.log(3)

A. 1 2 3
B. 2 1 3
C. 1 3 2

29、语句“var x = 0; while(____) x+=2;”,要使while循环执行10次,空白处的循环判定应为 C
A. x < 10
B. x <= 10
C. x < 20
D. x <= 20

30、下面的代码会输出什么结果()B

var a = 10;
var b = 20;
alert("a+b="+a+b)

A. a+b=30
B. a+b=1020
C. a+b=a+b

31、产生当前日期的方法是()C
A. Now();
B. Date()
C. new Date()
D. new Now()
解析:
BC都可以,只不过B产生的是字符串,C产生的是对象。
在这里插入图片描述
32、在 JavaScript 语言中,当元素失去了焦点激发的事件是()D
A. Focus
B. UnLoad
C. MouseOver
D. Blur
33、分析下面的代码段

function add(){
	var count = 0;
	return function(){
		count += 1;
		alert(count)
	}
}
var s = add();
s();
s();

结果会输出什么()B
A. 1 1
B. 1 2
C. 1 undefined
D. undefined undefined

34、分析下面的代码段

var foo = 1; 
(function(){
	console.log(foo);
	var foo = 2;
	console.log(foo);
}())

结果会输出什么?A
A. undefined 和 2
B. 1 和 2
C. 1 和 undefined
D. 2 和 1

35、分析下面的代码块

var b = 2;
function test2(){
	window.b = 3;
	console.log(b);
}
test2();

结果会输出什么()C
A. undefined
B. 2
C. 3
D. null

36、[“1”,“2”,“3”].map(parseInt) 的输出正确的是()A
A. [1,NaN, NaN]
B. [1,2,3]
C. 1
D. undefined

多选题
37、HTML5的存储类型有哪些?AB
A. localStorage
B. sessionStorage
C. cookie

38、要动态改变层中内容可以使用的方法有 AB
A. innerHTML
B. innerText
C. 通过设置层的隐藏和显示来实现
D. 通过设置层的样式属性的display属性

39、以下的元素,哪些是块级元素 BD
A. input
B. div
C. select
D. p

40、CSS文本属性中,文本对齐属性的取值有( BCDE
A. auto
B. justify 两端对齐的
C. center
D. right
E. left

41、CSS中,盒模型的属性包括( BCE
A. font
B. margin
C. padding
D. visible
E. border

42、CSS引入的方式有哪些?ABCD
A. 内联
B. 内嵌
C. 外链
D. 导入

43、以下哪些方式能让元素脱离正常文档流( BCD
A. position: relative
B. position: absolute
C. position: fixed
D. float: left

44、v-if 和 v-show 的区别,以下说法正确的是哪个?AD
A. v-if 只有条件为真才会渲染
B. v-show 只有条件为真才会渲染
C. v-if 不管条件真假都会渲染,只是条件为真才会显示
D. v-show 不管条件真假都会渲染,只是条件为真才会显示

45、position 取值有哪几种 ABCD
A. static
B. relative
C. fixed
D. absolute

46、以下哪些HTML标签可以使用v-model指令?BD
A. div
B. input
C. p
D. textarea

(六)
1、列出display的值,说明他们的作用。position的值,relative和absolute定位原点是?
2、什么是闭包,闭包的作用,请
写出一个简单的例子
3、分别写出 arr[0]()arr[1]()arr[2]() 运行结果为? 3 3 3

function F(){
	var arr = [];
	for(i=0;i<3;i++){
		arr[i] = function(){
			return i;
		}
	}
	return arr;
}
var arr = F();
arr[0]();  // 3
arr[1]();  // 3
arr[2]();  // 3

4、下列代码中,alert 弹出的内容是什么?

var a = 1;
function f(){
	function n(){
		//alert(a);
		console.log('值',a)
	}
	var a = 2;
}

5、JS中继承如何实现?
6、什么是MVVM框架?
7、请描述一下 cookie、sessionStorage和localStorage的区别?
8、eval是做什么的?
9、JavaScript中,有一个函数,执行时对象查找时,永远不会去查找原型,这个函数是?
hasOwnProperty
10、说说最近最流行的一些东西吧?常去哪些网站?


补充:
面试前总结:https://zhuanlan.zhihu.com/p/43445809 【重要】

面试题集合
面试题1:https://segmentfault.com/a/1190000020627528?utm_source=tag-newest
面试题2:https://github.com/markyun/My-blog/tree/master/Front-end-Developer-Questions/Questions-and-Answers
面试题3:https://www.cnblogs.com/chengxs/p/8289890.html
面试题4【查看该博客历史】:https://blog.csdn.net/lxcao/article/details/52782771

Vue 面试题:https://www.jianshu.com/p/0bff900aec81

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Windyluna

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值