前端面试题总结

Posted 煜成'Studio

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了前端面试题总结相关的知识,希望对你有一定的参考价值。

4以下对 Ajax 描述不正确的是( A )

A.readyState 属性请求的状态值,当值为 3 时是正在加载。

B.使用 XML 和 XSLT 进⾏行数据交换及相关操作。

C.总共有 8 种 callback(onSuccess onFailure onUninitialized onloading onloaded onInteractive onComplete onException)

D.abort()⽅法,停⽌当前请求

XML,Extensible Markup Language,可扩展标记语言,是一种用于标记电子文件使其具有结构性的标记语言。

XSLT,EXtensible Stylesheet Language,可扩展样式表转换语言,是一种样式转换标记语言,可以将XML数据档转换为另外的XML或其它格式,如html网页,纯文字。

readyState表示xhr对象的请求状态,取值范围是0——4,分别表示5个不同的状态。
0:(未初始化)xhr对象已经创建,但还没有调用open()方法。值为0表示对象已经存在,否则浏览器会报错:对象不存在。
1 :(载入/发送请求)调用open()方法对xhr对象进行初始化,根据参数(method,url,true),完成对象状态的设置。并调用send()方法开始向服务端发送请求。值为1表示正在向服务端发送请求。
2 :(载入完成/响应接收)接收服务器端响应回的数据。但获得的还只是服务端响应的原始数据,并不能直接在客户端使用。值为2表示send()请求方法执行完成,并已经接收完全部的响应数据(未解析)。
3 - (交互/解析数据)正在解析从服务器端接收到的响应数据。即根据服务器端响应头部返回的MIME类型把数据转换成能通过responseBody、responseText或responseXML属性存取的格式,为在客户端调用作好准备。值为3表示正在解析数据。
4 - (后台处理完成)响应内容解析完成,可以在客户端调用了。此阶段确认全部数据都已经解析为客户端可用的格式,解析已经完成。值为4表示数据解析完毕,可以通过XMLHttpRequest对象的相应属性取得数据。

总之,整个XMLHttpRequest对象的生命周期应该包含如下阶段:
创建-0初始化请求-1发送请求-2接收数据-3解析数据-4完成 。

Ajax原理

Ajax 的原理简单来说是在⽤户和服务器之间加了—个中间层( AJAX 引擎),通过XmlHttpRequest 对象来向服务器发异步请求,从服务器获得数据,然后⽤ javascript来操作 DOM ⽽更新⻚⾯。使⽤户操作与服务器响应异步化。这其中最关键的⼀步就是从服务器获得请求数据
Ajax 的过程只涉及 JavaScript 、 XMLHttpRequest 和 DOM 。 XMLHttpRequest 是ajax的核⼼机制

/** 1. 创建连接 **/
var xhr = null;
xhr = XMLHTTPRequest ? new XMLHttpRequest() : new window.ActiveXObject('Microsoft.XMLHTTP');
/** 2. 连接服务器 **/
xhr.open('get', url, true)
/** 3. 发送请求 **/
xhr.send(null);
/** 4. 接受请求 **/
xhr.onreadystatechange = function(){
    if(xhr.readyState == 4){
        if(xhr.status == 200){
        	success(xhr.responseText);
        } else {
            /** false **/
            fail && fail(xhr.status);
        }
    }
}

ajax 有那些优缺点?
优点:
通过异步模式,提升了⽤户体验.
优化了浏览器和服务器之间的传输,减少不必要的数据往返,减少了带宽占⽤.
Ajax 在客户端运⾏,承担了⼀部分本来由服务器承担的⼯作,减少了⼤⽤户量下的服务器负载。
Ajax 可以实现动态不刷新(局部刷新)
缺点:
安全问题 AJAX 暴露了与服务器交互的细节。
对搜索引擎的⽀持⽐较弱。
不容易调试。

5typeOf(null)//Object

null是一个只有一个值的特殊类型。表示一个空对象引用。

6alert(null instanceof Object); 返回值为false,( null表示为空的引用;instanceof 表示某个变量是否是某个对象的实例)

8HTTP的GET和POST有什么区别?

9HTTP状态码200、302、401、404、500分别代表什么意思?

10一个页面上有大量的图片(大型电商网站),加载很慢,你有哪些图片优化这些图片的加载,给用户更好

图⽚懒加载,在⻚⾯上的未可视区域可以添加⼀个滚动事件,判断图⽚位置与浏览器顶端的距离与⻚⾯的距离,如果前者⼩于后者,优先加载。
如果为幻灯⽚、相册等,可以使⽤图⽚预加载技术,将当前展示图⽚的前⼀张和后⼀张优先下载。
如果图⽚为css图⽚,可以使⽤ CSSsprite , SVGsprite , Iconfont 、 Base64 等技术。
如果图⽚过⼤,可以使⽤特殊编码的图⽚,加载时会先加载⼀张压缩的特别厉害的缩略图,以提⾼⽤户体验。
如果图⽚展示区域⼩于图⽚的真实⼤⼩,则因在服务器端根据业务需要先⾏进⾏图⽚压缩,图⽚压缩后⼤⼩与展示⼀致。

14base64的原理及优缺点

优点:可以加密,减少了 HTTP 请求
缺点:是需要消耗 CPU 进⾏编解码

11说一说前端性能优化有哪些方法

12Vue有哪些生命周期函数?

15关于类型转换,下列说法错误的有?以下三个都是错的

A null + 1 === 1 B undefined + 1 === 1 C !“0” == false

undefined + 1 === NaN,undefined将它强制转换成数值会返回NaN

16请完成代码,点击按钮“button”后,以弹窗的方式显示按钮“button”的值

<input type=“button” id=“text” value=“点击一下” />

oText.onclick = function (e) {

​ alert(e.target.value);

}

17完成function a()代码,实现点击“这是第N条”列表项的时候alert n (n >= 0 && n <= 2)

<script type = "text/javascript">
    function a () {
        //补充代码
        var oLi = document.getElementByTagName("li");
        for (let i = 0; i < oLi.length; i++) {
        	oLi[i].index = i;
        	oLi[i].onClick = function () {
				alert(this.index + 1);
			}
        }
    }
