1 事件循环机制

1.1多线程的浏览器和单线程的JavaScript

众所周知,JS只有一个主线程,那么在执行所有任务的时候只能一个一个来吗?就像是排队看病一样,医生一次只能接待一个病人。那么如果这个病人需要去化验,等待结果的这段时间医生还是等待这个病人化验吗?还是会先接待后面的病人?JS有自己的机制来解决这个问题。

通俗来讲,操作分为:发出调用和得到结果。发出调用立刻得到结果是为同步,发出调用无法立刻得到结果,需要额外的操作才能得到结果是为异步。同步是调用之后一直等待,直到返回结果,异步则是调用之后需要通过一系列的手段才能拿到结果(发出调用,在拿到结果的这段事件可以介入其他任务)。

上面所说的一系列手段就是实现异步的方法,其中就包括:event loop。以及轮询、事件等。

轮询:就如同在点菜之后,不断去问服务员才有没有好。

事件:就如同点菜之后,不用不断的问,饭菜好了之后服务员自己会通知菜好了。

虽然JS是单线程的,但是t他的宿主浏览器却不是,一个浏览器中有多个常驻线程。

1.Js引擎线程:JavaScript引擎是基于事件驱动单线程执行的,JavaScript引擎一直等待着任务队列中任务的到来,然后加以处理,浏览器无论什么时候都只有一个JavaScript线程在运行JavaScript程序。

2.Gui渲染线程: GUI渲染线程负责渲染浏览器界面,当界面需要重绘(Repaint)或由于某种操作引发回流(Reflow)时,该线程就会执行。但需要注意,GUI渲染线程与JavaScript引擎是互斥的,当JavaScript引擎执行时GUI线程会被挂起,GUI更新会被保存在一个队列中等到JavaScript引擎空闲时立即被执行。

3.事件触发线程: 当一个事件被触发时该线程会把事件添加到待处理队列的队尾,等待JavaScript引擎的处理。这些事件可来自JavaScript引擎当前执行的代码块如setTimeout、也可来自浏览器内核的其他线程如鼠标点击、Ajax异步请求等,但由于JavaScript的单线程关系所有这些事件都得排队等待JavaScript引擎处理(当线程中没有执行任何同步代码的前提下才会执行异步代码)。

还会开辟一些其他线程,例如http请求线程等等(用完就关闭)

1.2 浏览器的事件循环(event loop)

1.函数入栈,当Stack中执行到异步任务的时候,就将他丢给WebAPIs,然后继续执行同步任务,直到Stack为空;

2.在此期间WebAPIs完成这个异步任务并产生一个事件,把这个事件(回调函数)放入CallbackQueue中等待(js主线程执行异步任务,就是在执行回调函数);

3.当执行栈为空时,Event Loop把Callback Queue中的一个任务放入Stack中,回到第1步。

#stack 执行栈,JS主线程来处理

#Event Loop事件循环是由javascript宿主环境(像浏览器)来轮询实现的;使得主线程从执行队列中不断获取任务(异步事件的回调)。

#WebAPIs是由C++实现的浏览器创建的线程,处理诸如DOM事件、http请求、定时器等异步事件;

#JavaScript 的并发模型基于"事件循环";

#Callback Queue(Event Queue 或者 Message Queue) 任务队列,存放异步事件的回调函数

2 事件机制

2.1 Dom节点的删除与事件绑定

在删除了Dom节点的时候,是否需要手动删除绑定在其上的事件。在删除一个节点的时候,浏览器会将删除掉的节点的兄弟指针和父指针置为null,并没有delete。所以此时,这个节点依然存在于内存中,事件依然存在。什么时候,这个节点会被析构呢?当发生GC回收的时候,会将不存在引用的Dom节点的内存释放掉,并删除map里面的数据。所以并不需要手动删除事件。

2.2 Dom的Level 0 事件

指的是dom节点的onclick onfocus等属性。

onClick 和 addEventLisener: onClick的绑定实际上仍然调用的是addEventLisener,只是在此之前会先clear掉上一次绑定的属性事件。所以onClick和addEvnetLisener并不冲突。

1.onclick事件在同一时间只能指向唯一对象

2.addEventListener给一个事件注册多个listener

3.addEventListener对任何DOM都是有效的,而onclick仅限于HTML

