前端面试题(总结归纳--月更)

204 阅读13分钟

1.原型对象、原型链及其解释

原型对象:在js中,当系统加载构造函数之后,会自动在内存中增加一个对象,这个对象就是原型对象。构造函数和原型对象在内存中表现为相互独立,但两者之间还存在联系,构造函数的prototype是原型对象,而原型对象的constructor是构造函数。
原型链:每一个实例对象上有一个__proto__属性,指向的构造函数的原型对象,构造函数的原型对象也是一个对象,也有__proto__属性,这样一层一层往上找的过程就形成了原型链,原型链顶端为null。

image.png

2.Js的数据类型有哪些

基本数据类型(简单数据类型):string(字符串)、number(数字)、boolean(布尔)、null(空)、undefined(未定义)
引用数据类型(复杂数据类型):object(对象)
ES6新增基本类型:symbol (作唯一标识)
注:原始类型(Primitives):Null Undefined Boolean Number String Symbol 6种

3.this的指向问题

this:上下文,会根据执行环境变化而发生指向的改变

  1. 单独的this,指向window对象alert(this)
  2. 全局函数中的this,指向window对象
function demo() {
alert(this);  // this -> window`
}
demo();
//在严格模式下,this是undefined
  1. 事件里,this指向当前的事件挂载者
  2. 在箭头函数里,this指向上下文环境变量
  3. 定时器、延时器里的this,始终指向window
  4. 在对象的函数里,this指向当前对象

4.改变this指向的方式

call()和apply():两种方法都能改变this指向,很类似,区别主要是第二个以后的参数,call()第一个参数表示要把this指向的新目标,第二个之后的参数其实相当于传参,参数以逗号隔开;apply(): 第一个参数同上,第二个参数接受一个数组,里面也是传参,只是以数组的方式。
bind()

function fn(a,b,c){
    console.log(this,a+b+c); //window
}
let ff = fn.bind('小明',1,2,3); //手动调用一下

区别:call、apply可以自动执行,而bind需要手动调用

5.get与post请求的区别

  1. get比post快 (解释在第六点)
  2. post比get安全(get请求参数会被完整保留在浏览器历史纪录)
  3. get参数通过Url传递,post放在Request body中
  4. get容易被缓存(浏览器主动cache),post不会,除非手动设置
  5. get请求在Url中传送的参数是有长度限制的,而post木有,post传输的大小由服务端控制。
  6. get产生一个tcp数据包,post产生两个tcp数据包(对于get请求,浏览器会把http header和data一并发送出去;对于post请求,浏览器会先发送header,服务器响应100continue,浏览器再发送data)

注:get和post本质上没有区别,都是http协议中的两种发送请求的方法,也就是说get/post都是tcp链接,能做的事情是一样的;并不是所有浏览器都会在post中发送两次包,Firefox就只发送一次;

6.cookie、localStorage、sessionStorage有什么区别?

  1. 与服务器交互
    • cookie是网站为了标识用户身份而存储在用户本地终端上的数据(通常经过加密)
    • cookie始终会在同源http请求头中携带(即使不需要),在浏览器和服务器间来回传递
    • sessionStorage和localStorage不会自动把数据发给服务器,仅在本地保存
  2. 存储大小
    • cookie数据根据不同浏览器限制,大小一般不超过4k
    • sessionStorage和localStorage虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大
  3. 有效时间
    • localStorage存储持久数据,浏览器关闭后数据不丢失,除非主动删除数据
    • sessionStorage 数据在当前浏览器窗口关闭后自动删除
    • cookie设置的cookie过期时间之前一直有效,与浏览器是否关闭无关

7.回流(reflow)和重绘(repaint)