</script>
//闭包实现
var lis=document.querySelectorAll('li');
for (var i = 0; i < lis.length; i++) {
    var li = lis[i];
    li.onclick=(function(index){
        return function(e){
        	alert(index);
        };
    })(i);
}

18请编写一个JavaScript函数parseQueryString,把URL参数解析为一个对象,var url = “http://witmax.cn/index.php?key0=0&key1=1&key2=2”;

function serilizeUrl (url) {
	var result = {};
	url = url.split("?")[1];
	var map = url.split("&");
	for (var i = 0, len = map.length; i < len; i++) {
		result[map[i].split("=")[0]] = map[i].split("=")[1];
	}
	return result;
}

21有哪些方法可以解决跨域?三种以上(考虑到兼容性的话用哪种)

*21补 vue和react内部是怎么实现数据的双向绑定的?react钩子的状态的实现???

vue.js 则是采⽤数据劫持结合发布者-订阅者模式的⽅式,通过Object.defineProperty() 来劫持各个属性的 setter , getter ,在数据变动时发布消息给订阅者,触发相应的监听回调

vue objectDefined proxy v-model

React并没有自带的双向绑定,需要自己实现,结合setState()以及onChange事件来实现,每次文本框改变文本(即触发onChange事件),就使用setState()改变state数据

import React, { Component } from "react"
import ReactDOM from "react-dom"
 
export default class DataBind extends Component{
    constructor(props){
        super(props)
        this.state = {
            value: "Name"
        }
    }
    handleChange(e){
        this.setState({
            value : e.target.value
        })
    }
    
    render(){
        return(
        <div style={{color:'black'}}>
		 <input value={this.state.value} onChange={this.handleChange.bind(this)}></input>
		 <p>{this.state.value}</p>
		</div>
        )
    }
}

99Vue 和 React 之间的区别

Vue 的表单可以使⽤ v-model ⽀持双向绑定,相⽐于 React 来说开发上更加⽅便,当然了 v-model 其实就是个语法糖,本质上和 React 写表单的⽅式没什么区别

改变数据⽅式不同, Vue 修改状态相⽐来说要简单许多, React 需要使⽤ setState来改变状态,并且使⽤这个 API 也有⼀些坑点。并且 Vue 的底层使⽤了依赖追踪,⻚⾯更新渲染已经是最优的了,但是 React 还是需要⽤户⼿动去优化这⽅⾯的问题。

React 16 以后,有些钩⼦函数会执⾏多次,这是因为引⼊ Fiber 的原因。React 需要使⽤ JSX ,有⼀定的上⼿成本,并且需要⼀整套的⼯具链⽀持,但是完全可以通过 JS 来控制⻚⾯,更加的灵活。 Vue 使⽤了模板语法,相⽐于 JSX 来说没有那么灵活,但是完全可以脱离⼯具链,通过直接编写 render 函数就能在浏览器中运⾏。

在⽣态上来说,两者其实没多⼤的差距,当然 React 的⽤户是远远⾼于 Vue 的

102发布-订阅模式

发布-订阅模式也叫做观察者模式。通过⼀对⼀或者⼀对多的依赖关系,当对象发⽣改变时,订阅⽅都会收到通知。在现实⽣活中,也有很多类似场景,⽐如我需要在购物⽹站上购买⼀个产品,但是发现该产品⽬前处于缺货状态,这时候我可以点击有货通知的按钮,让⽹站在产品有货的时候通过短信通知我。在实际代码中其实发布-订阅模式也很常⻅,⽐如我们点击⼀个按钮触发了点击事件就是使⽤了该模式

vue数据双向绑定也用到了发布订阅模式

观察者模式和发布订阅模式最大的区别就是发布订阅模式有个**事件调度中心 **

观察者模式中观察者和目标直接进行交互,而发布订阅模式中统一由调度中心进行处理,订阅者和发布者互不干扰。这样一方面实现了解耦,还有就是可以实现更细粒度的一些控制。比如发布者发布了很多消息,但是不想所有的订阅者都接收到,就可以在调度中心做一些处理,类似于权限控制之类的。还可以做一些节流操作。

22函数的哪些原生方法可以改变其执行的上下文 call apply bind

25 ES6新增了哪些特性至少五个

1.新增了块级作用域(let,const)

2.提供了定义类的语法糖(class)

3.新增了一种基本数据类型(Symbol)

4.新增了变量的解构赋值

5.函数参数允许设置默认值,新增了箭头函数。

6.数组新增了一些API,如isArray / from / of 方法;数组实例新增了 entries(),keys() 和 values() 等方法。

7.对象和数组新增了扩展运算符

8.ES6新增了模块化(import / export)

9.ES6新增了Set和Map数据结构。

10.ES6原生提供Proxy构造函数,用来生成Proxy实例

11.ES6新增了生成器(Generator)和遍历器(Iterator)

26浅拷贝

// 1. ...实现
let copy1 = {...{x:1}}

// 2. Object.assign实现

let copy2 = Object.assign({}, {x:1})

深拷贝(遍历一个对象用for in)

deepClone = (initalObj) => {
    const obj = {};
    if(typeof initalObj !== 'object'){
    	return initalObj
    }
    for (const key in initalObj) {
        if (typeof initalObj[key] === 'object') {
            //对数组特殊处理
            if (Array.isArray(initalObj[key])) {
                //用map方法返回新数组,将数组中的元素递归
                obj[key] = initalObj[key].map(item => deepClone(item))
            } else {
            //递归返回新的对象
            	obj[key] = deepClone(initalObj[key]);
            }
        } else if (typeof initalObj[key] === 'function') {
            //返回新函数
            obj[key] = initalObj[key].bind(obj);
        } else {
            //基本类型直接返回
            obj[key] = initalObj[key];
        }
    }
    return obj;
}
//
JSON.parse(JSON.stringify(obj))

111扁平化数组

