kevinaskin / front-end-study-note Goto Github PK
View Code? Open in Web Editor NEWcollect && write down for remember
collect && write down for remember
编译原理
javascript
归为动态语言或解释型语言,但是实际上是一门编译语言
语法分析
、代码生成
等活儿(技术部的)编译器
LHS
&& RHS
LHS
查询;如果目的是获取变量的值,就会使用 RHS
查询。( 和set
, get
是什么关系?)无论函数在哪里被调用,如何被调用,词法作用域只 由函数被声明时所处的位置决定
内层作用域定义同名变量标识符,覆盖外层作用域的变量
全局(e.g. 浏览器中的window)属性,可以通过
window.a
访问被遮蔽的a
变量
eval(..)
对词法作用域进行修改
function foo(str, a) {
eval( str )
console.log(a, b)
}
var b = 2
foo("var b = 3", 1) // 1, 3 而不是 1, 2
eval(..)
和with
影响了对词法进行静态分析,导致优化无法进行,影响性能。
控制好作用域,只暴露必要的内容而把其他内容都隐藏起来。
let a = {}
// 连接到Object.prototype上
let b = Object.create(null)
// 没有连接到Object.prototype上,
// 不能使用一些方法e.g. b.hasOwnProperty
let myObject = { a: 2 }
("a" in myObject) // true
("b" in myObject) // false
// 会检查原型链
// in 操作符检查的是属性名,对应到数组中就是下标(是否存在)而不是是否存在
myObject.hasOwnProperty('a') // true
myObject.hasOwnProperty('b') // false
// 不会检查原型链
enumberable
:for (.. in ..) {
.. // 只能遍历到 enumberable 为 true 的属性
}
// 检验是否可以枚举,并且不会检查原型链 Obj.propertyIsEnumerable() 返回值为一个布尔值
Object.keys(..)
// 返回一个数组包含所有可枚举属性
Object.getOwnPropertyNames(..)
// 返回所有属性名组成的数组,包括不可枚举的属性
forEach(..)
// 遍历数组所有值
every(..)
// 直到回调函数返回false
some(..)
// 回调函数返回true为止
// every和some都会提前终止遍历,类似break
for(let value of arr) {..}
// 遍历值,而不是下标
// for..of 循环首先会向被访问对象请求一个迭代器对象,
// 然后通过调用迭代器对象的 next() 方法来遍历所有返回值。
// 数组有内置的 @@iterator,
// 因此 for..of 可以直接应用在数组上。
// 我们使用内置的 @@ iterator 来手动遍历数组,
// 看看它是怎么工作的:
var myArray = [ 1, 2, 3 ];
var it = myArray[Symbol.iterator]();
it.next(); // { value:1, done:false }
it.next(); // { value:2, done:false }
it.next(); // { value:3, done:false }
it.next(); // { done:true }
// 自行编写迭代器
var randoms = {
[Symbol.iterator]: function() {
return {
next: function() {
return { value: Math.random() } // 不编写done属性,无限循环
}
}
}
}
[ descriptor ]:
{
configurable: false | true, // 是否可以被改变,删除
enumberable: false | true, // 枚举属性
value: undefined | .. ,
writable: false | true, // 可以被`赋值操作符`运算(与configurable有关联关系)
get: undefined | func,
set: undefined | func
}
修饰器对类的行为的改变,是代码编译时发生的,而不是在运行时。这意味着,修饰器能在编译阶段运行代码。也就是说,修饰器本质就是编译时执行的函数。
class Observer {
constructor(data) {
this.walk(data) // 遍历data对象
}
walk(data) {
Object.keys(data).forEach(key => {
console.log('walk =>', data, key, data[key])
defineReactive(data, key, data[key])
})
}
}
// 添加get, set
function defineReactive(data, key, val) {
// 递归
if (Object.prototype.toString.call(val) !== '[object Object]') new Observer(val)
let dep = new Depend()
let _value = val
Object.defineProperty(data, key, {
enumberable: true,
configurable: true,
get() {
console.log('[get '+ key +']: ', _value)
dep.add(dep.target)
return _value
},
set(newVal) {
if (val === newVal) return
console.log('[set '+ key +']: ', newVal)
new Observer(newVal)
dep.trigger()
_value = newVal
}
})
}
// 记录依赖,触发依赖上的回调函数
class Depend {
constructor() {
this.dependList = []
}
add() {
console.log('depList add =>', Depend.target)
this.dependList.push(Depend.target) // Depend.target -> new Watch()
}
trigger() {
for (let i = 0; i < this.dependList.length; i++) {
this.dependList[i].cb()
}
}
}
class Watch {
constructor(exp, cb) {
this.exp = exp
this.cb = cb
Depend.target = this
data[exp] // 触发get函数
}
}
let data = {
a: 1,
b: 2
}
new Observer(data)
new Watch('a', () => {
console.log('a has changed')
data.b = 1
})
setTimeout(() => {
data.a = 10
setTimeout(() => {
console.log(JSON.stringify(data, null, 2))
}, 1000)
}, 3000)
参考Vue源码学习
原文中data数值并没有真正发生改变,这里稍稍加了几行
transform
、 opacity
,尽量不改变其他属性(避免重新计算布局 [ reflow ] )transform: translate3d(0, 0, 0)
、 will-change: transform
等, 开启硬件加速
will-change: left, top
、 will-change: opacity
…fixed
、 absolute
定位(避免重新计算布局 [ reflow ])z-index
,减少复合层的使用,在单一层进行重绘display
的取值,所以特地查了一下,真的挺多的,主要要关注一下关于grid
的吧,但是好像大多数浏览器不支持,关于table
因为是css2.1的,也没有特别关注
在没有硬件加速的情况下,浏览器通常是依赖于CPU来渲染生成网页的内容,大致的做法是遍历这些层,然后按照顺序把这些层的内容依次绘制在一个内部存储空间上(例如bitmap),最后把这个内部表示显示出来,这种做法就是软件渲染(software rendering)
transform: translate3d(0, 0, 0)
开启硬件加速, 可能得到性能提升,也可能带来严重的性能问题 (?没有亲自考证过有什么性能问题,但是从看过的blog中基本是认可这种做法的)fixed
与absolute
配合left | right | top | bottom
定位,浏览器不能确定属性的变化与布局无关,还是会重新布局。transform
与opacity
与布局是无关的。
GPU是独立的一部分,有自己的处理器、内存核数据处理模型,那么意味着通过CPU在内存里创建的图像数据无法直接与GPU共享,需要打包发送给GPU,GPU收到后才能执行我们期望的一系列操作,这个过程需要时间,而打包数据需要内存
这个过程取决于
相对于数量,复合层的大小影响更大一些,例如:
.rect {
width: 320px;
height: 240px;
background: #f00;
}
这个红块如果要发送给GPU的话,需要的存储空间是:320 × 240 × 3 = 230400B = 225KB
(rgb需要3个字节),如果图像含有透明部分,就需要320 × 240 × 4 = 307200B = 300KB
这样一个不起眼的小红块就需要2、300KB,页面动辄几十上百个元素,占全屏半屏的元素也不少,如果都作为复合层,交给GPU,内存消耗可想而知,所以一些很极端的硬件加速场景性能非常差
对于1GB RAM的设备,去掉系统和后台进程的1/3
,再去掉浏览器和当前页面的1/3
,实际能用的只有200到300MB,如果复合层太多太大,内存会被迅速消耗,然后掉帧(卡顿、闪烁)现象,甚至浏览器/应用崩溃也就很合理了
<video>
, <canvas>
and <iframe>
elements;被隐式创建的复合层
z-index
**的层级中优点:
- 动画非常流畅,能达到60fps
- 动画执行过程在独立线程里,不受计算密集的JS任务影响(
CPU
&&GPU
)
缺点:
- 把元素塞进复合层时需要额外重绘,有时很慢(可能需要整页重绘)
- 复合层数据传递给GPU有额外时耗,取决于复合层的数量和大小,这在中低端设备可能会导致闪烁
- 每个复合层都要消耗一部分内存,移动设备上内存很贵,过多占用会导致浏览器/应用崩溃
- 存在隐式复合层的问题,不注意的话内存飙升
- 文字模糊,元素有时会变形
z-index
,最好直接作为body
的子元素,对于嵌套很深的动画元素,可以复制一个到body
下,仅用于实现动画效果will-change
,浏览器会提前把这些元素塞进复合层,可以让动画开始/结束时更流畅些,但不能滥用,在不需要的时候赶紧去掉,减少内存消耗transform
和opacity
transform
、opacity
优先用,不能用的话想办法用,比如背景色渐变,可以用盖在上面的伪元素背景色opacity
动画模拟;box-shadow
动画可以用铺在下面的伪元素opacity
动画模拟,这些曲折的实现方式能带来显著性能提升width
、height
(计算参考 width * height * tunnerSum
),减少传递给GPU的数据,由GPU做scale放大展示,视觉效果无差异(多用于纯色背景元素,对不太重要的图片也可以进行5%到10%的宽高压缩)<div id="a"></div>
<div id="b"></div>
<style>
#a, #b {
will-change: transform;
background-color: #f00;
}
#a {
width: 100px;
height: 100px;
}
#b {
width: 10px;
height: 10px;
transform: scale(10);
}
</style>
考虑对子元素动画与容器动画
早早关注复合层的数量和大小
translateZ(0.0001px), translateZ(0.0002px)
// 给每个元素应用不同的translateZ原文 CSS动画与GPU 部分整合和注解
前端性能优化(css篇)
解释执行效率低的主要原因之一在于,相同的语句被反复解释,因此优化的思路是动态的观察哪些代码是经常被调用的。对于那些被高频率调用的代码,可以用编译器把它编译成机器码并且缓存下来,下次执行的时候就不用重新解释,从而提升速度。这就是 JIT(Just-In-Time) 的技术原理。
但凡基于缓存的优化,一定会涉及到缓存命中率的问题。在 JavaScript 中,即使是同一段代码,在不同上下文中生成的机器码也不一定相同。比如这个函数:
function add(a, b) {
return a + b;
}
如果这里的 a 和 b 都是整数,可以想见最终的代码一定是汇编中的 add 命令。如果类似的加法运算调用了很多次,解释器可能会认为它值得被优化,于是编译了这段代码。但如果下一次调用的是 add("hello", "world")
,之前的优化就无效了,因为字符串加法的实现和整数加法的实现完全不同。
于是优化后的代码(二进制格式)还得被还原成原先的形式(字符串格式),这样的过程被称为去优化。反复的优化 -> 去优化 -> 优化
…… 非常耗时,大大降低了引入 JIT 带来的性能提升。
JIT 理论上给传统的 JavaScript 带了了 20-40 倍的性能提升,但由于上述去优化的存在,在实际运行的过程中远远达不到这个理论上的性能天花板。
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.