Giter Site home page Giter Site logo

front-end-study-note's People

Watchers

 avatar  avatar

front-end-study-note's Issues

You-Dont-Know-JS 学习笔记(一)

作用域

  • 编译原理

    • 尽管通常把javascript归为动态语言或解释型语言,但是实际上是一门编译语言
      • 因为与其他语言不同,JavaScript 的编译过程不是发生在构建之前的
      • 对于 JavaScript 来说,大部分情况下编译发生在代码执行前的几微秒(甚至更短!)的时间内
        • JIT对重复执行的代码编译成机器码达到优化
    • 引擎、编译器与作用域
      • 引擎: 负责整个编译过程(领导)
      • 编译器: 做语法分析代码生成等活儿(技术部的)
      • 作用域: 管理成员结构归属等(人事)
  • 编译器

    • LHS && RHS
      • 如果查找的目的是对变量进行赋值,那么就会使用 LHS 查询;如果目的是获取变量的值,就会使用 RHS 查询。( 和setget 是什么关系?)

词法作用域

无论函数在哪里被调用,如何被调用,词法作用域 由函数被声明时所处的位置决定

遮蔽效应

内层作用域定义同名变量标识符,覆盖外层作用域的变量

全局(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影响了对词法进行静态分析,导致优化无法进行,影响性能。

最小授权(最小暴露)原则

控制好作用域,只暴露必要的内容而把其他内容都隐藏起来。

You-Dont-Know-JS学习笔记(对象篇)

创建一个空对象:

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属性,无限循环
      }
    }
  }
}

Object.defineProperty

Object.defineProperty(obj, prop, descriptor)

[ descriptor ]:

{

​	configurable: false | true, // 是否可以被改变,删除

​	enumberable: false | true, // 枚举属性

​	value: undefined | .. ,

​	writable: false | true, // 可以被`赋值操作符`运算(与configurable有关联关系)

​	get: undefined | func,

​	set: undefined | func

}

Decorator

修饰器对类的行为的改变,是代码编译时发生的,而不是在运行时。这意味着,修饰器能在编译阶段运行代码。也就是说,修饰器本质就是编译时执行的函数

数据响应系统(Vue)

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数值并没有真正发生改变,这里稍稍加了几行

CSS动画 性能调优 学习笔记

常见CSS的优化技巧

  • 只改变 transformopacity ,尽量不改变其他属性(避免重新计算布局 [ reflow ] )
  • 对设置动画的元素应用 transform: translate3d(0, 0, 0)will-change: transform 等, 开启硬件加速
    • will-change
      • 提示浏览器哪些属性将会被改变,做出优化
      • e.g. will-change: left, topwill-change: opacity
  • 动画元素使用 fixedabsolute 定位(避免重新计算布局 [ reflow ])
  • 对动画元素尽量使用高一些的 z-index ,减少复合层的使用,在单一层进行重绘
  • ...

触发重布局的属性

  • 盒子模型相关:
    • width
    • height
    • padding
    • margin
    • display
      • mdn文档 因为在面试时特别被问到display的取值,所以特地查了一下,真的挺多的,主要要关注一下关于grid的吧,但是好像大多数浏览器不支持,关于table因为是css2.1的,也没有特别关注
        • Formal syntax: none | inline | block | list-item | inline-list-item | inline-block | inline-table | table | table-cell | table-column | table-column-group | table-footer-group | table-header-group | table-row | table-row-group | flex | inline-flex | grid | inline-grid | run-in | ruby | ruby-base | ruby-text | ruby-base-container | ruby-text-container | contents
    • border-width
    • border
    • min-height
  • 定位属性及浮动也会触发重布局:
    • top
    • bottom
    • left
    • right
    • position
    • float
    • clear
  • 改变节点内部文字结构也会触发重布局:
    • text-align
    • overflow-y
    • font-weight
    • overflow
    • font-family
    • line-height
    • vertival-align
    • white-space
    • font-size

硬件加速

在没有硬件加速的情况下,浏览器通常是依赖于CPU来渲染生成网页的内容,大致的做法是遍历这些层,然后按照顺序把这些层的内容依次绘制在一个内部存储空间上(例如bitmap),最后把这个内部表示显示出来,这种做法就是软件渲染(software rendering)

  • 使用 transform: translate3d(0, 0, 0) 开启硬件加速, 可能得到性能提升,也可能带来严重的性能问题 (?没有亲自考证过有什么性能问题,但是从看过的blog中基本是认可这种做法的)

transform 与 opacity

  • 尽量避免重新布局(reflow),可以把背景(不动的部分)和前景(需要做动画的)部分放在不同的层级
    • 如果使用fixedabsolute配合left | right | top | bottom定位,浏览器不能确定属性的变化与布局无关,还是会重新布局。
    • 可以肯定的是transformopacity与布局是无关的。
      • 可以单独抽象出一层交给GPU来处理, 好处是 细腻(GPU能做到亚像素级精度,且对GPU来说不费劲),流畅(不受其它运算密集的JS任务影响,动画交给GPU了,与CPU无关)