//ES6
var arr = [1, [2, [3]], 4, [5]];
arr.flat(Infinity);
//ES5第一种
function flatArr (arr) {	
    var resultArr = [];	
    arr.forEach(function (item) { 
        //数组遍历最好用forEach		
        var str = Object.prototype.toString.call(item);		
        if (str.indexOf("Array") !== -1) {			
        	resultArr = resultArr.concat(flatArr(item));		
        }else {			
        	resultArr = resultArr.concat(item);		
        }	
    });	
    return resultArr;
}
//迭代
//对于把递归转换成迭代,很多情况下都需要一个stack来模拟函数调用。先看代码:
function flatten2(arr) {
  const stack = [...arr];
  const res = [];
  while (stack.length) {
    // 从栈里取出
    const next = stack.pop();
    if (Array.isArray(next)) {
      // 把next扁平化,然后放入stack中
      stack.push(...next);
    } else {
      res.push(next);
    }
  }
  // reverse to restore input order
  return res.reverse();
}
console.log(flatten2(arr))
递归是重复调用函数自身实现循环。迭代是函数内某段代码实现循环,循环代码中参与运算的变量同时是保存结果的变量,当前保存的结果作为下一次循环计算的初始值。
递归循环中,遇到满足终止条件的情况时逐层返回来结束。迭代则使用计数器结束循环。当然很多情况都是多种循环混合采用,这要根据具体需求。
结构不同:递归与迭代都是基于控制结构:迭代用重复结构,而递归用选择结构。
//第二种
var tempArr = arr.toString().split(","); 
//arr.toString()的结果是"1, 2, 3, 4, 5",
//arr.toString().split(",")结果为["1", "2", "3", "4", "5"]
var resultArr = tempArr.map(function (item) {	
	return parseInt(item);
});
console.log(resultArr);
//第三种
var tempArr = arr.join().split(",");
var resultArr = tempArr.map(function (item) {	
	return parseInt(item);
});
console.log(resultArr);

27请基于vue框架,实现一个父组件调用子组件方法的示例

方案一:通过ref直接调用子组件的方法;

//父组件中
<template>
    <div>
        <Button @click="handleClick">点击调用子组件方法</Button>
        <Child ref="child"/>
    </div>
</template>    

<script>
import Child from './child';
export default {
    methods: {
        handleClick() {
              this.$refs.child.sing();
        },
    },
}
</script>
//子组件中
<template>
  <div>我是子组件</div>
</template>
<script>
export default {
  methods: {
    sing() {
      console.log('我是子组件的方法');
    },
  },
};
</script>

方案二:通过组件的 e m i t 、 emit、 emiton方法;

//父组件中<template>    <div>        <Button @click="handleClick">点击调用子组件方法</Button>        <Child ref="child"/>    </div></template>    <script>import Child from './child';export default {    methods: {        handleClick() {               this.$refs.child.$emit("childmethod")    //子组件$on中的名字        },    },}</script>//子组件中<template>    <div>我是子组件</div></template><script>export default {    mounted() {        this.$nextTick(function() {            this.$on('childmethods', function() {                console.log('我是子组件方法');            });        });     },};</script>

至于项目中的$emit,是父子组件间传值

29请实现对数组[{a:2},{a:1},{a:0},{a:10},{a:2.1}]按属性a从小到大排序。

js实现二维数组去重

const removeRepeat1 = (arr) => {
  const obj={};
  return arr.filter(item=>{
    if(!obj[item.toString()]) {
      obj[item.toString()]=item.toString();
      return true;
    }
  });
}

数组去重⽅法总结

⽅法⼀、利⽤ES6 Set去重(ES6中最常⽤)
function unique (arr) {
	return Array.from(new Set(arr))
}
或者[...new Set(arr)]
⽅法⼆、利⽤for嵌套for,然后splice去重(ES5中最常⽤)
function unique(arr){ 
    for(var i=0; i<arr.length; i++){
        for(var j=i+1; j<arr.length; j++){
            if(arr[i]==arr[j]){ //第⼀个等同于第⼆个,splice⽅法删除
                arr.splice(j,1);
                j--;
            }
        }
    }
    return arr;
}
⽅法三、利⽤indexOf去重
var array = [];
for (var i = 0; i < arr.length; i++) {
    if (array .indexOf(arr[i]) === -1) {
    	array .push(arr[i])
    }
    return array;
}
⽅法四、利⽤includes
var array =[];
for(var i = 0; i < arr.length; i++) {
    if( !array.includes( arr[i]) ) {//includes 检测数组是否有某个值
    	array.push(arr[i]);
    }
    return array
}
⽅法五、利⽤filter
function unique(arr) {
    return arr.filter(function(item, index, arr) {
    	//当前元素,在原始数组中的第⼀个索引==当前索引值,否则返回当前元素
    	return arr.indexOf(item, 0) === index;
    });
}

32使用setTimeout模拟实现一个setInterval的功能