4.addEventListener可以控制listener的触发阶段,(捕获/冒泡)。对于多个相同的事件处理器,不会重复触发,不需要手动使用removeEventListener清除

5.IE9使用attachEvent和detachEvent

Dom.onClick(()=>console.log(‘第一次’)

Dom.onClick(()=>console.log(“第二次”)

只会触发一次,输出‘第二次’

Dom.addEventLisener(‘click’,()=>console.log(‘第一次’))

Dom.addEventLisener(‘click’,()=>console.log(‘第二次’))

会触发两次,依次触发。

2.3 事件的捕获目标冒泡

1.捕获(Capture):事件对象(event object) 从 window 派发到 目标对象父级的过程。

2.目标(Target): 事件对象派发到目标元素时的阶段,如果事件类型指示其不冒泡,那事件传播将在此阶段终止。

3.冒泡(Bubbling):和捕获相反,是以目标对象父级到 window 的过程。

在任一阶段调用 stopPropagation 都将终止本次事件的传播

addEventLisener形式注册的监听事件接受参数以指定是否在捕获阶段触发本次事件,默认值为否(既冒泡阶段)。以事件处理器注册的事件在非捕获阶段触发。

通过e.stopPropagation()可以阻止事件传播。

2.4 浏览器的默认事件

例如点击a标签时候的跳转,通过input提交表单等,可以通过e.preventDefault()进行阻止。

3 渲染机制:

3.1 关键渲染路径

浏览器从接收到页面开始到页面显示,这整个过程中的所有步骤,称为关键渲染路径。用户看到页面实际上可以分为两个阶段:页面内容加载完成和页面资源完成。

渲染过程:

1.解析HTML,生成DOM树(DOM)

2.解析CSS,生成CSSOM树(CSSOM)

3.将DOM和CSSOM合并,生成渲染树(Render-Tree)

4.计算渲染树的布局(Layout)

5.将布局渲染到屏幕上(Paint)

(JS脚本会对1 2 产生影响,后面单独解释)

3.2 Dom生成

浏览器在获取HTML后,解析HTML代码,将HTML的元素关系转换成一个数据结构,就是我们所熟知的DOM(Document Object Model)。

在解析HTML过程中,会碰到几类特殊的节点需要特殊的处理:

style、link元素以及具有内联样式的元素:交给“CSSOM生成

script(无论是否外链)元素:见“Script标签的处理”

3.2.1 CSSOM生成

style和内联样式

浏览器会直接根据样式声明生成CSSOM,因为它们本身就直接含有样式内容。

link

对外联样式,浏览器会首先发送请求,待请求成功,获取外联样式后,浏览器便会解析该外联样式,并生成相应的CSSOM。

由于CSSOM负责存储渲染信息,浏览器就必须保证在合成渲染树之前,CSSOM是完备的,这种完备是指所有的CSS(内联、内部和外部)都已经下载完,并解析完,只有CSSOM和DOM的解析完全结束,浏览器才会进入下一步的渲染,这就是传说中的CSS阻塞渲染。

CSS阻塞渲染意味着,在CSSOM完备前,页面将一直处理白屏状态,这就是为什么样式放在head中,仅仅是为了更快的解析CSS,保证更快的首次渲染。

需要注意的是,即便你没有给页面任何的样式声明,CSSOM依然会生成,默认生成的CSSOM自带浏览器默认样式(default styles)。

3.2.2 Script标签的处理

JS可以操作DOM来修改DOM结构,可以操作CSSOM来修改节点样式,这就导致了浏览器在解析HTML时,一旦碰到script,就会立即停止HTML的解析(而CSS不会),执行JS,再返还控制权。

事实上,JS执行前不仅仅是停止了HTML的解析,它还必须等待CSS的解析完成。当浏览器碰到script元素时,发现该元素前面的CSS还未解析完,就会等待CSS解析完成,再去执行JS。

JS阻塞了HTML的解析,也阻塞了其后的CSS解析,整个解析进程必须等待JS的执行完成才能够继续,这就是所谓的JS阻塞页面。一个script标签,推迟了DOM的生成、CSSOM的生成以及之后的所有渲染过程,从性能角度上讲,将script放在页面底部,也就合情合理了。

css加载不会阻塞DOM树的解析

css加载会阻塞DOM树的渲染

css加载会阻塞后面js语句的执行

js的加载执行会阻塞其后Dom树的解析和渲染

3.3 渲染流程

3.3.1 渲染树

当DOM和CSSOM构建完成,它们一个存储了节点信息,一个存储了节点渲染信息,都不能直接用来渲染,为此浏览器会将两者结合,生成渲染树(Render-Tree),这棵树就包含了页面所有可见元素及其渲染信息。

1. 从DOM的根节点开始,遍历每个可视节点:script、link、meta都属于不可视节点,另外,display: none的节点也属于不可视节点

2. 从CSSOM中搜索可视节点的样式

3. 计算这些样式,将计算值应用到可视节点上

渲染树生成后,还是没有办法渲染到屏幕上,渲染到屏幕需要得到各个节点的位置信息,这就需要布局(Layout)的处理了。

3.3.2 布局

渲染树生成后,浏览器便可以根据渲染树中的样式信息,结合设备的屏幕信息,计算每个元素的位置和尺寸。

3.3.3 渲染

得到了渲染树及其节点的布局信息,浏览器便可以将最终的页面渲染到屏幕。整个关键渲染路径主要就包括了以上这些步骤,每个步骤的快慢都决定着页面的性能,或者说网站的性能,因此,谈到首屏或者首渲的性能优化,就不得不从关键渲染路径着手,每一步都是有或多或少的可优化点。

当页面首渲完成后,会有很多页面交互,例如:动画、用户点击、滚屏。所有的交互都会引发浏览器新的渲染操作,这些操作直接影响着用户交互性能,Chrome官网里直接称作渲染性能。

3.3.4 整个流程

JS:渲染引擎会等待所有的JS操作完成,收集JS对DOM和CSSOM的操作结果

Style:样式计算,计算交互引起的样式变更,并应用到相应的节点上

Layout:布局,根据新的Style,计算出新的节点位置和尺寸信息

Paint:渲染,计算最终的渲染信息(与上述的关键渲染路径-渲染好像不同,其实是一样的,只是上面直接跳到了渲染到屏幕这一步),在实际的渲染中,浏览器会尽可能地在多个层上去渲染,这个层类似PS里的图层概念

Composite:合成,将每个渲染层合并,生成最终的一层渲染画面。Paint阶段,每个层独立渲染,并不关心与其他层之间的关系,Composite就需要将这些层以正确的关系合成,有点像PS的导出PNG。合成发生在GPU上

性能优化:

  1. 省略layout:Layout是计算节点的布局信息——位置和尺寸,当我们修改的样式里不涉及布局,浏览器就会省略这个步骤。例如:color。通常来讲,Layout是很耗渲染性能的,从性能优化角度讲,能避免Layout就避免。除了修改布局属性会触发Layout外,很多获取布局信息的JS操作也会引发Layout,如offsetHeight,getComputedStyle。
  2. 省略layout和paint: 避免Paint就是让浏览器将渲染直接交给GPU合成,目前transform和opacity两类样式属性是可以直接跳过Paint的。至于将translate2D变3D,实际上是触发了层提升,使得相应的元素渲染可以在独立层上与其他层并行处理,间接提升了渲染性能。至于别人说的触发GPU加速,也只是因为被新建了一个层。

似乎提升层来提升性能是个很不错的玩法,但是,你的硬件不是无限的,每多一个层,就会多一份内存,因此,控制层数,也是很重要的性能提升。

4 存储:

4.1 浏览器常用的储存方法:cookie、localStorage、sessionStorage

cookie属于文档对象模型DOM树根节点document,而 sessionStorage 和 localStorage 属于浏览器对象模型BOM的对象window

其中 sessionStorage 和 localStorage 是 HTML5 Web Storage API 提供的

sessionStorage:为每一个给定的源(given origin)维持一个独立的存储区域,该存储区域在页面会话期间可用(即只要浏览器处于打开状态,包括页面重新加载和恢复)

localStorage:同样的功能,但是在浏览器关闭,然后重新打开后数据仍然存在。

1. cookie

h5之前,存储主要用cookies,缺点是在请求头上带着数据,导致流量增加。大小限制4k

操作方式:

document.cookie = "username=John Doe; expires=Thu, 18 Dec 2013 12:00:00 GMT; path=/"    // 设置cookie

document.cookie = "username=; expires=Thu, 01 Jan 1970 00:00:00 GMT"    // 删除cookie

设置cookie的方法比较简单,其中有几个参数可以添加

expires:

过期时间,当过了到期日期时,浏览器会自动删除该cookie,如果想删除一个cookie,只需要把它过期时间设置成过去的时间即可 比如希望设置过期时间一年:new Date().getTime() + 365 * 24 * 60 * 60 * 1000

如果不设置过期时间,则表示这个cookie生命周期为浏览器会话期间,只要关闭浏览器窗口,cookie就消失了。

path:

路径,值可以是一个目录,或者是一个路径。

如果cc.com/test/index.html 建立了一个cookie,那么在cc.com/test/目录里的所有页面,以及该目录下面任何子目录里的页面都可以访问这个cookie。因此在cc.com/test/test2/test3 里的任何页面都可以访问cc.com/test/index.html建立的cookie。若cc.com/test/ 若想访问cc.com/test/index.html设置的cookes,需要把cookies的path属性设置成“/”。 在指定路径的时候,凡是来自同一服务器,URL里有相同路径的所有WEB页面都可以共享cookies。

domain:

主机名,是指同一个域下的不同主机,例如:www.baidu.com和map.baidu.com就是两个不同的主机名。默认情况下,一个主机中创建的cookie在另一个主机下是不能被访问的,但可以通过domain参数来实现对其的控制:document.cookie = "name=value;domain=.baidu.com" 这样,所有*.baidu.com的主机都可以访问该cookie。

2. localStorage

以键值对(Key-Value)的方式存储,永久存储,永不失效,除非手动删除。IE8+支持,每个域名限制5M,打开同域的新页面也能访问得到

操作方式:

window.localStorage.username = 'hehe'                   // 设置
window.localStorage.setItem('username', 'hehe')         // 设置
window.localStorage.getItem('username')                 // 读取
window.localStorage.removeItem('username')             // 删除
window.localStorage.key(1)                             // 读取索引为1的值
window.localStorage.clear()                            // 清除所有

可以存储数组、数字、对象等可以被序列化为字符串的内容

3. sessionStorage

sessionStorage操作的方法与localStroage是一样的,区别在于 sessionStorage 在关闭页面后即被清空,而 localStorage 则会一直保存。很多时候数据只需要在用户浏览一组页面期间使用,关闭窗口后数据就可以丢弃了,这种情况使用sessionStorage就比较方便。

注意,刷新页面sessionStorage不会清除,但是打开同域新页面访问不到

4.2 cookie、localStorage、sessionStorage之间的区别

1.cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递。而sessionStorage和localStorage不会自动把数据发给服务器,仅在本地保存。cookie数据还有路径(path)的概念,可以限制cookie只属于某个路径下。

2. 存储大小限制不同,cookie数据不能超过4k,同时因为每次http请求都会携带cookie,所以cookie只适合保存很小的数据,如会话标识。sessionStorage和localStorage 虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大。

3. 数据有效期不同,sessionStorage:仅在当前浏览器窗口关闭前有效,自然也就不可能持久保持;localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据;cookie只在设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭。

4. 作用域不同,sessionStorage不在不同的浏览器页面中共享,即使是同一个页面;localStorage 在所有同源窗口中都是共享的;cookie也是在所有同源窗口中都是共享的。

Web Storage 支持事件通知机制,可以将数据更新的通知发送给监听者。Web Storage 的 api 接口使用更方便,cookie的原生接口不友好,需要自己封装。

5. 安全性问题cookie容易受到安全问题攻击影响,例如关键攻击载体的CSRF(Cross Site Request Forgery),XSS(Cross Site Scripting Attacks) 以及 Session Hijacking 。一个用功且专业的开发者也许不会把很多安全细节信息放在cookie中,或者实现一系列的方法来减轻可能的这些形式的攻击。

5. 浏览器缓存机制

5.1 浏览器缓存总览

对http请求来说,客户端缓存分三类:

1.不发任何请求,直接从缓存中取数据,代表的特性有: Expires ,Cache-Control=<number!=0>和appcache

2.发请求确认是否新鲜,再决定是否返回304并从缓存中取数据 :代表的特性有:Last-Modified/If-Modified-Since,Etag/If-None-Match

3.直接发送请求, 没有缓存,代表的特性有:Cache-Control:max-age=0/no-cache

5.2 缓存方式

5.2.1 last-modified

last-modified: 浏览器第一次请求资源,服务器会在返回资源时在response header中添加last-modified,值是这个资源在服务器最后修改的时间。

if-modified-since: 下一次请求资源时候,浏览器检测到有last-modified这个header,于是添加if-modified-since这个header(值为last-modifed-since),服务器会根据这个值和资源的最后修改时间进行对比,如果没有变化,则返回304和空的响应体。如果小于了则返回新资源和200

Last-Modified 的弊端

本地打开缓存文件,即使没有修改,last-modified也会被修改。

5.2.1 Etag

 完善:http1.1中出现了Etag和if-none-match

Etag是服务器响应请求时,返回当前资源文件的一个唯一标识(由服务器生成),只要资源有变化,Etag就会重新生成。浏览器在下一次加载资源向服务器发送请求时,会将上一次返回的Etag值放到request header里的If-None-Match里,服务器只需要比较客户端传来的If-None-Match跟自己服务器上该资源的ETag是否一致,就能很好地判断资源相对客户端而言是否被修改过了。

Etag和last-modified对比:

1.etag的精度很高,而last-modifed的单位是秒。如果一秒内改变了多次资源,那么在last-modified中体现不出来。

2.性能上,etag因为需要服务器计算,而last-modified只需要记录时间,所以last-modified性能更好。

3.etag优先级更高。

5.3 强缓存和协商缓存

1.强缓存:不会向服务器发送请求,直接从缓存中读取资源,在chrome控制台的Network选项中可以看到该请求返回200的状态码,并且Size显示from disk cache或from memory cache。强缓存可以通过设置两种 HTTP Header 实现:Expires 和 Cache-Control。

2.协商缓存:协商缓存就是强制缓存失效后,浏览器携带缓存标识(etag/last-modified)向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程。 结果:协商缓存失效,返回200和结果。  协商缓存生效,返回304和not modified

6. 浏览器跨域机制

6.1 .产生跨域的条件:

请求类型是XHR类型、浏览器开启校验、不同源(不同端口,不同域名,不同HTTP头)

6.2 解决方法

1.通关关闭浏览器的secure校验,来实现。

2.使用jsonP解决。

①客户端注册一个用来处理数据的callback函数,然后通过<script>标签,向服务器发出请求,src="url....",在url中可以携带callback的名字,和需要的参数。

②服务端收到请求(并拿到参数),经过处理后,返回一个js文档。 js文档中包含了  callback(data),   callback是客户端注册的函数,data是客户端需要的数据

③客户端收到服务端返回的Js文档后,可以执行该文档,从而调用callback函数来处理数据

JsonP发送的不是XHR请求类型,而是script请求类型从而解决跨域问题。

jsonp的缺点:

1.因为Jsonp返回的也是js脚本,所以需要后端配合

2.只支持get请求(因为是通过url请求的)

3.callback中被恶意注意js脚本,进行xss攻击

3.解决请求是跨域的问题

1.被调用方解决(服务器端解决,通过设置    Header("Access-Control-Allow-Origin", "*")来允许跨域 )

2.调用方解决(将接口代理到本地)

①.配置nginx来解决

②.使用webpack等工具携带proxy代理功能(解决本地开发调用问题)

www.woke20.com

【前端知识体系】浏览器相关推荐

  1. web前端知识体系大全

    1. 前言 大约在几个月之前,让我看完了<webkit技术内幕>这本书的时候,突然有了一个想法.想把整个web前端开发所需要的知识都之中在一个视图中,形成一个完整的web前端知识体系,目的 ...

  2. Web前端知识体系精简

    Web前端技术由html.css和javascript三大部分构成,是一个庞大而复杂的技术体系,其复杂程度不低于任何一门后端语言.而我们在学习它的时候往往是先从某一个点切入,然后不断地接触和学习新的知 ...

  3. layui 如何动态加载局部页面_从输入URL到页面加载的过程?如何由一道题完善自己的前端知识体系!

    前言 见解有限,如有描述不当之处,请帮忙指出,如有错误,会及时修正. 为什么要梳理这篇文章? 最近恰好被问到这方面的问题,尝试整理后发现,这道题的覆盖面可以非常广,很适合作为一道承载知识体系的题目. ...

  4. 针对WEB前端新人的前端知识体系

    现在谈到WEB前端的技术结构.知识体系,都会搞出一个非常大.枝叶非常多且细的相当宏大的思维导图,就像这种的,这种大而全的图示对新人来讲,太大了,不好掌握,而且容易在其中迷失.我想今天和大家谈一下针对新 ...

  5. Web前端知识体系梳理,值得收藏!

    前言 现在是信息时代,经过 web1.0 时期.web2.0 时期到现在的移动互联网时期,信息获取越来越高效,坐着不动就会有大量的新闻.博客.资讯向你推荐而来. 作为一名靠谱的程序员,你免不了要查阅大 ...

  6. Web 前端知识体系精简

    Web前端技术由html.css和javascript三大部分构成,是一个庞大而复杂的技术体系,其复杂程度不低于任何一门后端语言.而我们在学习它的时候往往是先从某一个点切入,然后不断地接触和学习新的知 ...

  7. 历时8个月,10w字!前端知识体系+大厂面试笔记(工程化篇)

    前言 本文是10w字 前端知识体系+大厂面试总结 的 第三篇 其余3篇为基础知识篇.算法篇.前端框架和浏览器原理篇,如果还没有阅读,建议了解下 工程化目的是为了提升团队的开发效率.提高项目的质量 例如 ...

  8. 前端劝退之前端知识体系(前端面试体系)

    关注公众号 前端开发博客,领27本电子书 回复加群,自助秒进前端群 写文的目的主要是梳理下知识,能对复习有点帮助就行,本文主要针对前端知识体系相关,涉及的内容有: 浏览器 计算机网络 前端基础(htm ...

  9. 如何形成前端知识体系

    来啦各位大佬-但很不好意思,我就是标题党,这篇博文并没有很明确的给出「如何形成前端知识体系」答案,我自学前端,在面试字节的时候,字节的大佬说我的知识点没有成体系,很零散的飘在各个地方,故而我尝试整理了 ...

  10. 从输入URL到页面加载的过程?如何由一道题完善自己的前端知识体系!

    前言 见解有限,如有描述不当之处,请帮忙指出,如有错误,会及时修正. 为什么要梳理这篇文章? 最近恰好被问到这方面的问题,尝试整理后发现,这道题的覆盖面可以非常广,很适合作为一道承载知识体系的题目. ...

最新文章

  1. MaxCompute大数据实践,电商数据仓库选择雪花还是星型模型?
  2. fx2n4ad模块中文手册_三菱特殊模块FX2N-4AD-PT详细说明及编程应用
  3. VSFTP服务--Linux学习笔记
  4. #面试!,一定要注意,避免踩这些雷!!
  5. 微型计算机的alu部件是包含在,微型计算机的ALU部件是什么?
  6. 大学生计算机应用基础考试,大学计算机应用基础知识试题.doc
  7. Android架构 armeabi、armeabi-v7a、arm64-v8a、x86详解
  8. 【响应式Web前端设计】i标签和em标签的区别
  9. 操作系统之哲学原理 第2版
  10. 什么样的女孩是真正爱你的!
  11. 如何重新发明短信息这个古老的轮子
  12. Vue directives 自定义局部指令中调用 method 中的方法
  13. 局域网计算机配置扫描系统,fly42局域网计算机配置检测系统
  14. html设置字体为5号,以下HTML代码中,哪一个是将词语“Run Away Bride”显示为Verdana字体并且字号为5号的正确代码?...
  15. supermap地图风格优化
  16. 史上最强 iPhone 越狱工具开源:功能永久有效!
  17. 【C语言走进现实】猜凶手
  18. 7-96 获奖的运气
  19. 什么是aPaaS?低代码与高生产率的aPaaS和RAD相比如何?
  20. Kotlin 学习 高阶函数

热门文章

  1. 【二叉树进阶】红黑树(Red Black Tree) - 平衡二叉搜索树
  2. 物联网rfid技术在工业自动化中的应用
  3. Symbian^3模拟器快捷键
  4. 以下这段程序将单链表逆转。(单链表不带有空头结点,链表头指针是head)例如,链表 1 -> 2 -> 3 -> 4 逆转后变为 4 -> 3 -> 2 -> 1 .
  5. 华为大连软件开发云上线,打造软件云生态,加速软件产业升级
  6. Prometheus监控-下
  7. 云悦智联企业级物联网官网源码
  8. 7行代码实现一个Tvoc/eCO2有害气体检测仪
  9. 对口单招计算机电工试题答案,对口单招电工试题52题.doc
  10. python中time模块和datetime模块