GPU合成的代价

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,如果复合层太多太大,内存会被迅速消耗,然后掉帧(卡顿、闪烁)现象,甚至浏览器/应用崩溃也就很合理了

创建复合层

  • 3D transforms: translate3d, translateZ and so on;
  • <video>, <canvas> and <iframe> elements;
  • animation of transform and opacity via Element.animate();
  • animation of transform and opacity via СSS transitions and animations;
  • position: fixed;
  • will-change;
  • filter;

隐式创建的复合层

  • 位于复合层之上的元素会被创建复合层(B的z-index大于A,对A做动画,B也会被塞进独立的复合层)
    • 把动画放在**高z-index**的层级中

硬件加速的优缺点

优点:

  • 动画非常流畅,能达到60fps
  • 动画执行过程在独立线程里,不受计算密集的JS任务影响(CPU && GPU)

缺点:

  • 把元素塞进复合层时需要额外重绘,有时很慢(可能需要整页重绘)
  • 复合层数据传递给GPU有额外时耗,取决于复合层的数量和大小,这在中低端设备可能会导致闪烁
  • 每个复合层都要消耗一部分内存,移动设备上内存很贵,过多占用会导致浏览器/应用崩溃
  • 存在隐式复合层的问题,不注意的话内存飙升
  • 文字模糊,元素有时会变形

性能优化技巧

  • 尽量避免隐式复合层
    • 给动画元素应用高z-index,最好直接作为body的子元素,对于嵌套很深的动画元素,可以复制一个到body下,仅用于实现动画效果
    • 给动画元素应用will-change,浏览器会提前把这些元素塞进复合层,可以让动画开始/结束时更流畅些,但不能滥用,在不需要的时候赶紧去掉,减少内存消耗
  • 只改变transformopacity
    • 能用transformopacity优先用,不能用的话想办法用,比如背景色渐变,可以用盖在上面的伪元素背景色opacity动画模拟;box-shadow动画可以用铺在下面的伪元素opacity动画模拟,这些曲折的实现方式能带来显著性能提升
  • 减少复合层的大小
    • 小元素放大展示,减小widthheight(计算参考 width * height * tunnerSum),减少传递给GPU的数据,由GPU做scale放大展示,视觉效果无差异(多用于纯色背景元素,对不太重要的图片也可以进行5%到10%的宽高压缩)
    • 最终显示的两个红色块在视觉上没有差异,但减小了90%的内存消耗 :
<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>
  • 考虑对子元素动画与容器动画

    • 容器动画可能存在不必要的内存消耗,比如子元素之间的空隙,也会被当做有效数据发送给GPU,如果对各个子元素分别应用动画,就能避免这部分的内存消耗
    • 例如12道太阳光线旋转,转容器就把容器整张图都发送给GPU,单独转12道光线就去掉了光线之间的11条空隙,能够节省一半内存
  • 早早关注复合层的数量和大小

    • 从一开始就关注复合层,尤其是隐式创建的复合层,避免后期优化影响布局
    • 复合层的大小比数量影响更大,但浏览器会做一些优化操作,把几个复合层整合成一个,叫Layer Squashing,但有时一个大复合层比几个小复合层消耗的内存更多,有必要的话可以手动去掉这种优化:
    • translateZ(0.0001px), translateZ(0.0002px) // 给每个元素应用不同的translateZ

原文 CSS动画与GPU 部分整合和注解
前端性能优化(css篇)

JIT(just-in-time)

解释执行效率低的主要原因之一在于,相同的语句被反复解释,因此优化的思路是动态的观察哪些代码是经常被调用的。对于那些被高频率调用的代码,可以用编译器把它编译成机器码并且缓存下来,下次执行的时候就不用重新解释,从而提升速度。这就是 JIT(Just-In-Time) 的技术原理。

但凡基于缓存的优化,一定会涉及到缓存命中率的问题。在 JavaScript 中,即使是同一段代码,在不同上下文中生成的机器码也不一定相同。比如这个函数:

function add(a, b) {
    return a + b;
}

如果这里的 a 和 b 都是整数,可以想见最终的代码一定是汇编中的 add 命令。如果类似的加法运算调用了很多次,解释器可能会认为它值得被优化,于是编译了这段代码。但如果下一次调用的是 add("hello", "world"),之前的优化就无效了,因为字符串加法的实现和整数加法的实现完全不同。

于是优化后的代码(二进制格式)还得被还原成原先的形式(字符串格式),这样的过程被称为去优化。反复的优化 -> 去优化 -> 优化 …… 非常耗时,大大降低了引入 JIT 带来的性能提升。

JIT 理论上给传统的 JavaScript 带了了 20-40 倍的性能提升,但由于上述去优化的存在,在实际运行的过程中远远达不到这个理论上的性能天花板。

原文

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.