setInterval = () =>{	    console.log(1)     //使用递归	    setTimeout(setInterval,1000);};setInterval()

34请写出React16的新特性,至少五个

render方法新增返回类型

render方法支持直接返回string,number,boolean,null,portal,以及fragments(带有key属性的数组),这可以在一定程度上减少页面的DOM层级

新的组件生命周期钩子

static getDerivedStateFromProps(nextProps, prevState)

componentDidCatch(error, info)

如果错误在组件的渲染或者生命周期方法中被抛出,则会触发该函数。

性能方面

lazy / Suspense

React.lazy() 提供了动态 import 组件的能力,实现代码分割。

Suspense 作用是在等待组件时 suspend(暂停)渲染,并显示加载标识。

React.Fragments

这样并不会在DOM中增加额外节点,相当于 render 返回数组元素。

35请简述display: none和visibility: hidden对比的优缺点

36介绍下vuex以及应用的场景***

Vuex 是一个专为 Vue.js应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 也集成到Vue 的官方调试工具 devtools extension ,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。当我们构建中大型SPA(单页面应用)项目时,Vuex则从中具有非常大的作用。

Vuex的应用场景

涉及到非父子组件之间的传值,例如兄弟关系、祖孙关系,甚至更远的关系组件之间的联系,如果使用传统父子组件之间传值的方式时,对开发人员来说可能是噩梦;

中大型单页应用,考虑如何更好地在组件外部管理状态;

39闭包是什么,有什么特性,请简单书写一个简单实例

特性:闭包是能够读取其他函数内部变量的函数,即在外面可以调用函数中的函数的变量,其实他就是将函数内外部连接起来的桥梁

//事例:<script type='text/javascript'>    function a(){            var i = 99;            iAdd = function(){            	i++;    	    }            function b(){                	alert(i);            }    	    return b;    }    var result = a();    result();//结果为99    iAdd();    result();//结果为100</script>

40ES5 的继承和 ES6 的继承有什么区别 ?

ES5 的继承时通过 prototype 或构造函数机制来实现。ES5 的继承实质上是先创建子类的实例对象,然后再将父类的方法添加到 this 上(Parent.apply(this))。ES6 的继承机制完全不同,实质上是先创建父类的实例对象 this(所以必须先调用父类的 super()方法),然后再用子类的构造函数修改this。具体的:ES6 通过 class 关键字定义类,里面有构造方法,类之间通过 extends 关键字实现继承。子类必须在 constructor 方法中调用 super 方法,否则新建实例报错。因为子类没有自己的 this 对象,而是继承了父类的 this 对象,然后对其进行加工。如果不调用 super 方法,子类得不到 this 对象。ps:super 关键字指代父类的实例,即父类的 this 对象。在子类构造函数中,调用 super 后,才可使用 this 关键字,否则报错。

107vue-router

方式 hash history

跳转 this.$router.push() <router-link to=""></router-link>

占位 <router-view></router-view>

84动态路由hash history区别

https://www.cnblogs.com/LazyPet/p/12170995.html

前端路由原理?两种实现⽅式有什么区别?

前端路由实现起来其实很简单,本质就是监听 URL 的变化,然后匹配路由规则,显示相应的⻚⾯,并且⽆须刷新⻚⾯。⽬前前端使⽤的路由就只有两种实现⽅式

Hash 模式

www.test.com/#/ 就是 Hash URL ,当 # 后⾯的哈希值发⽣变化时,可以通过 onhashchange 事件来监听到 URL 的变化,从⽽进⾏跳转⻚⾯,并且⽆论哈希值如何变化,服务端接收到的 URL 请求永远是 www.test.com

window.addEventListener(‘hashchange’, () => {

// … 具体逻辑

})

Hash 模式相对来说更简单,并且兼容性也更好

History 模式 History 模式是 HTML5 新推出的功能,主要使⽤ history.pushState和 history.replaceState 改变 URL 通过 History 模式改变 URL 同样不会引起⻚⾯的刷新,只会更新浏览器的历史记录。当⽤户做出浏览器动作时,⽐如点击后退按钮时会触发 popState 事件

两种模式对⽐

Hash 模式只可以更改 # 后⾯的内容, History 模式可以通过 API 设置任意的同源URL

History 模式可以通过 API 添加任意类型的数据到历史记录中, Hash 模式只能更改哈希值,也就是字符串

Hash 模式⽆需后端配置,并且兼容性好。 History 模式在⽤户⼿动输⼊地址或者刷新⻚⾯的时候会发起 URL 请求,后端需要配置 index.html ⻚⾯⽤于匹配不到静态资源的时候

computed 和 watch 区别

computed 是计算属性,依赖其他属性计算值,并且 computed 的值有缓存,只有当计算值变化才会返回内容。

watch 监听到值的变化就会执⾏回调,在回调中可以进⾏⼀些逻辑操作。

所以⼀般来说需要依赖别的属性来动态获得值的时候可以使⽤ computed ,对于监听到值的变化需要做⼀些复杂业务逻辑的情况可以使⽤ watch 。

另外 computer 和 watch 还都⽀持对象的写法,这种⽅式知道的⼈并不多。

keep-alive 组件有什么作⽤

如果你需要在组件切换的时候,保存⼀些组件的状态防⽌多次渲染,就可以使⽤ keepalive 组件包裹需要保存的组件。

对于 keep-alive 组件来说,它拥有两个独有的⽣命周期钩⼦函数,分别为 activated和 deactivated 。⽤ keep-alive 包裹的组件在切换时不会进⾏销毁,⽽是缓存到内存中并执⾏ deactivated 钩⼦函数,命中缓存渲染后会执⾏ actived 钩⼦函数。

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

https://blog.csdn.net/m0_48560510/article/details/110354840
进入首页时两次加载一个文档,优化代码当是登录页面跳转过来的才加载,可以通过导航守卫来实现,还可以有三种方法,query传参 params传参和直接路径拼接this.$router.push(’/index?from=login’),说明是从登录页面过来的,带有login的参数

query传参,其实是get传参,在地址栏里会拼接登录地址this.$router.push({ path: '/index', query: {  from: 'login' }})params传参,路径没有进行拼接,但是是由参数的this.$router.push({ name: 'index',不是路径,应该是路由的名称 params: {  from: 'login' }

})
获取参数时用this. r o u t e , route, routeroute是路由实例,而$router是全局路由,整个路由都在里面(11-4)

可以通过queryparam两种方式
区别:query通过url传参,刷新页面还在;params属性页面不在

params的类型:

  1. 配置路由格式:/router/:id

  2. 传递的方式:在path后面跟上对应的值

  3. 传递后形成的路径:/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 获取你说传递的值

    query的类型

    1. 配置路由格式:/router,也就是普通配置
    2. 传递的方式:对象中使用query的key作为传递方式
    3. 传递后形成的路径:/route?id=123
<!--动态路由-query -->//01-直接在router-link 标签上以对象的形式<router-link :to="{path:'/profile',query:{name:'why',age:28,height:188}}">档案</router-link>/*    02-或者写成按钮以点击事件形式    <button @click='profileClick'>我的</button>    */  //点击事件 profileClick(){   this.$router.push({            path: "/profile",            query: {                  name: "kobi",                  age: "28",                  height: 198            }      }); 

跳转方法:

// 方法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.query 获取你所传递的值

44vue-router有哪几种导航钩子,导航钩子的作用是什么?

vue-router提供的导航钩子主要用来拦截导航,让它完成跳转或取消。

导航钩子的分类

  • 全局守卫 简单点说就是触发路由就会触发这些钩子函数。钩子函数执行顺序包括beforeEach、beforeResolve、afterEach三个。
  • 路由独享守卫
  • 局部守卫

93如何配置React-Router

yarn add react-router-dom

在最外面的App.js里

import { BrowserRouter, Route } from ‘react-router-dom’;

在组件的render()里

<BrowserRouter>	<div>		<Route path='/' exact render={()=><div>home</div>}></Route>		<Route path='/detail' exact render={()=><div>detail</div>}></Route>	</div> </BrowserRouter>

代表的是路由说明里面内容要用路由了,它里面只能有一个children

Route代表的是路由规则

exact指只有路径完全相同才显示,否则只要路径包含就显示(8-1)

单页面应用就不能用<a href=’/detail’>…</a>

要使用Link

import { Link } from ‘react-router-dom’;

<Link to=’/detail’>…</Link>

注意:使用Link标签的组件必须放在总的文件<BrowserRouter></BrowserRouter>内部(8-10)

96react-router⾥的<Link>标签和<a>标签有什么区别

对比<a>,Link组件避免了不必要的重渲染 react-router:只更新变化的部分从而减少DOM性能消耗

react的创新之处在于,它利用虚拟DOM的概念和diff算法实现了页面的“按需加载”

42Vue引入了虚拟Dom主要解决了什么问题

43输出结果

var test = (function (a) {	
    this.a = a;	
    return function (b) {		
        return this.a + b;	
    }}(function(a, b) {	
        return a;
}(1, 2)));
console.log(test(4));

输出结果:5(1+4)

(function(a, b) {	
	return a;
}(1, 2))
回的是1,传给atest相当于
test = function (b) {	
	return this.a + b;
}b是4

45vue-loader是什么?使用它的途径有哪些?

一、vue-loader作用:

解析和转换.vue文件。提取出其中的逻辑代码 script,样式代码style,以及HTML 模板template,再分别把他们交给对应的loader去处理

二、用途

js可以写es6,style样式可以写scss或less、template可以加jade等

三、

css-loader:加载由vue-loader提取出的CSS代码

vue-template-compiler:把vue-loader提取出的HTML模板编译成可执行的javascript代码

30请写出删除数组元素的几种原生算法

splice(index,len,[item]) 注释:该方法会改变原始数组。注:concat不会改变原数组,需要有个变量来承接一下。

delete arr[1] delete删除掉数组中的元素后,会把该下标出的值置为undefined,数组的长度不会变

46delete和Vue.delete删除数组的区别

delete只是被删除的元素变成了 empty/undefined 其他的元素的键值还是不变。

Vue.delete 直接删除了数组 改变了数组的键值。

50使用选择排序或冒泡排序方法对数组let arr = [3, 5, 1, 9, 4, 2]进行排序

52介绍一下Vue组件的生命周期

https://www.cnblogs.com/qidh/p/11431998.html

https://www.cnblogs.com/wangjiachen666/p/9497749.html


53使用CSS的flexbox布局,不能实现以下哪一个效果:

A、三列布局,随容器宽度等宽弹性伸缩

B、多列布局,每列的高度按内容最高的一列等高

C、三列布局,左列宽度像素数确定,中、右列随容器宽度等宽弹性伸缩

D、多个宽高不等的元素,实现无缝瀑布流布局

答案:D

54下面关于this的说法错误的是A

A.严格模式下,普通函数中没有直接调用者的函数中this指向window

B.普通函数中this指向它的直接调用者

C.普通函数中使用call绑定的this指向绑定的对象

D.箭头函数中的this指向定义它时所处的宿主对象

A为undefined

55new Date(‘yyyy-mm-dd’)返回本地时间,new Date(‘yyyy/mm/dd’)返回UTC时间

56返回a1 undefined a2

var obj1 = {  a: 'a1',  b: function () {return this.a},  c: function (obj) {    obj = {a : 'a2'};    return this.a;  }}var getFunctionB = obj1.b;var res1 = obj1.b();var res2 = getFunctionB();var res3 = obj1.c(obj1);

57

var dateStr = 'Friday';(function () {  if (typeOf dateStr === 'undefined') {    var dateStr = 'Staurday';    console.log('Hello' + dateStr);  }else {    console.log('Happy' + dateStr);  }})();输出’Hello Staurday’var str = 'World!';    (function (name) {  if (typeof name === 'undefined') {    var name = 'Jack';    console.log('Goodbye ' + name);  } else {    console.log('Hello ' + name);  }})(str);输出Hello World 因为name已经变成函数内局部变量

58ES6内部使用严格相等运算符(===),判断一个位置是否有值。所以,如果一个数组成员不严格等于undefined,默认值是不会生效的。

var [x, y = “b”] = [“a”, undefined];//x = “a”, y = “b”

var [x = 1] = [undefined];//x = 1

var [y = 1] = [null];//y = null

59冒泡排序、选择排序、快速排序

排序算法的稳定性,通俗地讲就是能保证排序前2个相等的数其在序列的前后位置顺序和排序后它们两个的前后位置顺序相同。在简单形式化一下,如果Ai = Aj, Ai原来在位置前,排序后Ai还是要在Aj位置前。

60div嵌套,居中布局有几种方式,各自的适合范围和问题

margin: auto;

flex

绝对定位,反方向平移宽和高的一半

table vertical-align: middle;

20 如何垂直居中⼀个浮动元素***?

/**⽅法⼀:已知元素的⾼宽**/
\\#div1{
 background-color:#6699FF;
 width:200px;
 height:200px;
 position: absolute; //⽗元素需要相对定位
 top: 50%;
 left: 50%;
 margin-top:-100px ; //⼆分之⼀的height,width
 margin-left: -100px;
}
//不知道宽高
  width: 78px;
  height: 78px;
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translateX(-50%) translateY(-50%);
//
display:flex;
justify-content: center;
align-items: center; 
/**⽅法⼆:**/
\\#div1{
 width: 200px;
 height: 200px;
 background-color: #6699FF;
 margin:auto;
 position: absolute; //⽗元素需要相对定位***