简要:整个在浏览器的渲染过程中(页面初始化,用户行为改变界面样式,动画改变界面样式等)reflow(回流)和repaint(重绘) 会大大影响web性能,尤其是手机页面。因此我们在页面设计的时候要尽量减少reflow和repaint。

  • reflow:例如某个子元素的样式发生改变,直接影响到了其父元素以及网上追溯很多祖先元素(包括兄弟元素),这个时候浏览器都要重新去渲染这个子元素相关联的所有元素的过程称为回流。reflow几乎是无法避免的。现在界面上流行的一些效果,比如树状目录的折叠、展开(实质上是元素的显示与隐藏)等,都将引起浏览器的回流;鼠标滑过、点击...只要这些行为引起了某些元素的占位面积、定位方式、边距等属性的变化时,都会引起它内部、周围甚至整个页面的重新渲染。

  • repaint:如果只是改变某个元素的背景色、文 字颜色、边框颜色等等不影响它周围或内部布局的属性,将只会引起浏览器 repaint(重绘)。repaint 的速度明显快于 reflow

8.什么是闭包?手写一个闭包函数?闭包有哪些优缺点

闭包(closure)指有权访问另外一个函数作用域中变量的函数。简单理解就是,一个作用域可以访问另外一个函数内部的局部变量。

function fn() {
    var num = 10;
    function fun() {
        console.log(num);
    }
    return fun;
}
var f = fn();
f();

作用:避免全局污染,延长变量作用域、内部函数可以访问外部函数的变量和参数,容易造成内存泄漏,因为闭包中的局部变量永远不会被回收。

使用场景:封装一些高阶函数的时候能用到,例如柯里化

function fn(a){ 
    return function(b){
        retrun a+b; 
        } 
  } 
  var f=fn(10);
  f(50) //10+50 
  f(20) //10+20

9.Http状态码分别代表什么意思?

  • 1xx 表示HTTP请求已经接受,继续处理请求
  • 2xx 成功,操作被成功接收并处理(200)
  • 3xx 表示把请求访问的URL重定向到其他目录(304资源没有发生变化,会重定向到本地资源)
  • 4xx 表示客户端出现错误(403禁止访问、404资源不存在)
  • 5xx 表示服务端出现错误 具体状态码可查看www.runoob.com/http/http-s…

10.什么是Ajax?

  • 与服务器进行通信的一种技术,它能够实现同步或异步的局部刷新
  • ajax的五个步骤:
    1. 创建ajax的核心对象 XMLHttpRequest
    2. 调用open方法,准备请求参数
    3. 调用send方法,发送请求
    4. 异步使用 onreadystatechange()和readyState的状态,去读数据;同步直接判断status状态码为200
    5. xhr.response响应回数据

image.png 如果需要像HTML表单那样POST数据,则使用setRequestHeader()来添加请求头,然后在send()方法中规定您希望发送的数据

xmlhttp.open("POST","/try/ajax/demo_post2.php",true);
xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded"); 
xmlhttp.send("fname=Henry&lname=Ford");

简单实例

function loadXMLDoc()
{
	var xmlhttp;
	if (window.XMLHttpRequest)
	{
		//  IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码
		xmlhttp=new XMLHttpRequest();
	}
	else
	{
		// IE6, IE5 浏览器执行代码
		xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
	}
	xmlhttp.onreadystatechange=function()
	{
		if (xmlhttp.readyState==4 && xmlhttp.status==200)
		{
			document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
		}
	}
	xmlhttp.open("GET","/try/ajax/ajax_info.txt",true);
	xmlhttp.send();
}

详情请参考:www.runoob.com/ajax/ajax-x…

11.ES5新增了哪些特性

  1. ES5的严格模式'use strict',给作者提供了选择一个限制性更强语言变种的方式
    特点:消除不安全之处,保证安全运行;提升编译效率;未来发展趋势。
    规则:
    • 变量先定义再使用
    • 不允许变量重名
    • 不允许使用eval
    • 不允许delete
    • 不允许with语句
  2. JSON,新增了JSON.parse、JSON.stringify
  3. 对象新增方法
// 添加或更改对象属性
Object.defineProperty(object, property, descriptor)
// 添加或更改多个对象属性
Object.defineProperties(object, descriptors)
// 访问属性
Object.getOwnPropertyDescriptor(object, property)
// 以数组返回所有属性
Object.getOwnPropertyNames(object)
// 以数组返回所有可枚举的属性
Object.keys(object)
// 访问原型
Object.getPrototypeOf(object)
// 阻止向对象添加属性
Object.preventExtensions(object)
// 如果可将属性添加到对象,则返回 true
Object.isExtensible(object)
// 防止更改对象属性(而不是值)
Object.seal(object)
// 如果对象被密封,则返回 true
Object.isSealed(object)
// 防止对对象进行任何更改
Object.freeze(object)
// 如果对象被冻结,则返回 true
Object.isFrozen(object)

