文末
如果30岁以前,可以还不知道自己想去做什么的话,那30岁之后,真的觉得时间非常的宝贵,不能再浪费时间在一些碎片化的事情上,比如说看综艺,电视剧。一个人的黄金时间也就二,三十年,不能过得浑浑噩噩。所以花了基本上休息的时间,去不断的完善自己的知识体系,希望可以成为一个领域内的TOP。
同样是干到30岁,普通人写业务代码划水,榜样们深度学习拓宽视野晋升管理。
这也是为什么大家都说30岁是程序员的门槛,很多人迈不过去,其实各行各业都是这样都会有个坎,公司永远都缺的高级人才,只用这样才能在大风大浪过后,依然闪耀不被公司淘汰不被社会淘汰。
269页《前端大厂面试宝典》
包含了腾讯、字节跳动、小米、阿里、滴滴、美团、58、拼多多、360、新浪、搜狐等一线互联网公司面试被问到的题目,涵盖了初中级前端技术点。
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
前端面试题汇总
JavaScript
localStorage.setItem(‘key’, 1);
console.log(localStorage.getItem(‘key’)); // ‘1’
你会发现,存进去的如果是对象,拿出来就变成了字符串 '[object, object]'
,数据丢失了!
存进去的如果是 number,拿出来也变成了 string
要解决这个问题,一般是使用 JSON.stringify()
配合 JSON.parse()
localStorage.setItem(‘key’, JSON.stringify({name: ‘value’}));
console.log(JSON.parse(localStorage.getItem(‘key’))); // {name: ‘value’}
这样,就可以实现对象和非 string 类型的储存了
但是,这么做有一个缺点,那就是 JSON.stringify()
本身是存在一些问题的
const a = JSON.stringify({
a: undefined,
b: function(){},
c: /abc/,
d: new Date()
});
console.log(a) // “{“c”:{},“d”:“2021-02-02T19:40:12.346Z”}”
console.log(JSON.parse(a)) // {c: {}, d: “2021-02-02T19:40:12.346Z”}
如上,JSON.stringify()
无法正确转换 JS 的部分属性
-
undefiend
-
Function
-
RegExp(正则表达式,转换后变成了空对象)
-
Date(转换后变成了字符串,而非 Date 类的对象)
其实还有个 Symbol 也无法被转换,但由于 Symbol 本身定义(全局唯一性)就决定了,它不应该被转换,否则即使转换回来,也不会是原来那个 Symbol
Function 也比较特殊,不过要兼容的话,可以先调用 .toString()
转换为字符串储存,需要的时候再 eval
转回来
以及,JSON.stringify()
无法转换循环引用的对象
const a = { key: ‘value’ };
a[‘a’] = a;
JSON.stringify(a);
// Uncaught TypeError: Converting circular structure to JSON
// --> starting at object with constructor ‘Object’
// — property ‘a’ closes the circle
// at JSON.stringify ()
大部分应用中,JSON.stringify()
的这个问题基本上可以忽略,但是一小部分场景还是会导致问题,比如想保存一个正则表达式,一个 Date 对象,这种方法就会出问题
总结
在大部分应用场景下,LocalStorage 已经能完全替代 Cookie,只有类似于广告这种场景,由于 Cookie 可以被服务端设置,Cookie 仍存在不可替代的价值
但是 LocalStorage 并不完美,它只支持 10MB 储存,在一些应用场景还是不够用,并且原生只支持字符串,JSON.stringify()
的解决方案又不够完美,因此很多时候不太适合大量数据和复杂数据的储存
IndexedDB
=========
IndexedDB 的全称是 Indexed Database,从名字中就可以看出,它是一个数据库
IndexedDB 早在 2009 年就有了第一次提案,但其实它和 Web Storage 几乎是同一时间普及到各大浏览器的(没错,就是 2015 年那会,es6 也是那时候)
IndexedDB 是一个正经的数据库,它在问世后替代了原来不正经的 Web SQL 方案,成为了当今唯一运行在浏览器里的数据库
在我看来,IndexedDB 其实更适合当作终极前端本地数据储存方案
相比于 LocalStorage,IndexedDB 的优点是
-
储存量理论上没有上限
-
- Chrome 对 IndexedDB 储存空间限制的定义是:硬盘可用空间的三分之一
-
所有操作都是异步的,相比 LocalStorage 同步操作性能更高,尤其是数据量较大时
-
原生支持储存 JS 的对象
-
是个正经的数据库,意味着数据库能干的事它都能干
但是缺点也比较致命:
-
操作非常繁琐
-
本身有一定门槛(需要你懂数据库的概念)
由于提案较早,IndexedDB 的 API 设计其实是比较糟糕的,对于新手而言,光是想连上数据库,并往里面加东西,都需要折腾半天
对于简单的数据储存而言,IndexedDB 的 API 显得太复杂了,再加上其 API 全是异步的,会带来额外的心智负担,远没有 LocalStorage 简单两行代码搞定数据存取来的快
因此,IndexedDB 在今天的使用规模相比 LocalStorage 差远了,即使 IndexedDB 本身的设计其实更适合用来在浏览器上储存数据
总之,如果不考虑 IndexedDB 的操作难度,其作为一个前端本地储存方案其实是接近完美的
简单理解数据库
在使用 IndexedDB 前,你首先需要懂基本的数据库概念
这里用 Excel 类比,简单介绍数据库的基本概念,不做太深入的讨论
需要了解四个基本概念,以关系型数据库为例
-
数据库 Database
-
数据表 Table(IndexedDB 中叫 ObjectStore)
-
字段 Field
-
事务 Transaction
(虽然 IndexedDB 算不上关系型数据库,但概念都是相通的)
假设清华和北大各自需要建一个数据库,用来存各自学生与教工的信息,假设命名为
-
清华:
thu
-
北大:
pku
这样,清北之间的数据就可以相互独立
然后,我们再到数据库里建表
-
student
表,储存学生信息 -
stuff
表,储存教工信息
数据表(Table)是什么?说白了,就是一个类似于 Excel 表一样的东西
比如 student
表,可以长这样:
image-20210204032958192
上面的 学号、姓名、年龄、专业 就是数据表的字段
当我们想往 student
表添加数据时,就需要按照规定的格式,往表里加数据(关系型数据库的特点,而 IndexedDB 允许不遵守格式)
数据库也给我们提供了方法,当我们知道一个学生的学号(id),就可以在非常短的时间内,在表里成千上万个学生中,快速找到这个学生,并返回他的完整信息
也可以根据 id 定位,对该学生的数据进行修改,或者删除
id 这种每条数据唯一的值,就可以被用来做主键(primary key),主键在表内独一无二,无法添加相同主键的数据
而主键一般会被建立索引,所谓对字段建立索引,就是可以根据这个字段的值,在表里非常快速的找到对应的数据(通常不高于 O(logN)),如果没有索引,那可能就需要遍历整个表(O(N))
增、删、改、查这些操作,都需要通过事务 Transaction 来完成
-
如果事务中任何一个操作没有成功,整个事务都会回滚
-
在事务完成之前,操作不会影响数据库
-
不同事务之间不能互相影响
举个例子,当你发起一个事务,想利用这个事务添加两个学生,如果第一个学生添加成功,但是第二个学生添加失败,事务就会回滚,第一个学生将根本不会在数据库中出现过
事务在银行转账这种场景非常有用:如果转账中任何一步失败了,整个转账操作就和没发生过一样,不会造成任何影响
在同一个 Excel 文件(数据库)中,我们除了 student
表,还可以有 stuff
表(同一个数据库中有了两个不同的数据表):
image-20210204033839030
然后,清华和北大各自分一个 Excel 文件,就相当于分了两个数据库
image-20210204034441432
总而言之,不扯数据库各种难理解的概念,我们其实完全可以用 Excel 来类比数据库
-
一个 Excel 文件就是一个 Database
-
一个 Excel(Database)里可以有很多不同表格(数据表 Table)
-
表格的列的名称其实就是字段
上述类比最接近 MySQL 这种关系型数据库,但放在其它一些比较特殊的数据库上可能就不太妥当(比如图数据库)
如果你是新手,用 Excel 类比理解数据库完全没问题,足以使用 IndexedDB 了
虽然说 IndexedDB 使用 key-value 的模式储存数据,但你也完全可以用数据表 Table 的模式来看待它
IndexedDB 的使用
使用 IndexedDB 的第一步是打开数据库:
const request = window.indexedDB.open(‘pku’);
上面这个操作打开了名为 pku
的数据库,如果不存在,浏览器会自动创建
然后 request
上有三个事件:
var db; // 全局 IndexedDB 数据库实例
request.onupgradeneeded = function (event) {
db = event.target.result;
console.log(‘version change’);
};
request.onsuccess = function (event) {
db = request.result;
console.log(‘db connected’)l;
};
request.onblocked = function (event) {
console.log(‘db request blocked!’)
}
request.onerror = function (event) {
console.log(‘error!’);
};
IndexedDB 有一个版本(version)的概念,连接数据库时就可以指定版本
const version = 1;
const request = window.indexedDB.open(‘pku’, version);
版本主要用来控制数据库的结构,当数据库结构(表结构)发生变化时,版本也会变化
如上,request
上有四个事件:
-
onupgradeneeded
在版本改变时触发 -
- 注意首次连接数据库时,版本从 0 变成 1,因此也会触发,且先于
onsuccess
- 注意首次连接数据库时,版本从 0 变成 1,因此也会触发,且先于
-
onsuccess
在连接成功后触发 -
onerror
在连接失败时触发 -
onblocked
在连接被阻止的时候触发,比如打开版本低于当前存在的版本
注意这四个事件都是异步的,意味着在连接 IndexedDB 的请求发出去后,需要过一段时间才能连上数据库,并进行操作
开发者对数据库的所有操作,都得放在异步连上数据库之后,这有的时候会带来很大的不便
而开发者如果想创建数据表(在 IndexedDB 里面叫做 ObjectStore),只能将其放到 onupgradeneeded
事件中(官方的定义是需要一个 IDBVersionChange
的事件)
request.onupgradeneeded = function (event) {
db = event.target.result;
if (!db.objectStoreNames.contains(‘student’)) {
db.createObjectStore(‘student’, {
keyPath: ‘id’, // 主键
autoIncrement: true // 自增
});
}
}
上面这段代码,在数据库初始化时,创建了一个 student
的表,并且以 id
为自增主键(每加一条数据,主键会自动增长,无需开发者指定)
在这一切做好以后,终于,我们可以连接数据库,然后添加数据了
const adding = db.transaction(‘student’, ‘readwrite’) // 创建事务
.objectStore(‘student’) // 指定 student 表
.add({ name: ‘luke’, age: 22 });
adding.onsuccess = function (event) {
console.log(‘write success’);
};
adding.onerror = function (event) {
console.log(‘write failed’);
}
用同样的方法再加一条数据
db.transaction(‘student’, ‘readwrite’)
.objectStore(‘student’)
.add({ name: ‘elaine’, age: 23 });
然后,打开浏览器的开发者工具,我们就能看到添加的数据:
这里可以看到 IndexedDB 的 key-value 储存特性,key 就是主键(这里指定主键为 id
),value 就是剩下的字段和对应的数据
这个 key-value 结构对应的 Table 结构如下:
image-20210204050515354
如果要获取数据,需要一个 readonly
的 Transaction
const request = db.transaction(‘student’, ‘readonly’)
.objectStore(this.name)
.get(2); // 获取 id 为 2 的数据
request.onsuccess = function (event) {
console.log(event.target.result) // { id: 2, name: ‘elaine’, age: 23 }
}
综上,哪怕只是想简单的往 IndexedDB 里增加和查询数据,都需要写一大堆代码,操作非常繁琐,一不小心还容易掉坑里
那么,有没有什么办法,能更优雅的使用 IndexedDB,在代码量减少的情况下,还能更好的发挥其实力呢?
GoDB.js
=======
GoDB.js
是一个基于 IndexedDB 实现前端本地储存的类库
帮你做到代码更简洁的同时,更好的发挥 IndexedDB 的实力
首先安装:
npm install godb
对 IndexedDB 的增删改查,一行代码就可以搞定!
import GoDB from ‘godb’;
const testDB = new GoDB(‘testDB’); // 连接数据库
const user = testDB.table(‘user’); // 获取数据表
const data = { name: ‘luke’, age: 22 }; // 随便定义一个对象
user.add(data) // 增
.then(luke => user.get(luke.id)) // 查
.then(luke => user.put({ …luke, age: 23 })) // 改
.then(luke => user.delete(luke.id)); // 删
或者,一次性添加许多数据,然后看看效果:
const arr = [
{ name: ‘luke’, age: 22 },
{ name: ‘elaine’, age: 23 }
];
user.addMany(arr)
.then(() => user.consoleTable());
上面这段代码,会在添加数据后,在控制台中展示出 user
表的内容:
add-many
回到之前 LocalStorage 出问题的那个例子,用 GoDB
就可以实现正常储存:
import GoDB from ‘godb’;
const testDB = new GoDB(‘testDB’); // 连接数据库
const store = testDB.table(‘store’); // 获取数据表
const obj = {
a: undefined,
b: /abc/,
c: new Date()
};
store.add(obj)
.then(item => store.get(item.id)) // 获取存进去的实例
.then(res => console.log(res));
// {
// id: 1,
// a: undefined,
// b: /abc/,
// c: new Date()
// }
并且,循环引用的对象也能使用 GoDB
进行储存
const a = { key: ‘value’ };
a[‘a’] = a;
store.add(a)
.then(item => store.get(item.id)) // 获取存进去的实例
.then(result => console.log(result));
// 打印出来的对象比 a 多了个 id,其它完全一致
关于 GoDB
更详细的用法,可以参考 GoDB
的项目官网(不断完善中):
https://godb-js.github.io/
总之,GoDB
可以
-
帮你在背后处理好 IndexedDB 各种繁琐操作
-
帮你在背后维护好数据库、数据表和字段
-
- 以及字段的索引,各种属性(比如
unique
)
- 以及字段的索引,各种属性(比如
-
帮你规范化 IndexedDB 的使用,使你的项目更易维护
-
最终,开放几个简单易用的 API 给你,让你用简洁的代码玩转 IndexedDB
总结
==
总结一下三大方案各自的特点以及适用场景:
-
Cookie
-
- 能被服务器指定,浏览器会自动在请求中带上
-
大小只有 4kb
-
大规模应用于广告商定位用户
-
配合 session 也是一个可行的登录鉴权方案
-
Web Storage
-
- 大小有 10MB,使用极其简单
-
但是只能存字符串,需要转义才能存 JS 对象
-
大部分情况下能完全替代 Cookie,且更安全
-
配合 token 可以实现更安全的登录鉴权
-
IndexedDB
-
- 储存空间无上限,功能极其强大
-
原生支持 JS 对象,能更好的储存数据
-
以数据库的形式储存数据,数据管理更规范
-
但是,原生 API 操作很繁琐,且有一定使用门槛
我个人是非常看好 IndexedDB 的,我认为在前端越来越复杂的未来,在下一个十年各种重前端应用(在线文档,各种 SaaS 应用),以及 Electron 环境中,IndexedDB 一定能够大放光彩
css
1,盒模型
2,如何实现一个最大的正方形
3,一行水平居中,多行居左
4,水平垂直居中
5,两栏布局,左边固定,右边自适应,左右不重叠
6,如何实现左右等高布局
7,画三角形
8,link @import导入css
9,BFC理解
js
1,判断 js 类型的方式
2,ES5 和 ES6 分别几种方式声明变量
3,闭包的概念?优缺点?
4,浅拷贝和深拷贝
5,数组去重的方法
6,DOM 事件有哪些阶段?谈谈对事件代理的理解
7,js 执行机制、事件循环
8,介绍下 promise.all
9,async 和 await,
10,ES6 的 class 和构造函数的区别
11,transform、translate、transition 分别是什么属性?CSS 中常用的实现动画方式,
12,介绍一下rAF(requestAnimationFrame)
13,javascript 的垃圾回收机制讲一下,
14,对前端性能优化有什么了解?一般都通过那几个方面去优化的?
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
大小有 10MB,使用极其简单
-
但是只能存字符串,需要转义才能存 JS 对象
-
大部分情况下能完全替代 Cookie,且更安全
-
配合 token 可以实现更安全的登录鉴权
-
IndexedDB
-
- 储存空间无上限,功能极其强大
-
原生支持 JS 对象,能更好的储存数据
-
以数据库的形式储存数据,数据管理更规范
-
但是,原生 API 操作很繁琐,且有一定使用门槛
我个人是非常看好 IndexedDB 的,我认为在前端越来越复杂的未来,在下一个十年各种重前端应用(在线文档,各种 SaaS 应用),以及 Electron 环境中,IndexedDB 一定能够大放光彩
css
1,盒模型
2,如何实现一个最大的正方形
3,一行水平居中,多行居左
4,水平垂直居中
5,两栏布局,左边固定,右边自适应,左右不重叠
6,如何实现左右等高布局
7,画三角形
8,link @import导入css
9,BFC理解
[外链图片转存中…(img-lKeqEapW-1715763254757)]
js
1,判断 js 类型的方式
2,ES5 和 ES6 分别几种方式声明变量
3,闭包的概念?优缺点?
4,浅拷贝和深拷贝
5,数组去重的方法
6,DOM 事件有哪些阶段?谈谈对事件代理的理解
7,js 执行机制、事件循环
8,介绍下 promise.all
9,async 和 await,
10,ES6 的 class 和构造函数的区别
11,transform、translate、transition 分别是什么属性?CSS 中常用的实现动画方式,
12,介绍一下rAF(requestAnimationFrame)
13,javascript 的垃圾回收机制讲一下,
14,对前端性能优化有什么了解?一般都通过那几个方面去优化的?
[外链图片转存中…(img-YOnGYwE3-1715763254758)]