如何垂直居中⼀个 <img> ?(⽤更简便的⽅法。)

#container {
	display: table-cell;
	text-align: center;
	verticle-align: middle;
}
水平居中
  • 行内元素: text-align: center
  • 块级元素: margin: 0 auto
  • position:absolute +left:50%+ transform:translateX(-50%)
  • display:flex + justify-content: center
垂直居中
  • 设置line-height 等于height
  • position:absolute +top:50%+ transform:translateY(-50%)
  • display:flex + align-items: center
  • display:table+display:table-cell + vertical-align: middle;

62http返回的状态有几种,403、401、302、304

64输入1返回2,输入2返回1,有几种方法

65自适应布局几种方法,使用范围,左侧200px,右侧自适应

71sort排序

//升序arr.sort((a, b) => {return a-b;})

79不用组件怎么实现轮播图,当鼠标停留在图片上时怎么实现图片轮播不动

可以使用js和无序列表的方式;CSS动画过渡的方式,仔细看这两种实现方式的代码

https://www.jianshu.com/p/bd1f34e7e953

80v-if v-show 区别

v-show 只是在 display: none 和 display: block 之间切换。⽆论初始条件是什么

都会被渲染出来,后⾯只需要切换 CSS , DOM 还是⼀直保留着的。所以总的来说 v-show 在初始渲染时有更⾼的开销,但是切换开销很⼩,更适合于频繁切换的场景。

