# 前端爱好者学习周刊:第3期
2019-11-23
htmlcssjs
# 【Html+Css】
# 1. 溢出文字的scss
给新手前端的✋5段救命🚀css代码(scss mixin) (opens new window)
/**
* 溢出省略号
* @param {Number} 行数
*/
@mixin ellipsis($rowCount: 1) {
@if $rowCount <=1 {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
} @else {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: $rowCount;
-webkit-box-orient: vertical;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 【JavaScript】:
# 1. js小技巧
Tips to write better Conditionals in JavaScript (opens new window)
- 将多个if || 的操作可以做成array.includes的写法;
if (animal === 'dog' || animal === 'cat') {
console.log(`I have a ${animal}`);
}
//===================
const animals = ['dog', 'cat', 'hamster', 'turtle'];
if (animals.includes(animal)) {
console.log(`I have a ${animal}`);
}
2
3
4
5
6
7
8
9
- 一个函数中,能早返回就早返回
- 第3条中的写的用object的形式代替switch中的Map形式之前没用过:
const fruitColor = new Map()
.set('red', ['apple', 'strawberry'])
.set('yellow', ['banana', 'pineapple'])
.set('purple', ['grape', 'plum']);
function printFruits(color) {
return fruitColor.get(color) || [];
}
2
3
4
5
6
7
8
- 第5条中提到的检测数组中是否每个都匹配或者存在匹配的使用
Array.every
、Array.some
想起了match和test----好吧,此二种针对于字符串
# 2.将回调写成Promise
const shootPeasPromise = (...args) => {
return new Promise((resolve, reject) => {
// This is a not a Node styled callback.
// 1. data is the first argument
// 2. err is the second argument
shootPeas(...args, (data, err) => {
if (err) return reject(err)
resolve(data)
})
})
}
2
3
4
5
6
7
8
9
10
11
如果有多个参数的时候,需要以数组形式,不能写成resolve(data, size)
,正确应该是resolve([data, size])
。因为Promise只能返回一个参数。
# 3.理解 Javascript 执行上下文和执行栈
执行上下文的类型:
- 全局执行上下文
- 函数执行上下文
- Eval 函数执行上下文
执行栈: LIFO(后进先出)结构
执行上下文是如何被创建的:
1)创建阶段;
- 确定 this 的值,也被称为 This Binding。
- LexicalEnvironment(词法环境) 组件被创建。
- VariableEnvironment(变量环境) 组件被创建。
This Binding:
在全局执行上下文中,this 的值指向全局对象,在浏览器中,this 的值指向 window 对象。 在函数执行上下文中,this 的值取决于函数的调用方式。如果它被一个对象引用调用,那么 this 的值被设置为该对象,否则 this 的值被设置为全局对象或 undefined(严格模式下)。例如:
let person = { name: 'peter', birthYear: 1994, calcAge: function() { console.log(2018 - this.birthYear); } } person.calcAge();// 24 // 'this' 指向 'person', 因为 'calcAge' 是被 'person' 对象引用调用的。 let calculateAge = person.calcAge; calculateAge(); //NaN // 'this' 指向全局 window 对象,因为没有给出任何对象引用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16词法环境有两种类型:
- 全局环境
- 函数环境
function foo(a, b) { console.log(arguments)//{"0": 2,"1": 3, callee:f foo(a,b)...} var c = a + b; } foo(2, 3);
1
2
3
4
5
6环境记录有两种类型:
- 声明性环境记录
- 对象环境记录
变量环境指的是var声明的变量。let和const声明的变量在创建阶段是未初始化,而var声明的在创建阶段是undefined,所以var的变量提升以及let和const的提前调用报错可解;
2)执行阶段
完成对所有变量的分配,最后执行代码
# 4.服务端渲染和客户端渲染
服务端渲染和客户端渲染 (opens new window)
客户端请求:
在客户端渲染中, 客户端至少要对服务端发送两次请求
(1)用户在浏览器输入请求的地址例如:172.0.0.1:8080 到服务器, 服务器接受到客户端的请求拿到一个没有被数据渲染的空页面
(2)客户端拿到服务端的空字符串页面,然后从上往下开始执行里面的代码,当执行到script中有请求或者渲染等代码时,就会对服务器再次发出请求
(3)服务端接收到客户端的第二次请求,就把响应的数据发送给客户端,然后客户端再进行渲染
服务端渲染:
- 1)客户端只发送一次请求,服务端直接返回给客户端一个被渲染好的页面
两者的区别,以及什么场合使用:
1、客户端渲染需要对服务端进行两次请求,响应的开销较大,而服务端渲染只需要客户端对服务端进行一次请求
2、如何查看一个网页是客户端渲染还是服务端渲染:可以通过右键查看源代码的形式
客户端渲染: 右击查看源代码找不到内容
服务段渲染:是可以在源代码中找到内容的
3、网站一般都是用客户端渲染和服务端渲染结合的形式
4、正真的网站既不是纯异步,也不是纯服务端渲染,而是两者结合
5、商品的商品列表采用的是服务端渲染,目的是为了SEO搜索引擎优化,而他的商品评论为了用户体验,用户体验更好
6、服务端渲染可以被爬虫抓取到,客户端渲染爬虫抓取不到
# 5.JavaScript深入之执行上下文栈和变量对象
JavaScript深入之执行上下文栈和变量对象 (opens new window)
JS 引擎并不是一行一行地分析和执行程序,而是一段一段地分析执行,会先进行编译阶段然后才是执行阶段。
变量提升
函数提升
函数声明优先级高于变量声明--- 举例如下:
foo(); // foo2 var foo = function() { console.log('foo1'); } foo(); // foo1,foo重新赋值 function foo() { console.log('foo2'); } foo(); // foo1
1
2
3
4
5
6
7
8
9
10
11
12
函数上下文: 在函数上下文中,用活动对象(activation object, AO)来表示变量对象。
活动对象和变量对象的区别在于
- 1、变量对象(VO)是规范上或者是JS引擎上实现的,并不能在JS环境中直接访问。
- 2、当进入到一个执行上下文后,这个变量对象才会被激活,所以叫活动对象(AO),这时候活动对象上的各种属性才能被访问。
执行过程两个阶段:
- 进入执行上下文
- 代码执行
总结如下:
1、全局上下文的变量对象初始化是全局对象
2、函数上下文的变量对象初始化只包括 Arguments 对象
3、在进入执行上下文时会给变量对象添加形参、函数声明、变量声明等初始的属性值
4、在代码执行阶段,会再次修改变量对象的属性值
# 6.JavaScript深入之内存空间详细图解
JavaScript深入之内存空间详细图解 (opens new window)
变量的存放:
基本数据类型(Undefined、Null、Boolean、Number 、String和Symbol)存放在栈中,对象的地址存放在栈中,对象的数值放在堆中。当查询引用类型的变量时, 先从栈中读取内存地址, 然后再通过地址找到堆中的值。
栈快堆慢。
var a = { name: '前端开发' }
var b = a;
a = null;
a // null
b // { name: '前端开发' }
2
3
4
5
6
null是基本类型,a = null之后只是把a存储在栈内存中地址改变成了基本类型null,并不会影响堆内存中的对象,所以b的值不受影响
# 7.MVVM
什么是 mvvm?:
MVVM 是 Model-View-ViewModel 的缩写。mvvm 是一种设计思想。Model 层代表数据模型,也可以在 Model 中定义数据修改和操作的业务逻辑;View 代表 UI 组件,它负责将数据模型转化成 UI 展现出来,ViewModel 是一个同步 View 和 Model 的对象。
在 MVVM 架构下,View 和 Model 之间并没有直接的联系,而是通过 ViewModel 进行交互,Model 和 ViewModel 之间的交互是双向的, 因此 View 数据的变化会同步到 Model 中,而 Model 数据的变化也会立即反应到 View 上。
ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来,而 View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作 DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理。
mvvm 和 mvc 区别?:
mvc 和 mvvm 其实区别并不大。都是一种设计思想。主要就是 mvc 中 Controller 演变成 mvvm 中的 viewModel。mvvm 主要解决了 mvc 中大量的 DOM 操作使页面渲染性能降低,加载速度变慢,影响用户体验。 和当 Model 频繁发生变化,开发者需要主动更新到 View 。
# 8.垃圾回收算法
垃圾回收算法:
- 引用计数
现代浏览器已不再使用,IE依旧使用
引用计数算法定义“内存不再使用”的标准很简单,就是看一个对象是否有指向它的引用。如果没有其他对象指向它了,说明该对象已经不再需要了。
引用计数致命————循环引用:如果两个对象相互引用,尽管他们已不再使用,但是垃圾回收器不会进行回收,最终可能会导致内存泄露
var div = document.createElement("div");
div.onclick = function() {
console.log("click");
};
2
3
4
上面的就是一个循环引用。
变量div有事件处理函数的引用,同时事件处理函数也有div的引用,因为div变量可在函数内被访问,所以循环引用就出现了。
- 标记清除(常用)
标记清除算法将“不再使用的对象”定义为“无法到达的对象”。即从根部(在JS中就是全局对象)出发定时扫描内存中的对象,凡是能从根部到达的对象,保留。那些从根部出发无法触及到的对象被标记为不再使用,稍后进行回收。
现在对于主流浏览器来说,只需要切断需要回收的对象与根部的联系。
算法步骤:
- 1)、垃圾回收器创建了一个“roots”列表。roots 通常是代码中全局变量的引用。JavaScript 中,“window” 对象是一个全局变量,被当作 root 。window 对象总是存在,因此垃圾回收器可以检查它和它的所有子对象是否存在(即不是垃圾);
- 2)、所有的 roots 被检查和标记为激活(即不是垃圾)。所有的子对象也被递归地检查。从 root 开始的所有对象如果是可达的,它就不被当作垃圾。
- 3)、所有未被标记的内存会被当做垃圾,收集器现在可以释放内存,归还给操作系统了。
最常见的内存泄露一般都与DOM元素绑定有关:
email.message = document.createElement(“div”);
displayList.appendChild(email.message)
// 稍后从displayList中清除DOM元素
displayList.removeAllChildren();
2
3
4
上面代码中,div元素已经从DOM树中清除,但是该div元素还绑定在email对象中,所以如果email对象存在,那么该div元素就会一直保存在内存中
# 9.问答
# 1). const 声明一个只读的常量,那可以修改const的值?
const foo = {};
// 为 foo 添加一个属性,可以成功
foo.prop = 123;
foo.prop // 123
// 将 foo 指向另一个对象,就会报错
foo = {}; // TypeError: "foo" is read-only
2
3
4
5
6
7
8
const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心
# 2).从内存来看 null 和 undefined 本质的区别是什么?
给一个全局变量赋值为null,相当于将这个变量的指针对象以及值清空,如果是给对象的属性 赋值为null,或者局部变量赋值为null,相当于给这个属性分配了一块空的内存,然后值为null, JS会回收全局变量为null的对象。
给一个全局变量赋值为undefined,相当于将这个对象的值清空,但是这个对象依旧存在,如果是给对象的属性赋值 为undefined,说明这个值为空值
声明了一个变量,但未对其初始化时,这个变量的值就是undefined,它是 JavaScript 基本类型 之一。
对于尚未声明过的变量,只能执行一项操作,即使用typeof操作符检测其数据类型,使用其他的操作都会报错。
值 null 特指对象的值未设置,它是 JavaScript 基本类型 之一。
值 null 是一个字面量,它不像undefined 是全局对象的一个属性。null 是表示缺少的标识,指示变量未指向任何对象。
// foo不存在,它从来没有被定义过或者是初始化过:
foo;
"ReferenceError: foo is not defined"
// foo现在已经是知存在的,但是它没有类型或者是值:
var foo = null;
console.log(foo); // null
2
3
4
5
6
7
# 10.四种常见的JS内存泄漏
JavaScript深入之4类常见内存泄漏及如何避免 (opens new window)
- 1).意外的全局变量
解决办法:在 JavaScript 文件头部加上 'use strict',使用严格模式避免意外的全局变量,此时上例中的this指向undefined。如果必须使用全局变量存储大量数据时,确保用完以后把它设置为 null 或者重新定义。
- 2).被遗忘的计时器或回调函数
对于定时器----及时清理定时器;
var element = document.getElementById('button');
function onClick(event) {
element.innerHTML = 'text';
}
element.addEventListener('click', onClick);
2
3
4
5
现代的浏览器(包括 IE 和 Microsoft Edge)使用了更先进的垃圾回收算法(标记清除),已经可以正确检测和处理循环引用了。即回收节点内存时,不必非要调用 removeEventListener 了
- 3).脱离 DOM 的引用
var elements = {
button: document.getElementById('button'),
image: document.getElementById('image'),
text: document.getElementById('text')
};
function doStuff() {
image.src = 'http://some.url/image';
button.click();
console.log(text.innerHTML);
// 更多逻辑
}
function removeButton() {
// 按钮是 body 的后代元素
document.body.removeChild(document.getElementById('button'));
// 此时,仍旧存在一个全局的 #button 的引用
// elements 字典。button 元素仍旧在内存中,不能被 GC 回收。
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- 4).闭包
闭包的关键是匿名函数可以访问父级作用域的变量。