列举一下常见的关系型数据库和非关系型数据库?
- 关系型数据库:Oracle、MySQL、DB2、SQL Server
- 非关系型数据库:MongoDB、NoSql
- 两者的区别有哪些?
- 关系型数据库只支持基础类型存储;非关系型存储格式可以是key,value形式、文档形式、图片形式等,使用灵活,应用场景广泛
- 存储载体不同:关系型数据库存储载体只能使用硬盘;非关系型数据库可以使用硬盘或者随机存储器作为载体
- 关系型数据库支持SQL语句,可用于复杂的查询;非关系型数据库不支持SQL语句
- 非关系型数据库基于键值对存储,数据没有耦合性,容易扩展
简述js的执行机制有哪些?
- js的单线程机制
- 单线程简单的说就是同一时间只能做一件事,当有多个任务时,只能按照一个顺序,一个完成了再执行下一个
- 任务队列
- 当任务量过大,CPU处于忙碌状态或者任务所需的东西还未准备好,导致CPU闲置这两种情况时,为了提高执行效率,就会把等待中的任务先挂起到任务队列中,等得到需要的东西后再执行
- 任务队列是一个“先进先出”的数据结构,排在前面的事件会优先被主线程读取。主线程的读取基本上是自动的,只要执行栈一清空,“任务队列”上第一位的事件就自动进入到主线程。
- 详细说一下任务队列中的异步任务执行过程?
- 所有同步任务都在主线程上执行,形成一个执行栈
- 主线程之外,还存在一个“任务队列”。只要异步任务有了执行结果,就在“任务队列”中放置一个事件
- 一旦“执行栈”中的所有同步任务执行完毕,系统就会读取“任务队列”,看看里面有哪些事件。那些对应的异步任务,就结束等待状态,进入执行栈开始被执行
- 主线程不断重复以上三步
- 事件
- 回调函数
- Event Loop
- Event Loop 的作用是什么?
- 是一个程序结构,用于等待和发送消息和事件
- 简单的说,就是在程序中设置两个进程:
- 一个负责程序本身的运行,称为“主线程”
- 另一个负责主线程与其他进程的通信,被称为“Event Loop线程”
- 定时器等
什么是token
- token自身是三段式加密字符串,
- token自身第一、三段采用哈希散列的方式不可逆加密,不容易被伪造。
- token这一段字符串由后端发送给前端,前端将token保存在localstorage中,这样再通过axios的请求拦截器向后端发送数据请求的时候可以将token加载请求头中,便于后端引用第三方jwt验证中间件验证用户的登录信息。
session,有了session为什么还要使用token
- session:针对服务器的存储,他在安装后会在req添加上一个属性session,这是一个空间,可以在其中任意添加内容,在不借助服务器的情况下,一旦服务器重启,存储的数据便会消失,所以借助另一个插件connect-mongo连上数据库,实现持久化存储。就可以设置一个过期时间。
- session需要保证是同一台服务器,不能更换服务器,且session一半在客户端依赖cookie存储,跨域以后cookie不能再设置,而且极其容易被伪造。http协议无状态,每次请求都是独立的,不保存客户端的状态,客户端必须每次带上自己的状态去请求服务器,session无法跨域,在cors跨域后(cookie)便无法使用,
柯里化与闭包
- 闭包:有权访问另一个函数作用域中局部变量的函数。常见的用法就是函数式编程,向在执行函数中套函数,并向新函数中传递参数,这个参数就是该执行函数中的局部变量。
- 闭包的一个典型应用就是柯里化函数:
柯里化,可以理解为提前接收部分参数,延迟执行,不立即输出结果,而是返回一个接受剩余参数的函数。
柯里化函数的整体思想:将函数的执行拆分为多个部分,首先执行其中的一部分,该部分大多是处理整理函数中的数据,之后再执行要利用这些数据实现的功能。例如验证正则,导出正则js中的验证函数,实际导出的是return中的验证正则函数。
内存泄漏、垃圾回收机制
- js中没有专门的回收机制,如何处理内存溢出问题。
在堆中的对象可能被若干个变量引用其地址,如果这个对象在后面的内容中不再使用。我们需要将堆中的这个对象清除,否则不会被浏览器自动清除,这样就会造成垃圾产生。当不断产生这种垃圾时,我们就把这种情况叫内存泄漏。 - 什么情况下会产生内存泄漏
- 事件绑定未解绑
- 定时器未及时清理
- 对象内存过大
- 保存了多个好用内存过大的对象,造成内存超出限制。
- v8引擎的内存泄漏
- 如何处理内存泄漏
先创建每个对象的管理池,针对每个对象所引用他的变量做统一存储管理,如果需要清理该对象时,将引用他的所有变量设置为null,当内存上限达到峰值时,系统会通过垃圾回收车将堆中这些无引用的对象全部清除回收,这就是垃圾回收机制
深浅拷贝
浅拷贝
- 也就是拷贝A对象里面的数据,但是不拷贝A对象里面的子对象
- 首先可以通过
Object.assign
来解决这个问题,很多人认为这个函数是用来深拷贝的。其实并不是,Object.assign
只会拷贝所有的属性值到新的对象中,如果属性值是对象的话,拷贝的是地址,所以并不是深拷贝。
**Object.assign()**
方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
- Object.assign(target, …sources)//target 目标对象 source源对象
- 如果目标对象中的属性具有相同的键,则属性将被源对象中的属性覆盖,后面的源对象色属性将类似的覆盖前面的源对象的属性
let a = {
age: 1
}
let b = Object.assign({}, a)
a.age = 2
console.log(b.age) // 1
- 另外我们还可以通过展开运算符
...
来实现浅拷贝
let a = {
age: 1
}
let b = { ...a }
a.age = 2
console.log(b.age) // 1
- 浅拷贝只解决了第一层的问题,如果接下去的值中还有对象的话,那么就又回到最开始的话题了,两者享有相同的地址。要解决这个问题,我们就得使用深拷贝了。
深拷贝
- 会克隆出一个对象,数据类型相同,数据相同,但是引用地址不同(拷贝A对象里面的数据,而且拷贝它里面的子对象)
- 这个问题通常可以通过
JSON.parse(JSON.stringify(object))
来解决。
let a = {
age: 1,
jobs: {
first: 'FE'
}
}
let b = JSON.parse(JSON.stringify(a))
a.jobs.first = 'native'
console.log(b.jobs.first) // FE
- 该方法也是有局限性
- 会忽略
undefined
- 会忽略
symbol
- 不能序列化函数
- 不能解决循环引用的对象
- lodash深拷贝
浏览器是怎么具体渲染页面的
- 我们都知道,在浏览器加载一个HTML文件的时候,他会有一个js引擎,和一个渲染引擎,当我们打开一个网页时,浏览器都会去请求对应的 HTML 文件。虽然平时我们写代码时都会分为 JS、CSS、HTML 文件,也就是字符串,但是计算机硬件是不理解这些字符串的,所以在网络中传输的内容其实都是 0 和 1 这些字节数据。
- 当浏览器接收到这些字节数据以后,它会将这些字节数据转换为字符串,也就是我们写的代码。当数据转换为字符串以后,浏览器会先将这些字符串通过词法分析转换为标记(token),这一过程在词法分析中叫做标记化(tokenization),当结束标记化后,这些标记会紧接着转换为 Node,最后这些 Node 会根据不同 Node 之前的联系构建为一颗 DOM 树。
- 当然,在解析 HTML 文件的时候,浏览器还会遇到 CSS 和 JS 文件,这时候浏览器也会去下载并解析这些文件,而在生成cssom树的时候,浏览器会确定下每一个节点的样式到底是什么,并且这一过程其实是很消耗资源的。因为样式你可以自行设置给某个节点,也可以通过继承获得。在这一过程中,浏览器得递归 CSSOM 树,然后确定具体的元素到底是什么样式。当我们生成 DOM 树和 CSSOM 树以后,就需要将这两棵树组合为渲染树。在这一过程中,不是简单的将两者合并就行了。渲染树只会包括需要显示的节点和这些节点的样式信息,如果某个节点是 display: none 的,那么就不会在渲染树中显示。
- 当浏览器生成渲染树以后,就会根据渲染树来进行布局(也可以叫做回流),然后调用 GPU 绘制,合成图层,显示在屏幕上。
为什么要减少dom操作
- 因为 DOM 是属于渲染引擎中的东西,而 JS 又是 JS 引擎中的东西。当我们通过 JS 操作 DOM 的时候,其实这个操作涉及到了两个线程之间的通信,那么势必会带来一些性能上的损耗。操作 DOM 次数一多,也就等同于一直在进行线程之间的通信,并且操作 DOM 可能还会带来重绘回流的情况,所以也就导致了性能上的问题。
回流和重绘
- 重绘是当节点需要更改外观而不会影响布局的,比如改变 color 就叫称为重绘。
- 回流是布局或者几何属性需要改变就称为回流。回流必定会发生重绘,重绘不一定会 引发回流。回流所需的成本比重绘高的多。
如何减少http请求
- 首先将css,js压缩合并一下
- cdn:内容分发网络 content Delibery Network
- 合并图片:精灵图,iconfont
- data:url模式
- 合理的利用浏览器缓存
如果提高首页打开速度
- 首屏页面静态化
- DOM层级的调优
- 在服务器端做压缩
- 异步请求,可以做懒加载,
- 优化后端接口,数据量降低
- 中间件
gulp如何实现JS的模块化
- coommenjs规范
- 使用AMD规范
- webString等插件
- 哈希值
node.js中gulp模块化使用
常用 gulp插件
- sass的编译(gulp-sass)
- less编译(gulp-less)
- 重命名(gulp-rename)
- 自动添加css前缀(gulp-autoprefixer)
- 压缩css(gulp-clean-css)
- js代码检验(gulp-jsinit)
- 合并js文件(gulp-concat)
- 压缩js文件(gulo-uglify)
- 压缩图片(gulp-imagemin)
- 自动刷新页面(gulp-livereload)
- 图片缓存(gulp-cache)
- 更新提示(gulp-notify)
插件使用
- 下载插件 npm i install gulp-less --save-dev
- 在入口js文件中(gulpfile.js)中写代码(任务代码)
var g = require("gulp");
var gxx = require("gulp-xxxx");
g.task("任务名",function() {
g.src("目标路径")
.pipe(插件方法,如test_less())
.pipe(g.dest("任务完成后的路径"));
});
- gulp-htmlmin插件下载
- npm i gulp-htmlmin --save-dev
var gulp = require("gulp");
var gulp_htmlmin = require("gulp-htmlmin");
var minjs = require('gulp-uglify');//autoprefixer插件模块
gulp.task("test-htmlmin",function(){
var options = {
removeComments: true,
collapseWhitespace: true,
collapseBooleanAttributes: true,
removeEmptyAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
minifyJS: true,
minifyCSS: true
};
gulp.src("src/*.html")
.pipe(gulp_htmlmin(options))
.pipe(gulp.dest("dist/"))
});
- gulp-minify-css插件
- 安装 npm i gulp-minify-css --save -dev
var cssmin_minify = require('gulp-minify-css');
gulp.task("test-minify",function(){
gulp.src("src/css/*.css")
.pipe(cssmin_minify({
advanced: false,
compatibility: 'ie7',
keepBreaks: false,
keepSpecialComments: '*'
}))
.pipe(gulp.dest("dist/css/"))
});
- gulp-sass插件
- 安装:npm i gulp-sass --save-dev
var gulp = require('gulp'),
sass = require("gulp-sass");
gulp.task('test-sass', function () {
gulp.src('sass/*.sass')
.pipe(sass())
.pipe(gulp.dest('dist/css'));
});
- gulp-uglify插件
- 安装 npm install gulp-uglify --save-dev
var minjs = require('gulp-uglify');
gulp.task('jsmin', function () {
gulp.src(['src/js/address.js','src/js/cart.js','src/js/detail.js','src/js/index.js'])
//多个文件以数组形式传入
.pipe(minjs())
.pipe(gulp.dest('dest/js'));
});