v-if 的话就得说到 Vue 底层的编译了。当属性初始为 false 时,组件就不会被渲染,直到条件为 true ,并且切换条件时会触发销毁/挂载组件,所以总的来说在切换时开销更⾼,更适合不经常切换的场景。并且基于 v-if 的这种惰性渲染机制,可以在必要的时候才去渲染组件,减少整个⻚⾯的初始渲染开销。

81key作用

85 都有哪些指令

86如何判断类型

87置换元素 哈希 require.js common.js AMD CMD

置换元素是指根据标签和属性来决定元素的具体显示内容,置换元素在其显示中生成了框,这就是有的内嵌元素能够设置宽高的原因。元素img input textarea select object。大多数元素都是不可置换。label p

一个对象在其生命周期内,保持不变就是可哈希的hashable,例如字符串。可改变的就是不可哈希的unhashable,列表

require.js是js的轻量化框架,用于提高代码质量

CommonJS、AMD、CMD是用于JavaScript模块管理的三大规范

CommonJS定义的是模块的同步加载,是一个更偏向于服务器端的规范(也可以在浏览器中使用),主要用于Nodejs,根据CommonJS规范,一个单独的文件就是一个模块,加载模块使用require()方法,该方法读取一个文件并执行,最后返回文件内部的exports对象。

AMD和CMD则是定义模块异步加载适用于浏览器端,都是为了 JavaScript 的模块化开发,(这里说一下为什要有异步加载,因为浏览器如果使用common.js同步加载模块的话,就会导致性能等问题,所以针对这个问题,又出了一个规范,这个规范可以实现异步加载依赖模块)

AMD规范会提前加载依赖模块,AMD规范是通过requireJs 在推广过程中对模块定义的规范化产出。RequireJS 从 2.0 开始,也改成可以延迟执行(根据写法不同,处理方式不同)。推崇依赖前置。AMD 推荐的⻛格通过返回⼀个对象做为模块对象

CMD规范会延迟加载依赖模块, CMD 规范是 SeaJs 在推广过程中对模块定义的规范化产出。推崇依赖就近。 CommonJS 的⻛格通过对module.exports 或 exports 的属性赋值来达到暴露模块对象的⽬的

88对async、await的理解,内部原理

async函数就是generator函数的语法糖。

async函数,就是将generator函数的*换成async,将yield替换成await。

async函数对generator的改进

(1)内置执行器,不需要使用next()手动执行。

(2)await命令后面可以是Promise对象或原始类型的值,yield命令后面只能是Thunk函数或Promise对象。

(3)返回值是Promise。返回非Promise时,async函数会把它包装成Promise返回。(Promise.resolve(value))

作用

异步编程的终极解决方案。

通俗理解

async/await,就是异步编程回调函数写法的替代方法。(使代码以同步方式的写法完成异步操作)函数执行时,一旦遇到await就会返回。等到触发的异步操作完成(并且调用栈清空),再接着执行函数体内后面的语句。

await语句后面的代码,相当于回调函数。(即:await的下一行开始,都视作回调函数的内容)

回调函数会被压入microtask队列,当主线程调用栈被清空时,去microtask队列里取出各个回调函数,逐个执行。

91redux的设计思想 接⼊redux的过程 绑定connect的过程

Redux是什么呢?一个状态管理工具。那是干嘛用的呢?都知道,React可以进行单页应用(SPA)的开发,可以对页面中各个模块进行分割形成组件,而组件之间就避免不了事件的传递或数据的交互,那Redux就是用来对这些组件的状态进行管理的。

https://segmentfault.com/a/1190000008736866

92webpack介绍

WebPack 是⼀个模块打包⼯具,你可以使⽤ WebPack 管理你的模块依赖,并编绎输出模块们所需的静态⽂件。它能够很好地管理、打包 Web 开发中所⽤到的 HTML 、Javascript 、 CSS 以及各种静态⽂件(图⽚、字体等),让开发过程更加⾼效。对于不同类型的资源, webpack 有对应的模块加载器。 webpack 模块打包器会分析模块间的依赖关系,最后⽣成了优化且合并后的静态资源

95promise、async有什么区别

函数前面多了一个async关键字。await关键字只能用在async定义的函数内。async函数会引式返回一个promise,改promise的resolve值就是函数return的值。

简洁:使用async和await明显节约了不少代码,不需要.then,不需要写匿名函数处理promise的resolve的值,不需要定义多余的data变量,还避免了嵌套代码。

async/await让try/catch 可以同时处理同步和异步错误。try/catch不能处理JSON.parse的错误,因为他在promise中。此时需要.catch,这样的错误处理代码非常冗余。并且,在我们的实际生产代码会更加复杂

条件语句

97cookie放哪⾥

设置过期时间失效(只要设置了过期时间cookie就会存储在硬盘里面)

当会话结束时失效,即关闭浏览器窗口(如果没有设置Expires,cookie就会存储在内存里面)

103 es5异步编码的方式

回调函数,这是异步编程最基本的方法。

