学习篇——闭包/虚拟DOM

本文介绍了闭包的概念,特点以及应用场景,强调了闭包在内存管理和防止全局变量污染上的作用。此外,文章讨论了虚拟DOM的原理,解释了为何使用虚拟DOM能提高性能并实现跨平台渲染。最后,探讨了操作真实DOM的高成本,以及回流和重绘的影响。
摘要由CSDN通过智能技术生成

闭包篇

闭包这个概念一直没有被明确的定义, 每个人心中都可以有自己的理解,希望通过下面的学习介绍,大家可以有个大概的理解。
有什么错误的地方,还希望大佬留言提出来哦~

  • 什么是闭包?闭包有什么作用?

百度百科:
     闭包 就是能够读取其他函数内部变量的函数。例如在javascript中,只有函数内部的子函数才能读取 局部变量,所以闭包可以理解成“定义在一个 函数内部的函数“。在本质上,闭包是将函数内部和函数外部连接起来的桥梁。
简单概括:

    闭包 就是能够读取其他函数内部变量的函数,或者子函数在外调用,子函数所在的父函数的作用域不会被释放。

闭包就是由函数及其相关的引用环境组合而成的实体(即:闭包=函数+引用环境)。

  • 闭包的写法

//在函数里定义一个匿名函数,并且调用它
function printStr() {
    $func = function( $str ) {
        echo $str;
    };
    $func( 'some string' );
}printStr();

//例二
//在函数中把匿名函数返回,并且调用它
function getPrintStrFunc() {
    $func = function( $str ) {
        echo $str;
    };
    return $func;
}
$printStrFunc = getPrintStrFunc();
$printStrFunc( 'some string' );

//例三
//把匿名函数当做参数传递,并且调用它
function callFunc( $func ) {
    $func( 'some string' );
}
$printStrFunc = function( $str ) {
    echo $str;
};
callFunc( $printStrFunc );
//也可以直接将匿名函数进行传递。如果你了解js,这种写法可能会很熟悉
callFunc( function( $str ) {
    echo $str;
} );

例子:

package main

import (
	"fmt"
)
//匿名函数外的变量
func f1(n int) func() {
	n++
	return func() {
		fmt.Println(n)
	}
}
//匿名函数内的变量
func f2(n int) func() {
	return func() {
		n++
		fmt.Println(n)
	}
}
//传址
func f3(n *int) func()  {
	*n++
	fmt.Println("n=",*n)
	return func() {
		*n++
		fmt.Println(*n)
	}
}
func f4(n int) func(m int) {
	r:=1
	return func(m int){
		r += n+m
		fmt.Println(r)
	}
}

var a  = 20
var b = 10
var c = 0
var d = 30
var e = 40
func main() {
	func1:=f1(a) //n++ 执行20+1,仅执行一次,实例化(初始化)闭包,匿名函数部分暂不执行,匿名函数以外的代码执行
	fmt.Println(func1)  //0x48e3d0  在这儿已经定义了n=20 ,然后执行++ 操作,所以是21 。
	func1()     //21 仅对闭包的的匿名函数部分的调用,匿名函数可使用所处环境的变量,所以仍是21
	func1()     //21 仅对闭包的的匿名函数部分的调用,匿名函数可使用所处环境的变量,所以仍是21
	func1()     //21 仅对闭包的的匿名函数部分的调用,匿名函数可使用所处环境的变量,所以仍是21

	func2:=f2(b) //仅执行一次,实例化(初始化)闭包,实例化(初始化)闭包,匿名函数部分暂不执行,匿名函数以外的代码执行
	fmt.Println(func2)  //0x48e340   这儿定义了n 为10
	func2()       //11  仅对匿名函数调用,执行n++, 即使本次调用完成,匿名函数中引用的变量不会被销毁,一直保持,(普通函数调用完一次会被重置)
	func2()       //12  仅对匿名函数调用,执行n++, 即使本次调用完成,匿名函数中引用的变量不会被销毁,(普通函数调用完一次会被重置)
	func2()       //13 仅对匿名函数调用,执行n++, 即使本次调用完成,匿名函数中引用的变量不会被销毁,(普通函数调用完一次会被重置)

	func3 := f3(&c) //仅执行一次,实例化(初始化)闭包,实例化(初始化)闭包,匿名函数部分暂不执行,匿名函数以外的代码执行
	func3()	//2 仅对匿名函数调用, 即使本次调用完成,匿名函数中引用的变量不会被销毁,一直保持,(普通函数调用完一次会被重置)
	func3()	//3 仅对匿名函数调用, 即使本次调用完成,匿名函数中引用的变量不会被销毁,一直保持,(普通函数调用完一次会被重置)
	func3()	//4 仅对匿名函数调用, 即使本次调用完成,匿名函数中引用的变量不会被销毁,一直保持,(普通函数调用完一次会被重置)

	func4 := f4(d) //仅执行一次,实例化(初始化)闭包,实例化(初始化)闭包,匿名函数部分暂不执行,匿名函数以外的代码执行
	func4(e)  //71
	func4(e) //141
	func5 := f4(d) //实例化一个新的闭包,和其他实例的闭包没有相互关系
	func5(e) //71
	fmt.Println(a,b,c,d,e) //20,10,4,30,40  从结果可以看出,func1,func2,func4对引用环境中的原变量不产生影响,func3改变了引用环境的原变量
}

