es6+Webpack+nodejs常见面试题总结
1.call() 函数和 apply() 函数的区别和作用?
答:call()函数 改变this 的指向,可以传递多个参数作为参数 立即执行
apply()函数 改变this 的指向,可以传递一个数组作为参数 立即执行
2.箭头函数有哪些特性?
答:第一,箭头函数区别于一般函数:一般函数可以先调用后定义,而箭头函数必须先定义再调用;一般函数有arguments、而箭头函数没有arguments,但可以使用rest参数;(扩展运算符);一般函数可以当作构造函数,而箭头函数不能当作构造函数,因为箭头函数没有自己this; 第二,箭头函数有简写形式:当箭头函数只有一个形参,外边小括号()可以省略;当箭头函数的函数体中只有一条语句,并且该条语句作为函数的返回值,那么return关键字和花括号{}狗可以省略;第三,箭头函数中的this指向:箭头函数中的this指向依赖外层函数中的this指向,与当前调用者无关;第四:箭头函数没有原型属性;不能简单的返回对象字面量;
3.let的应用场景。
答:第一:let定义的变量是块级作用域,它的作用范围在一个{}中;var定义的变量是函数作用域[局部的], 但在if、for等定义的变量是全局的。第二:同一个变量声明的次数:在同一个作用域中同一个变量let只能声明一次,而var可以多次声明;因此,let声明的变量可以避免全局变量被污染。第三:是否有变量预解析:var声明的变量存在变量提升,而let则没有变量提升,let声明的变量要先定义后使用。第四:let有暂时性死区:let声明的变量要先定义后使用。(// Cannot access 'i' before initialization)。示例: 即使上一个作用域有此变量,但是当前作用也申明了,那么变量也必须先申明再使用;不然还是报错,而且不会向上查找;第五:变量的绑定归属:let声明的变量不会绑定到顶层对象(顶层对象可以理解为最大的全局变量,即window对象)上。
4.箭头函数中this的指向。
答:箭头函数中的this指向依赖外层函数中的this指向,与当前调用者无关;
5.我们如何解决函数传参顺序不一致的问题。
答:给函数形参设置默认值,这样就即使没有这个参数也不会造成影响;
6.使用 promise 封装ajax.。
function sendAjax({
url,
type = 'get',
data = {},
dataType = 'json'
// 对象解构参数传递
} = {}) {
// 执行异步代码
return new Promise((resolve, reject) => {
$.ajax({
url,
type,
data,
dataType,
success: function (data) {
// 调用成功
resolve(data);
},
error: function (e) {
// 请求失败
reject(e);
}
});
});
}
// ==========================================
// 所有一级分类
sendAjax({
url: 'http://106.13.114.114:5000/api/firstCategory'
}).then(function (data) {
// console.log(data);
// 传递二级分类ID, 获取下属的三级分类列表
sendAjax({
url: 'http://106.13.114.114:5000/api/secondCategory',
data: {
firstId: data.list[0][0].firstId
}
}).then(function (d) {
// console.log(d);
// 传递三级分类ID, 获取下属的商品数据列表
sendAjax({
url: 'http://106.13.114.114:5000/api/thiredCategory',
data: {
secondId: d.list[0].secondId
}
}).then(function (da) {
console.log(da);
}).catch(function (e) {
// 失败的函数只会调用一次,写一个就好;
console.log(e);
});
});
});
7.async 和 await 的基本用法。
答:async是基于Promise的generator语法糖,以同步流程表达异步操作;
async函数的特点:
被async修饰过的函数调用,返回的是一个promise对象,对象的状态默认是 '完成状态 resolve';
async函数会根据当前状态自动调用并且返回值,无需手动调用resolve() 和 reject() 方法;
然后使用函数对象.then()方法处理返回的结果值;
await的特点:
不能单独使用,需要和 async 一起使用;
遇到await可以让程序暂停执行,等待promise的执行结果;
await可以直接处理Promise的resolve()结果,对Promise的reject()结果我们有两种处理方式:
第一种解决方式:在promise对象中使用catch()方法;
第二种解决方式:在promise中不管成功与否都调用resolve()方法,通过resolve()方法传参来区别是成功或失败;
async function 函数名(){
await 异步操作1;
await 异步操作2;
}
8.简单介绍一下promise。
答:Promise 实际上是一个许诺器,里面的代码通常是将来要执行的代码,而这些代码一般都是异步的I[input:输入]/O[output:输出]操作,这些操作执行完成后会有两种结果:成功或失败,因些promise有三种状态:初始状态[pending]、成功状态[resolve]、失败状态[reject];
// var prs = new Promise(回调函数);
// resolve成功状态的回调函数;
// reject 失败状态的回调函数;
var prs = new Promise((resolve,reject)=>{
// 将来要执行的代码; 异步代码
setTimeout(function(){
...
});
});
9.如何实现对象深拷贝。
答:浅拷贝+递归:
let obj3 = deepCopy(obj);
console.log(obj3);
//浅拷贝+递归:解决JSON.parse(JSON.stringify(obj))不能拷贝对象中的方法
function deepCopy(obj) {
if (Object.prototype.toString.call(obj).slice(8, -1) == 'Object') {
var result = {}
} else if (Object.prototype.toString.call(obj).slice(8, -1) == 'Array') {
var result = []
} //判断数据类型类型
for (var attr in obj) {
if (typeof obj[attr] == 'object') {
result[attr] = deepCopy(obj[attr])
} else {
result[attr] = obj[attr]
}
}
return result
}
10.ES6 如何实现继承, ES5 如何实现。
// ES6实现继承的方式:
class Son extends Father {
// ... ...
}
// ES5 实现继承的方式
// 寄生组合式继承
function inherit(Child, Parent){
var F = function() {};
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.prototype.constructor = Child;
}
function Student(names,ages){
....
}
function SmallStudent(...){
Student.call(this,...); // 引用相同的属性
}
inherit(SmallStudent, Student); // 继承父类的原型
11.js继承方式及其优缺点。
答:原型链继承;对象冒充继承(借用构造函数);组合继承/伪经典继承(对象冒充+原型链);原型式继承;寄生式继承;寄生组合式继承;
- 原型链继承:原理:就是将父类的实例赋值给子类的原型对象;优点:实现了父类的继承;弊端:通过原型来实现继承时,原型实际上会变成另一个类型的实例。当一个实例对象改变了属性的值,那么所有的实例对象都会跟着改变;子类不能传参;
- 对象冒充继承:原理:在子类型构造函数的内部调用超类型构造函数;优点:子类可以传参,解决了继承引用类型的问题,即不会有一改全改的问题;缺点:因为只调用父类的构造函数,不能继承父类的原型上的属性和方法
- 原型式继承:原理:是借助原型可以基于已有的对象创建新对象;(Object.create()方法规范化了原型式继承。)优点:可以创建出一个和另一个对象的类似的对象,即方法和属性基本相同的情况下,利于使用,不需要使用new来创建对象就可以实现继承;缺点:你必须有一个对象可以作为另一个对象的基础,继承具有局限性;而且包含引用类型值的属性始终都会共享相应的值,就像使用原型模式一样
-
function object(o){ function F(){} F.prototype = o; return new F(); }
-
- 组合继承(对象冒充+原型链):原理:是使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。优点:实现对实例属性的继承和对原型属性和方法的继承;缺点:调用了两次构造函数,相同的属性既存在于它的实例上又存在于它的原型上。
- 寄生式继承:原理:创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真地是它做了所有工作一样返回对象。缺点:使用寄生式继承来为对象添加函数,会由于不能做到函数复用而降低效率 ;优点:任何能够返回新对象的函数都适用于此模式。
- 寄生组合式继承:原理:使用寄生式继承来继承父类的原型,然后再将结果指定给子类型的原型;在子类型构造函数的内部调用父类 构造函数继承对象实例的属性和方法;优点:集寄生式继承和组合继承的优点与一身,是实现基于类型继承的最有效方式。
12.Nodejs有哪些特性。
答:nodejs既是平台(作为web服务器[软件]),又是开发语言; 单线程,跨平台; 非阻塞,异步 I/O 模型; 事件驱动; 基于模块化开发;
13.事件订阅与发布机制。
答:Nodejs核心的API是异步事件驱动框架; events.EventEmitter(); 可以自定义事件,然后设置触发执行;
eventEmitter.on('事件名',回调函数);
eventEmitter.once('事件名', 回调函数); once()注册的事件只会被触发一次
eventEmitter.emit('事件名') // 触发事件
// 事件触发器(events)模块:让用户可以自定义事件并触发事件
const EventEmitter = require('events');
//自定义事件:eventEmitter.on('事件名',回调函数)
//自定义只执行一次事件:eventEmitter.once('事件名',回调函数);
class MyEmitter extends EventEmitter { }
const myEmitter = new MyEmitter();
myEmitter.on('event', () => {
console.log('触发事件');
});
function fn() {
// timer 模块开放了一个全局的 API,用于安排函数在未来某个时间点被调用。
// 因为定时器函数是全局的,所以使用 API 不需要调用 require('timers') 。
setTimeout(() => {
console.log('2秒之后会执行到这里');
}, 2000);
}
myEmitter.on('cctv13', fn);
//触发事件:eventEmitter.emit('事件名');
myEmitter.emit('event');
myEmitter.emit('cctv13');
14.npm 的包依赖。
答:package.json 文件中有一个dependencies的配置项,里面包括了项目对所有的包的依赖管理;使用 npm install 命令就可以下载安装项目中需要依赖的所有包;
15.npm 全局安装和局部安装的区别。
答:全局安装的包,可以在任何目录下,都可以访问到;局部安装,只能在安装的目录中才可以访问;
16.使用递归删除非空目录。
const fs = require('fs');
const path = require('path');
function fn(filename) {
let arr = fs.readdirSync(filename);
// console.log(arr);
arr.forEach(item => {
//获取当前文件或目录的状态:fs.stat() fs.statSync()
let st = fs.statSync(path.resolve(filename, item));
// console.log(st);
//判断当前文件是目录: st.isDirectory()
//判断当前文件是文件: st.isFile();
if (st.isFile()) { //为文件
// 删除文件
fs.unlinkSync(filename + '/' + item);
} else { //目录
// 继续查找即可...递归
fn(path.resolve(filename, item))
}
})
// 删除目录
fs.rmdirSync(filename);
}
fn('E:/Web');
17.get 和 post 请求的区别是什么?
1、GET请求会将参数跟在URL后进行传递,而POST请求则是作为HTTP消息的实体内容发送给WEB服务器。当然在Ajax请求中,这种区别对用户是不可见的;
2、Get传输数据容量小,不安全,post传输数据内容大,更加安全; 当向服务器发生一些数据的时候选择post比较安全;
nodejs后端接收数据的方式也不同;
18.node 怎么设置允许跨域。
答:就在服务器端加入以下代码允许跨域调用:
res.header("Access-Control-Allow-Origin", "*"); // 开启跨域支持
res.header("Access-Control-Allow-Headers", "*"); // 跨域时, 允许前端携带的请求头字段
19.如何设置 ajax 跨域携带 cookie ?
答: 使用jsonp,但要注意jsonp只支持get方式调用,同时对方程序要支持jsonp方式才行;
20.使用 express 的原因是什么?
答:express是基于 Node.js平台,快速、开放、极简的Web开发框架[后端] ; 具有如下优点:
- 提供了方便简洁的路由定义方式;
- 对获取HTTP请求参数进行了简化处理;
- 对模板引擎支持程度高,方便渲染动态HTML页面;
- 提供了中间件机制有效控制HTTP请求;
- 拥有大量第三方中间件对功能进行扩展;
21.请说一下 express 4 中 app 和 router 级别路由的区别。
答:app级别的路由 可用 请求中的path路径返回不同的响应,有三种方式post get all;router 级别路由的功能可以实现 模块化划分,可以模块管理;express中的Router作用就是为了方便我们更好的根据路由去分模块。避免将所有路由都写在入口文件中。也有三种请求方式:post get all;
22.请说一下 express 4 中如何接受 get 和 post 请求参数?
答:get请求方式有字符串请求方式(获取参数:req.query),还有path路径请求方式(获取参数:req.params);post请求参数:req.body; 使用express内置中间件 express.urlencoded() 接收 post请求参数
23.请说一下 express 4 中如何设置静态资源目录。
答:使用express内置中间件express.static(路径)来实现静态资源托管: app.use([路径,] express.static(路径));使用express内置中间件express.urlencoded()来实现接收post方式发送的参数;注意:使用内置中间件express.static()将某个目录托管后再访问时不需要带上该目录;通常将需要托管的文件放到public文件夹中统一托管;
在项目中通常将需要托管的文件放置在public文件夹中
24.请说一下 express 4 中的中间件是做什么的,如何使用?
答:中间件就是在接收请求之后返回响应之前要执行的函数,而这些函数有一定的业务处理能力。
语法:app.use([路径],中间件函数)
25.什么是 ejs 模板引擎?
答:ejs 是一套简单的模板语言,在nodejs后端利用ejs模板引擎,生成HTML页面;
26.你都用过哪些 node 第三方模块,功能是什么?
答:cookie-parser:获取cookie的值;cookie-session:设置/获取session;mime:客户端可以根据MIME来判断返回的内容类型,然后根据不同类型的内容启动不同应用程序来处理。
27.关系型数据库和非关系型数据库有哪些区别?
存储方式 | 存储结构 | 存储规范 | 扩展方式 | 查询方式 | 事务性 | |
关系型数据库 | 采用表格的存储方式,数据以行和列的方式进行储存 | 结构化存储,需要先定义表的结构再存储; | 采用最小关系表的形式进行储存 | 只具备纵向扩展能力 |
结构化查询语言SQL | 原子性、一致性、隔离性、持久性;(ACID) |
非关系型数据库 | 以数据集的方式,大量数据集中存储在一起; | 动态储存,可以适应结构的改变 | 平面数据集的方式集中存放 | 横、纵向扩展能力 |
相对SQL更简单而精确 | 基本可用、软状态、最终一致性; |
当然,关系型数据库一般都是收费的,除了MySQL,但性能也有一些限制;而非关系型数据库是开源免费的;
28.常见的关系型数据库和非关系型数据库有哪些?
答:常见的关系型数据库:MySQL,Oracle,SQLServer,DB2等;非关系型数据库:Redis,HBase,MongoDb,memcache等
29.MVC 和 MVP 的优缺点。
答:MVC分离的各个模块的功能,使程序结构更加直观;但是view是依赖model模块的,但是还有还一些耦合性,后期维护view困难;MVP实现了M和V层的完全分离,是通过定义好的借口进行交互,降低模块之间的耦合性,提高代码重用度。
30.webpack 的作用是什么,谈谈你对它的理解。
答:webpack是自动化打包工具,它可以根据配置文件查询项目目录,然后将js模块文件及其它语言的代码根据配置文件的规则转换成浏览器能够识别的文件。在webpack中,所有的静态资源都可以被处理成一个模块,包括js,css,图片,字体;
31.什么是 Webpack ?
答:Webpack是一个前端自动化打包工具,根据它的名字也很好理解,web-pack顾名思义就是前端打包工具,它是基于Node和NPM的,所以在安装使用webpack之前,需要安装nodejs,nodejs的版本过低也不行,所以推荐安装nodejs版本为v8.11.2以上,npm版本为v5.6.0以上。
32.常用的 loader 和 plugin 有哪些?
loader:加载器,用于处理各种不同类型的模块,可扩展。
- babel-loader: 将ES6+转移成ES5-
- css-loader,style-loader:解析css文件,能够解释@import url()等
- file-loader:直接输出文件,把构建后的文件路径返回,可以处理很多类型的文件
- url-loader:打包图片
plugins:插件,在webpack打包过程中不同时机执行一些任务,比如清除打包目录、复制静态文件、抽取CSS文件;
- html-webpack-plugin: 压缩html
- clean-webpack-plugin: 打包器清理源目录文件,在webpack打包器清理dist目录
33.谈谈 webpack 的优缺点。
答:webpack 作为前端自动构建工具,应用广泛,很多框架的脚手架都是基于webpack的;模块化打包工具,可以模块化的打包任何资源,适配任何模块系统,适合SPA单页应用的开发;但是学习成本低, 配置复杂, 通过babel编译后的js代码打包后体积过大
34.什么要进行模块化开发。
答:模块化就是指把系统代码分为一系列职责单一且可替换的模块。模块化开发是指如何开发新的模块和复用已有的模块来实现应用的功能。能够加快开发效率,提高性能,提高代码复用性,多人开发互不影响。
何为按需加载
35.es6 模块化规范的注意事项有哪些?
使用es6模块化语法定义的模块不能在vscode工具中直接运行,需要使用工具babel将es6语法转换成es5语法,然后才能在vscode工具中运行。es6模块化语法在很多环境软件还不支持,所以需要转化成es5的语法然后运行。
36.common.js 模块化规范的注意事项。
一个文件就是一个模块,拥有自己单独的作用域; 单个暴露与批量暴露不能一起使用 ;
// 单个暴露
exports.属性 = 值
exports.方法 = function(){}
module.exports.属性 = 值
module.exports.方法 = function(){}
// 批量暴露
module.exports = {}
37.websocket 是什么,有什么优点?
答:websocket是html5新增的协议,通过它可以让客户端与服务器双向通信,也是说除了客户端可以发消息给服务器外,服务器也可以主动推送消息给客户端。
特点:
- 双向通信,可以实时传输数据;
- 一旦建立连接、通道一直保持;
- 节省服务器资源
38.koa 与 express 的区别。
1、koa没有中间件、express有中间件
2、Koa中使用级联操作替代express中回调方法;
3、Koa使用上下文context(ctx)封装了node中的request对象、response对象;
39.为什么要进行前后端分离?
答:功能模块分离更加清晰,可以实现真正的前后端解耦。前端大量的组件代码得以复用,组件化,提升开发效率。减少后端服务器的并发/负载压力。有益于后期维护。
40.常见的 web 优化有哪些?
- 减少HTTP请求
- 使用CDN
- 添加Expires头
- 压缩组件
- 将样式表放在头部
- 将脚本放在底部
- 避免CSS表达式
- 使用外部的js和css
- 减少DNS查找
- 精简js
- 避免重定向
- 删除重复脚本
- 配置ETag
- 使Ajax可缓存