事件监听,另一种思路是采用事件驱动模式。任务的执行不取决于代码的顺序,而取决于某个事件是否发生。

发布/订阅

Promises对象,Promises 对象是CommonJS 工作组提出的一种规范,目的是为异步编程提供统一接口。

https://cloud.tencent.com/developer/article/1499717

es6

promise generator async

104js常见的内存泄漏

意外的全局变量引起的内存泄露leak=“xxx”;//leak**成为一个全局变量,不会被回收

闭包

没有清理的DOM元素引用

定时器的第一个参数不是函数而是字符串

循环(在两个对象彼此引用且彼此保留时,就会产生一个循环)

子元素存在引起的内存泄露

可⽤ chrome 中的 timeline 进⾏内存标记,可视化查看内存的变化情 况,找出异常点。

怎样避免内存泄露

1)减少不必要的全局变量,或者生命周期较长的对象,及时对无用的数据进行垃圾回收;

2)注意程序逻辑,避免“死循环”之类的 ;

3)避免创建过多的对象 原则:不用了的东西要及时归还。

https://blog.csdn.net/michael8512/article/details/77888000

105前端工程化

模块化 js scss

组件化

规范化 HTML css js规范 命名规范 前后端接口规范

自动化

106函数调用的方式

//方法调用模式var blogInfo={  blogId:123,  blogName:"werwr",  showBlog:function(){alert(this.blogId);}};blogInfo.showBlog();//函数调用模式var myfunc = function(a,b){  return a+b;}alert(myfunc(3,4));//构造器调用模式var myfunc = function(a){  this.a = a;};myfunc.prototype = {  show:function(){alert(this.a);}}var newfunc = new myfunc("123123123");newfunc.show();//apply,call调用模式var myobject={};var sum = function(a,b){  return a+b;};var sum2 = sum.call(myobject,10,30); //var sum2 = sum.apply(myobject,[10,30]); alert(sum2);

108动画的例子,好好看一下,会手写

#div:hover {} //hover的位置

110

var cityData = [{

​ id: 1,

​ name: ‘广东省’,

​ children: [{

​ id: 11,

​ name: ‘深圳’}]

}]

//直接想到用递归来做吧~,先获取最外层的元素,判断id是否相等,不相等就继续判断是否含有子节点,然后继续递归循坏const recursion = (cityData, id) => {    if (!cityData || !cityData.length) {    	return;    }    //先循坏cityData    for (let i = 0; i < cityData.length; i++) {        const childs = cityData[i].children;        if (cityData[i].id === id) {        	result = cityData[i].name;        }        if (childs && childs.length > 0) {        	recursion(childs, id);        }    }      return result;}console.log(recursion(cityData, 122))//灵芝

113vue模板语法底层是怎么实现的

Vue的MVVM模型讲的是 model(数据模型),view(视图、模板),VM-> ViewModel,VM是M和V之间的桥梁

响应式 :vue如何监听到data的每个属性变化?

利用Object.defineProperty定义属性,将data里面的属性代理到vm上

模板解析: vue的模板如何被解析,指令如何处理?

  1. vue里面为什么要解析模板?

这个模板里的html有逻辑,v-for, v-if等等,最终必须解析成html来显示,模板最终必须转换成 JS 代码

因为:

有逻辑(v-if v-for),必须用 JS 才能实现( 图灵完备)

转换为 html 渲染页面,必须用 JS 才能实现

因此,模板最重要转换成一个 JS 函数

  1. render函数

with 用法:with 语句可以方便地用来引用某个特定对象中已有的属性,但是不能用来给对象添加属性。要给对象创建新的属性,必须明确地引用该对象。

render 函数

VUE一般使用template来创建HTML,然后在有的时候,我们需要使用javascript来创建html,这时候我们需要使用render函数。

Vue的实现流程

1、首先模板解析器解析成render函数

2、响应式监听

3、将数据渲染进模板里

4、data属性变化,触发render

114 vue中action和mutation的区别

action是处理异步任务的或者繁琐的同步任务的

mutation是处理同步任务的

115https是怎么加密的

对称加密

非对称加密

对称加密+非对称加密结合

https://blog.csdn.net/qq_29996285/article/details/84284524

116 rem em相关知识(看书)

117url页面流程

浏览器先查找当前的url是否存在缓存,并比较缓存是否过期

没有缓存,浏览器将当前的url发送给DNS服务器解析url对应的IP地址

根据IP地址建立TCP连接

HTTP发起请求

服务器处理请求,浏览器接收HTTP响应

渲染页面,构建DOM树

关闭TCP连接

118 vue中nextTick() process.nextTick

vue中nextTick()方法是将回调函数延迟在下一次dom更新数据后调用,简单的理解是:当数据更新了,在dom中渲染后,自动执行该函数,

process.nextTick()的意思就是定义出一个动作,并且让这个动作在下一个事件轮询的时间点上执行。

function foo() {
    console.error('foo');
}
process.nextTick(foo);
console.error('bar');
//先输出'bar',再输出'foo'

119HTTP / 2.0

因为浏览器会有并发请求限制,在 HTTP / 1.1 时代,每个请求都需要建⽴和断开,消耗了好⼏个 RTT 时间,并且由于 TCP 慢启动的原因,加载体积⼤的⽂件会需要更多的时间
在 HTTP / 2.0 中引⼊了多路复⽤,能够让多个请求使⽤同⼀个 TCP 链接,极⼤的加快了⽹⻚的加载速度。

并且还⽀持 Header 压缩,进⼀步的减少了请求的数据⼤⼩

120查找两个字符串的最长公共子串的JavaScript函数

function findLongestCommonStr(s1, s2) {
    var commonStr = '', L1 = s1.length, L2 = s2.length;
    var shortStr = L1>L2 ? s2 : s1;
    var longStr = L1>L2 ? s1 : s2;
    var strLen = shortStr.length;
    for (let j = strLen; j > 0; j--) {
        for (let i = 0, k = j; i <= strLen - j; i++, k++) {
            commonStr = shortStr.subString(i, k);
            if (longStr.indexOf(commonStr) >= 0) return commonStr;
        }
    }
    return '';
}

121