4.新增数组方法

  • Array.prototype.indexOf
//跟字符串查找一样,查找指定元素是否存在,如果存在,返回下标,如果不存在返回-1 
let arr = [1,2,3,4]; 
console.log(arr.indexOf(3)); // 2
  • Array.prototype.every
let arr = [
	{name:"yang", age:18, like:"sport"},
	{name:"yang", age:38, like:"eat"}
]

let result = arr.every(function(value, index, arr){
	return value.name == "yang";
});
console.log(result)		// ture
  • Array.prototype.some
//some 查找数组中是否有满足条件的元素 
 var arr = [10304];
 var flag = arr.some(function(value,index,array) {
     //参数一是:数组元素
     //参数二是:数组元素的索引
     //参数三是:当前的数组
     return value < 3;
  });
console.log(flag);//返回值是布尔值,只要查找到满足条件的一个元素就立马终止循环
  • Array.prototype.forEach
 arr.forEach(function(value, index, array) {
     //参数一是:数组元素
     //参数二是:数组元素的索引
     //参数三是:当前的数组
 }) 
 //相当于数组遍历的 for循环 没有返回值
  • Array.prototype.map
//map()方法返回一个被操作后的新数组,不会改变原数组
//map()一般配合return使用,如果没有return那跟forEach一样;
示例:
var  arr = [
	{name:"yang", age:18, like:"sport"},
	{name:"wang", age:28, like:"sleep"},
	{name:"ling", age:38, like:"eat"}
]
var  newArr = arr.map(function(value, index, arr){
	var  json = {};
	json.n = value.name;
	json.a = value.age;
	json.l = value.like;
	return json;
})
console.log(newArr);

/* 输出:
[
	{n: "yang", a: 18, l: "sport"}
	{n: "wang", a: 28, l: "sleep"}
	{n: "ling", a: 38, l: "eat"}
]
*/
  • Array.prototype.filter
  var arr = [126648837];
  var newArr = arr.filter(function(value, index,array) { 
  	 //参数一是:数组元素
     //参数二是:数组元素的索引
     //参数三是:当前的数组
     return value >= 20;
  });
  console.log(newArr);//[66,88] //返回值是一个新数组

5.新增bind(),手动调用,改变this的指向

12.ES6新增了哪些特性

  1. 新增let、const
    let 表示申明变量;const 表示申明常量。
    • 常量定义了就不能改了。对象除外,因为对象指向的地址没变。
    • const在申明是必须被赋值。
    • 两者都为块级作用域。 块级作用域与函数作用域。任何一对花括号({和})中的语句集都属于一个块,在这之中定义的所有变量在代码块外都是不可见的,我们称之为块级作用域。函数作用域就好理解了,定义在函数中的参数和变量在函数外部是不可见的。
  2. 模块字符串``
    • 可以使用反引号``来进行字符拼接。并且可以在里面使用${}来包裹一个变量或者表达式
  3. 解构
    有数组解构和对象解构,具体请参考www.runoob.com/w3cnote/dec…
  4. 函数的默认参数
    函数传参可以有默认值
function printText(text = 'default') {
    console.log(text); 
}
  1. 拓展操作符...
    简单理解就是:对象中的扩展运算符(...)用于取出参数对象中的所有可遍历属性,拷贝到当前对象之中
    示例:
//es5
let book1 = ['平凡的世界第一部', '平凡的世界第二部', '平凡的世界第三部']
let book2 = ['人生']
let book3 = book1.concat(book2);
//console.log(book3)
// ["平凡的世界第一部", "平凡的世界第二部", "平凡的世界第三部", "人生"]

//es6
let book4 = [...book1, ...book2];
book4[3] = '月夜静悄悄'
console.log(book4) 
//["平凡的世界第一部", "平凡的世界第二部", "平凡的世界第三部", "月夜静悄悄"]
  1. 箭头函数
var fn = () => { }

