原文:掘金——jsliang
JS篇
1、引用方式
- 行内引入
- 内部引入
- 外部引入
- 注意:不推荐写行内或者在 HTML 中插入 <script>,因为浏览器解析顺序缘故,如果解析到死循环之类的 JS 代码,会卡住页面。
建议在 onload 事件之后,即等 HTML、CSS 渲染完毕再执行代码。
2、原型与原型链
关于 prototype、proto、new、call()、apply()、bind()、this 这些的知识点,由于篇幅太长,jsliang 已经抽离了出来,并做了简洁详细讲解,详见:相关知识点
3、作用域与闭包
- 在js中,作用域容易混淆,由于存在着变量提升现象,所以有时候我们就需要变通,通过自执行函数创建临时作用域,来源:相关知识点
function foo() {
var x = 1;
console.log(x); // 1
if(x) {
(function(x) {
console.log(x); // 1
var x = 2;
console.log(x); // 2
})(x)
}
console.log(x); // 1
}
foo();
- 闭包
函数 A 里面包含了 函数 B,而 函数 B 里面使用了 函数 A 的变量,那么 函数 B 被称为闭包。又或者:闭包就是能够读取其他函数内部变量的函数 - 闭包经典问题:现在我们有一段代码:
for(var i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
- 答案输出为:3个3
解析:首先,for 循环是同步代码,先执行三遍 for,i 变成了 3;然后,再执行异步代码 setTimeout,这时候输出的 i,只能是 3 个 3 了。 - 那么,我们有什么办法依次输出 0 1 2 么?
- 1.使用let
for(let i = 0; i < 3; i++) {
console.log("定时器外部:" + i);
setTimeout(function() {
console.log(i);
}, 1000);
}
- 在这里,每个 let 和代码块结合起来形成块级作用域,当 setTimeout() 打印时,会寻找最近的块级作用域中的 i,所以依次打印出 0 1 2。
- 打印过程:定时器外部:0 -> 定时器外部:1 -> 定时器外部:2 -> 0 -> 1 -> 2
- 2.使用自执行函数解决闭包问题
for(let i = 0; i < 3; i++) {
(function(i){
setTimeout(function() {
console.log(i);
}, 1000);
})(i)
}
4、浅拷贝与深拷贝
- 浅拷贝:仅仅拷贝了地址,不是完整复制,修改原对象拷贝对象也会随之改变。
- 深拷贝:完全备份体,有自己的地址,修改原对象拷贝对象不会随之改变。
- 如何实现深拷贝?->跳转
(1) 使用递归去解决深拷贝;
function deepClone(obj) {
let objClone = Array.isArray(obj) ? [] : {};
if(obj && typeof obj === "object") {
for(key in obj) {
if(obj.hasOwnProperty(key)) {
// 判断 obj 子元素是否为对象,如果是,递归复制
if(obj[key] && typeof obj[key] === "object") {
objClone[key] = deepClone(obj[key]);
} else {
// 如果不是,简单复制
objClone[key] = obj[key];
}
}
}
}
return objClone;
}
let a = [1, 2, 3, 4];
let b = deepClone(a);
a[0] = 2;
console.log(a, b); // a = [2, 2, 3, 4]; b = [1, 2, 3, 4];
(2) 使用 JSON 对象的 parse 和 stringify。
注意:采用 JSON 进行的深拷贝,无法拷贝到 undefined、function、symbol 这类数据,它是有小 bug 的深拷贝。
function deepClone(obj) {
let _obj = JSON.stringify(obj);
let objClone = JSON.parse(_obj);
return objClone
}
let a = [0, 1, [2, 3], 4];
let b = deepClone(a);
a[0] = 1;
a[2][0] = 1;
console.log(a, b); //a = [1, 1, [1, 3], 4]; b = [0, 1, [2, 3], 4];
5、模块化与组件化 ->跳转
-
模块化:最后,ECMA 国际推出了 ES6 的 modules。在 ES6 中,通过 export 关键字导出模块,通过 import关键字引用代码。当然,由于浏览器厂商诸多,ES6 在浏览器的尚不支持,目前主流做法是先将 ES6 通过 babel 编译成require。
-
组件化更关注的是 UI 部分:弹出框、头部,内容区、按钮等,都可以编写成组件,然后在适用的地方进行引用。而模块化更侧重于功能或者数据的封装,比如全局的 JSON 配置文件,比如通用的验证方法,比如规范时间戳等。
-
在前端工程化中,最重要的就是提高整个团队在 编码 -> 测试 -> 维护 这三个阶段的生产效率。团队的协调至关重要,将每个任务细分给各个成员,从而获取极致的工作效率,是管理者最喜欢看到的。而在上面的模块化和组件化的应用,就属于前端工程化中的一部分,其目的就是在一些复杂的项目中,方便团队进行合作开发,提高生产效率。
-
参考
①《到底什么是前端工程化、模块化、组件化》
②《【前端工程化系列】简谈前端模块化开发与开发规范》
③ 《个人关于模块化的理解》
④ 《组件化开发和模块化开发概念辨析》
⑤ 《JavaScript模块化 — Commonjs、AMD、CMD、es6 modules》
⑥ 《浅谈什么是前端工程化》
6、面向对象与面向过程
- 面向过程:就是做围墙的时候,由你本身操作,叠第一层的时候:放砖头,糊水泥,放砖头,糊水泥;然后第二层的时候,继续放砖头,糊水泥,放砖头,糊水泥……
- 面向对象:就是做围墙的时候,由他人帮你完成,将做第一层的做法抽取出来,就是放砖头是第一个动作,糊水泥是第二个动作,然后给这两个动作加上步数,最后告诉机器人有n 层,交给机器人帮你工作就行了。
- 可参见:fans跳转
7、防抖与节流
- 防抖与节流 -> enter
- 重绘与回流
- 浏览器解析 URL
- DNS 域名解析
- TCP 三次握手与四次挥手
- 浏览器渲染页面
8、ES6
- ES6 是个大知识点,如果你面试的公司不是 “饱经沧桑” 的那种,那么一定会出点 ES6 问题,例如:说说 let、var、const 区别讲讲 Promise 及其使用
- 学习推荐 -> 《ECMAScript 6 入门 - 阮一峰》
9、数组操作
在 JavaScript 中,用得较多的之一无疑是数组操作,这里过一遍数组的一些用法:
- map: 遍历数组,返回回调返回值组成的新数组
- forEach: 无法break,可以用try/catch中throw new Error来停止
- filter: 过滤
- some: 有一项返回true,则整体为true
- every: 有一项返回false,则整体为false
- join: 通过指定连接符生成字符串
- push / pop: 末尾推入和弹出,改变原数组, 返回推入/弹出项【有误】
- unshift / shift: 头部推入和弹出,改变原数组,返回操作项【有误】
- sort(fn) / reverse: 排序与反转,改变原数组
- concat: 连接数组,不影响原数组, 浅拷贝
- slice(start, end): 返回截断后的新数组,不改变原数组
- splice(start, number, value…): 返回删除元素组成的数组,value 为插入项,改变原数组
- indexOf / lastIndexOf(value, fromIndex): 查找数组项,返回对应的下标
- reduce / reduceRight(fn(prev, cur), defaultPrev): 两两执行,prev 为上次化简函数的return值,cur 为当前值(从第二项开始)
10、跨域相关
- 参见:《9钟跨域方法解析》
Vue.js版块
学习推荐:
1、MVVM