//主线程直接执行console.log('1');//丢到宏事件队列中setTimeout(function() {    console.log('2');    process.nextTick(function() {        console.log('3');    })    new Promise(function(resolve) {        console.log('4');        resolve();    }).then(function() {        console.log('5')    })})//微事件1process.nextTick(function() {    console.log('6');})//主线程直接执行new Promise(function(resolve) {    console.log('7');    resolve();}).then(function() {    //微事件2    console.log('8')})//丢到宏事件队列中setTimeout(function() {    console.log('9');    process.nextTick(function() {        console.log('10');    })    new Promise(function(resolve) {        console.log('11');        resolve();    }).then(function() {        console.log('12')    })})//1 7 6 8 2 4 3 5 9 11 10 12
async function async1() {  console.log('async1 start')  await async2()  console.log('async1 end')} async function async2() {  console.log('async2')} console.log('script start')setTimeout(function() {  console.log('setTimeout')}, 0) async1();    new Promise( function( resolve ) { console.log('promise1') resolve();} ).then( function() { console.log('promise2')} ) console.log('script end')script startasync1 startasync2promise1script endpromise2async1 endsetTimeout首先,事件循环从宏任务(macrostack)队列开始,这个时候,宏任务队列中,只有一个 script (整体代码)任务。从宏任务队列中取出一个任务来执行。首先执行 console.log('script start'),输出 ‘script start'遇到 setTimeout 把 console.log('setTimeout') 放到 macrotask 队列中执行 aync1() 输出 ‘async1 start' 和 'async2' ,把 console.log('async1 end') 放到 micro 队列中执行到 promise ,输出 'promise1' ,把 console.log('promise2') 放到  micro 队列中执行 console.log('script end'),输出 ‘script end'macrotask 执行完成会执行 microtask ,把 microtask quene 里面的 microtask 全部拿出来一次性执行完,所以会输出 'async1 end' 和 ‘promise2'开始新一轮的事件循环,去除执行一个 macrotask 执行,所以会输出 ‘setTimeout'

122哪些数组方法改变自身数组:splice sort pop push

哪些不改变:concat

124对于实现双向数据绑定,Object.defineProperty()有什么缺点?有没有更好的方法来实现?

Object.defineProperty()有以下2个非常明显的缺点:

(1)无法监听数组的变化。

(2)只能劫持对象的属性,无法劫持一个完整的对象。

对于实现双向数据绑定,更好的方法是使用Proxy对象来实现,如何实现

两种方式都要手撕代码

129定义一个矩形类Rectangle,它是由宽width和长height两个参数构造的,然后在类中定义一个getArea()方法,用来计算矩形的面积。

Class Rectangle {    constructor(width, height) {        this.width = width;        this.height = height;    }    getArea() {        return this.width * this.height;    }}

130vue的作用和核心功能

Vue是渐进式的JavaScript框架

优点:压缩后体积小,效率高(虚拟DOM)、双向绑定、成熟框架

功能:v-bind v-on v-if v-show v-text v-html 组件 双向绑定

132URL URI区别

URI 是统一资源标识符,而 URL 是统一资源定位符。

每个 URL 都是 URI,但不一定每个 URI 都是 URL。这是因为 URI 还包括一个子类,即统一资源名称 (URN),它命名资源但不指定如何定位资源。

133ajax过程,代码

134 email正则表达式

/^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$/

137 UDP和TCP的区别

TCP和UDP都是传输层的协议

UDP提供了不可靠的无连接传输服务;TCP提供了可靠的面向连接的字节流传输协议

138给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那两个整数,并返回他们的数组下标

var towSum = function(nums, target){  for(let i=0, len = nums.length; i< len; i++) {    if(nums.indexOf(target - nums[i]) > -1 ) {      return [i, nums.indexOf(target - nums[i])]    }  }}//var towSum = function(nums, target){  let obj = {};  for(let i=0, len=nums.length; i< len; i++){    let mult = target - nums[i];    if(mult in obj){      return [obj[mult], i];    }    obj[nums[i]] = i;  }}//var towSum = function(nums, target){  for(let i=0, len = nums.length; i<len; i++) {    for(let j = i+1; j < nums.length; j++) {      if(nums[i] + nums[j] == target) {        return [i, j]      }    }  }}

140 react中pureComponent的作用,处理异步操作用什么(react-thunk的作用,dispatch的升级)

对于多个组件的connect和store进行连接,一旦store内部修改了,那么每个组件都会重新渲染,也就是每个组件的render都会重新执行,解决这个问题可以使用之前的shouldComponentUpdate,react内部有PureComponent解决这个问题,只要把之前的引入包括使用Component的地方都改为PureComponent
import React, { PureComponent } from ‘react’;
因为使用了immutable.js这个框架,PureComponent和immutable.js能完美结合,但是如果不使用immutable.js就使用PureComponent的话,那么会遇到一些底层问题的坑,所以如果不使用immutable.js,最好自己写一个shouldCompomentUpdate来进行代码优化,不要使用PureComponent

141 vue中父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted

142 max-age=0

no-cache

如果request headers中,Cache-Control为no-cache。表示不管服务端有没有设置Cache-Control,都必须从重新去获取请求。 浏览器缓存,但是认为是过期缓存

max-age=0

max-age=0表示不管response怎么设置,在重新获取资源之前,先检验ETag/Last-Modified

不管是max-age=0还是no-cache,都会返回304(资源无修改的情况下),no-store才是真正的不进行缓存。

144闭包的好处

1.减少全局变量

//达到每次调用函数add(),a都不断加1

function add(){	var a = 0;	a++;	alert(a);}add();add();//这样做每次调用a都从0开始//这样可以实现累加效果var a = 0;function add() {	a++;	alert(a);}add();add();//但这样做就增加全局变量//改为闭包的方式function 

以上是关于前端面试题总结的主要内容,如果未能解决你的问题,请参考以下文章

Java进阶之光!2021必看-Java高级面试题总结

前端面试题总结

前端面试题总结

经验总结:Java高级工程师面试题-字节跳动,成功跳槽阿里!

前端面试题总结

面试题系列|前端面试题前端高频面试题总结(2021年最新版)