* 注意: 通过这个例子可以清晰看到,闭包内存保持的变量在匿名函数中,被外部传递的变量在这两种情况都不受影响】

由上得出闭包的特点

  • 闭包的特点

  1. 闭包是由函数及其相关的引用环境组合而成的实体(即:闭包=函数+引用环境)。
  2. 闭包的三种写法可以看出 ,在一个函数里执行一个匿名函数(1.在函数中执行,2.返回匿名函数执行,3.当参数传递后再调用执行)
  3. 可以使用当前声明处可见的所有变量
  4. 可以存在多个实例,将这个匿名函数变量赋值给其他的变量,将产生新的实例
  5. 在同一变量实例中,这个实例持续存在,即使一次调用结束,在下次调用仍然继承上次调用的结果
  6. 同一个实例,内存共享,多个实例相互不受影响,
  7. 一般函数在执行完一瞬间会销毁其执行环境.匿名函数用完即销毁,而闭包一次调用执行完, 闭包会一直存在内存中,垃圾收集器不会销毁闭包占用的内存。
     

闭包就是可以创建一个独立的环境,每个闭包里面的环境都是独立的,并且互不干扰。闭包会发生内存泄漏,每次外部函数执行的时候,外部函数的引用地址不同,都会重新创建一个新的地址。但凡是当前活动对象中有被内部子集引用的数据,那么这个时候,这个数据不删除,保留一根指针给内部活动对象。大概意思就是说当外部函数运行结束甚至销毁时,局部的变量key=value,尽管key被垃圾回收机制给回收了,但是value仍不会被回收,会变成一个自由变量留下引用的指针。
 

这里可以点击下面链接查看大佬详解:

闭包的优缺点

总结:

  使用闭包主要是为了设计私有的方法和变量。闭包的优点是可以避免全局变量的污染,缺点是闭包会常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。在js中,函数即闭包,只有函数才会产生作用域的概念!


虚拟DOM篇

 

  • 什么是虚拟DOM?

虚拟 DOM (Virtual DOM )这个概念相信大家都不陌生,从 React 到 Vue ,虚拟 DOM 为这两个框架都带来了跨平台的能力(React-Native 和 Weex)

实际上它只是一层对真实DOM的抽象,以JavaScript 对象 (VNode 节点) 作为基础的树,用对象的属性来描述节点,最终可以通过一系列操作使这棵树映射到真实环境上

在Javascript对象中,虚拟DOM 表现为一个 Object对象。并且最少包含标签名 (tag)、属性 (attrs) 和子元素对象 (children) 三个属性,不同框架对这三个属性的名命可能会有差别

创建虚拟DOM就是为了更好将虚拟的节点渲染到页面视图中,所以虚拟DOM对象的节点与真实DOM的属性一一照应

在vue中同样使用到了虚拟DOM技术

示例:

定义真实DOM:

<div id="app">
    <p class="p">节点内容</p>
    <h3>{{ foo }}</h3>
</div>

实例化vue:

const app = new Vue({
    el:"#app",
    data:{
        foo:"foo"
    }
})

观察render的render,我们能得到虚拟DOM:

(function anonymous() {
 with(this){return _c('div',{attrs:{"id":"app"}},[_c('p',{staticClass:"p"},
       [_v("节点内容")]),_v(" "),_c('h3',[_v(_s(foo))])])}})

  • 为什么要用虚拟DOM来描述真实的DOM呢?

创建真实DOM成本比较高,而如果用js对象来描述一个dom节点,成本比较低,另外我们在频繁操作dom是一种比较大的开销。所以建议用虚拟dom来描述真实dom。

真实的DOM节点,哪怕一个最简单的div也包含着很多属性,可以打印出来直观感受一下:

由此可见,操作DOM的代价仍旧是昂贵的,频繁操作还是会出现页面卡顿,影响用户的体验

举个例子:

你用传统的原生api或jQuery去操作DOM时,浏览器会从构建DOM树开始从头到尾执行一遍流程

当你在一次操作时,需要更新10个DOM节点,浏览器没这么智能,收到第一个更新DOM请求后,并不知道后续还有9次更新操作,因此会马上执行流程,最终执行10次流程

而通过VNode,同样更新10个DOM节点,虚拟DOM不会立即操作DOM,而是将这10次更新的diff内容保存到本地的一个js对象中,最终将这个js对象一次性attach到DOM树上,避免大量的无谓计算

 很多人认为虚拟 DOM 最大的优势是 diff 算法,减少 JavaScript 操作真实 DOM 的带来的性能消耗。虽然这一个虚拟 DOM 带来的一个优势,但并不是全部。虚拟 DOM 最大的优势在于抽象了原本的渲染过程,实现了跨平台的能力,而不仅仅局限于浏览器的 DOM,可以是安卓和 IOS 的原生组件,可以是近期很火热的小程序,也可以是各种GUI

在这里延伸几个相关问题:

1.为什么操作真实DOM的成本比较高?

(1) dom 树的实现模块和 js 模块是分开的这些跨模块的通讯增加了成本

(2) dom 操作引起的浏览器的回流和重绘,使得性能开销巨大。

原本在 pc 端是没有性能问题的,因为pc端的计算能力强,但是随着移动端的发展,越来越多的网页在智能手机上运行,而手机的性能参差不齐,会有性能问题。
我们之前用jquery在pc端写那些商城页面都没有问题,但放到移动端浏览器访问之后会发现除了首页会出现白屏之外在其他页面的操作并不流畅。

2.浏览器收到一个html页面是如何解析成页面呈现给用户的呢?

(1) 解析html:会按顺序解析。浏览器有专门的html解析器来解析HTML,并在解析的过程中构建DOM树

(2)构建dom树:它和步骤(1) 是同步进行,可以理解为边解析边构建。

(3)构建呈现树renderTree:将dom树与css结合,也就是将样式应用到dom节点上

(4)布局:计算呈现树节点的大小和位置,这一位置是递归进行的。

(5)绘制:布局完成后,便是将呈现树绘制出来显示在屏幕上。

这个具体的解析过程可参考:参考icon-default.png?t=M4ADhttps://www.cnblogs.com/dojo-lzz/p/3983335.html

3.什么是回流和重绘?

回流 reflow:当呈现树renderTree中的一部分或全部因为尺寸、布局、隐藏等改变改重新构建,称之为回流。
重绘:当呈现树renderTree中的一部分元素需要更新属性,而属性只会影响外观、风格而不影响布局,比如颜色、字体大小等,则称之为重绘。
我们之前用jquery时基本都是在操作dom。会频繁引起呈现树的重绘和回流,pc端处理能力还不错,但移动端性能就会很差。导致页面卡顿
 

  • 虚拟DOM的作用和类库

  • 虚拟DOM作用
  1. 可以维护视图和状态之间的关系
  2. 复杂视图情况下提升渲染性能
  3. 虚拟DOM除了可以渲染成DOM节点,还可以渲染到其他平台如ssr(nuxt.js/next.js)、原生应用(weex/rn)、小程序等,增加了跨平台能力
  • 开源库
  1. snabbdom
  2. vue 2.x就是使用的snabbdom并进行了一定的改造
  3. 代码量少
  4. 可以通过模块进行扩展
  5. 源码使用ts开发
  6. virtual-dom
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值