注:箭头函数没有this,this始终指向函数申明时所在作用域下的this值

  1. 新增class类
    ES6 中支持 class 语法,不过,ES6的class不是新的对象继承模型,它只是原型链的语法糖表现形式。
class Student {
    constructor() {
        console.log("I'm a student."); 
    } 
    study() { 
        console.log('study!');
    } 
    static read() {
        console.log("Reading Now.");
    } 
} 
console.log(typeof Student);
// function let stu = new Student();
// "I'm a student." stu.study(); 
// "study!" stu.read();
// "Reading Now."
  1. 模块化导入导出
    • 导入import
    • 导出export default
  2. Promise对象
    概述:是异步编程的一种解决方案,可以解决回调地狱问题
new Promise((resolve,reject) => {
    setTimeout(function() { 
        resolve('成功了!') 
    },1000) 
    // reject("失败了,wuwu")
    }).then(data => {
        console.log(data) 
    }).catch(err => { 
        console.log(err) 
    })
  1. async函数
    async是ES7才有的异步操作有关的关键字,能比promise更好的解决回调地狱
async function() { 
    awiat fn() 
}
  1. Symbol
    新增一种原始数据类型,表示独一无二的值,最大的用法是用来定义对象的唯一属性名

  2. Map对象和Set对象
    详情见www.runoob.com/w3cnote/es6…

  3. Reflect与Proxy
    详情见www.runoob.com/w3cnote/es6…

  4. 字符串、数组、对象的增强
    详情见es6.ruanyifeng.com/#docs/strin…

13.什么是回调函数

把一个函数当前另外一个函数的参数,在另外一个函数内部,被执行和传递参数
好处:

  • 解决异步
  • 对函数的功能扩展 缺点:
  • 容易造成回调地狱, 回调函数调用回调函数
    如何解决呢?
    使用promise,它是一个构造函数,它需要传入一个函数,函数里有成功和失败的回调函数 promise有3个状态
  • 等待 pending
  • 成功 resolve
  • 失败 reject
  • 等待-->成功
  • 等待-->失败 不可逆
    原型方法
  • then()
  • catch()
  • finally()

静态方法

  • all() 并发
  • rece() 谁先完成,取谁的结果
  • reoslve() 返回是一个promsie对象 所有成功的
  • reject() 返回是一个promsie对象所有失败的

promsie能结束回调地狱,但是代码量有冗余,就结合ES7 async+await await后面必须接promise对象 有await的地方,一定是一个 异步函数

14.对前端路由的理解?前后端路由的区别

1. 什么是路由?

  • 路由是根据不同的url地址展示不同内容或页面

2. 什么是前端路由?

  • 很重要的一点是页面不刷新,前端路由就是把不同路由对应不同的内容或页面的任务交给前端来做,每跳转到不同的URL都是使用前端的锚点路由. 随着(SPA)单页应用的不断普及,前后端开发分离,目前项目基本都使用前端路由,在项目使用期间页面不会重新加载。

3. 什么是后端路由?

  • 浏览器在地址栏中切换不同的url时,每次都向后台服务器发出请求,服务器响应请求,在后台拼接html文件传给前端显示, 返回不同的页面, 意味着浏览器会刷新页面,网速慢的话说不定屏幕全白再有新内容。后端路由的另外一个极大的问题就是 前后端不分离。

4. 前端路由的优缺点

优点:

  • 用户体验好,和后台网速没有关系,不需要每次都从服务器全部获取,快速展现给用户
  • 可以再浏览器中输入指定想要访问的url路径地址。
  • 实现了前后端的分离,方便开发。有很多框架都带有路由功能模块 缺点:
  • 使用浏览器的前进,后退键的时候会重新发送请求,没有合理地利用缓存
  • 单页面无法记住之前滚动的位置,无法在前进,后退的时候记住滚动的位置

5. 后端路由的优缺点

优点:

  • 分担了前端的压力,html和数据的拼接都是由服务器完成。 缺点:
  • 当项目十分庞大时,加大了服务器端的压力,同时在浏览器端不能输入制定的url路径进行指定模块的访问
  • 另外一个就是如果当前网速过慢,那将会延迟页面的加载,对用户体验不是很友好