js的运行原理(js 闭包原理)
本文目录
- js 闭包原理
- js 中的自执行函数到底是怎么运行的
- *****的原理是怎么样的
- 我对JS异步执行机制的理解
- JS模块加载器加载原理是怎么样的
- @ 功能 JS 实现原理详解
- js运行机制以及如何动态获取input的值
- js的工作原理
- js中的this原理
- Js闭包的原理(图解)
js 闭包原理
1、闭包的概念:指有权访问另一个函数作用域中的变量的函数,一般情况就是在一个函数中包含另一个函数。
2、闭包的作用:访问函数内部变量、保持函数在环境中一直存在,不会被垃圾回收机制处理
因为函数内部声明 的变量是局部的,只能在函数内部访问到,但是函数外部的变量是对函数内部可见的,这就是作用域链的特点了。
子级可以向父级查找变量,逐级查找,找到为止
因此我们可以在函数内部再创建一个函数,这样对内部的函数来说,外层函数的变量都是可见的,然后我们就可以访问到他的变量了。
3、闭包的优点:
方便调用上下文中声明的局部变量
逻辑紧密,可以在一个函数中再创建个函数,避免了传参的问题
4、闭包的缺点:
因为使用闭包,可以使函数在执行完后不被销毁,保留在内存中,如果大量使用闭包就会造成内存泄露,内存消耗很大
js 中的自执行函数到底是怎么运行的
其实不要被“函数表达式”的函数两字束缚了,其实就是“表达式”,这样一来就会好理解了。在表达式的前面加个逻辑运算符(比如!取反)或数学运算符(比如+),js就会认为你是要获取表达式的值,这样它就会去运行这个表达式,这样函数就会被执行了。比如说有下面这个函数声明:
function abc(){return true}
那么执行 !abc() 时js就会先去调用abc函数,然后再把返回值取反,这个好理解吧?
而现在把abc直接替换为匿名函数:
!function(){return true}()
js也一样会把!后面的部分当作一个表达式去运行,然后把取回的值进行取反,这个过程中匿名函数就会自动运行了。
其实 (function(){})(); 和 (function(){}()) 能够自执行的原理也是一样的,我加黑的那对括号其实也是运算符(就好比小学数学中的 (2+3)×4 中的括号),这样js就会把括号里的部分当作表达式来处理了。
所以,说到底函数的自执行其实并不是js的有意为之,而是一种无心插柳的行为,说是旁门左道也不为过,但它确实可以帮我们实现一些特殊的要求,所以就慢慢变成一种正经用法了。
*****的原理是怎么样的
只要在页面上引入*****和相关的css,并不需要对业务逻辑做什么修改,就能拥有网页加载进度条,只要大家发挥想象力,那除了默认的进度条之外还能开发各种加载进度效果,使用起来非常方便。
那问题来了:*****是如何做到“自动”监控加载进度的呢?
*****监控了什么:
*****对于加载进度监控了什么呢?通过阅读源码,我们看到整体的进度有四个部分组成:document,elements,eventLag和ajax这四种监视器(Monitor)。
那接下来就来看看它们分别是什么。
首先是*****就是我们所熟悉的HTMLDocument节点。document节点有一个事件,onreadystatechange,当document的readyState状态变化时会触发。因为无法准确的知道document到底加载了多少百分比,所以就用这个状态来做一个估算。在*****中将document的readyState的三个状态loadong,interactive和complete分别定义为0%,50%和100%。通过**document节点的readyState状态变更,形成了*****的DocumentMonitor。
接下来是elements。我们可以通过配置传入一个css选择器列表,默认的选择器列表仅包含“body”。*****会按照设置的时间间隔搜索所有的选择器,如果能获得则认为这一项满足,所以elements这一项的加载进度就是 满足的选择器/全部的选择器。通过查询页面中制定的元素,就是*****的ElementMonitor。
然后是*****其实只是一个“假的”监视器。它就在那里安静匀速的更新进度,这一小小的措施却带来了不错的用户体验,让用户不会因为加载“卡住了”而慌张。
*****是如何监控ajax的:
最后是用来监控ajax进度的AjaxMonitor。这是最有趣的一部分。如何才能监控所有ajax请求,并且不需要开发者修改自己或第三方的业务逻辑呢?
如果了解原生js的话,应该了解这几个类:XMLHttpRequest,XDomainRequest,WebSocket。这三个类分别用来发送ajax请求,跨域的ajax请求,以及websocket。如果能监控所有这些请求的时间,包括progress,load,error等等,我们就可以更新我们的加载进度了。
既然我们知道了要监控的对象,那我们便可以“请君入瓮”了,而这个瓮,就是代理模式:我们可以把原生的类保存起来,再用我们自己写的埋藏了“间谍”的类覆盖原生的类,这样当其他代码建立这些类的实例的时候,我们的“间谍”便悄悄开始**加载进度了。
让我们来看一下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 保存原生的XMLHttpRequest
_XMLHttpRequest = *****;
// 覆盖XMLHttpRequest
***** = function(flags) {
var req;
// 调用原生的XMLHttpRequest
req = new _XMLHttpRequest(flags);
// 埋入我们的“间谍”
monitorXHR(req);
return req;
};
monitorXHR = function(req) {
var _open;
// “间谍”又对open方法埋入了间谍
_open = *****;
return ***** = function(type, url, async) {
if (shouldTrack(type)) {
_*****(’request’, {
type: type,
url: url,
request: req
});
}
return _*****(req, arguments);
};
};
通过代理模式埋了这些“间谍”后,我们就能对发起的ajax请求等做到监控,以更新加载进度。
当然*****也不是什么都**了
除了以上这些,当然也有*****并没有**的加载事件,比如script标签的加载。如果我们使用了*****或者*****等,当js代码动态加载时并不会影响进度条,因为这里我们用动态建立script标签的方式加载js代码,而*****并没有对这方面进行**。我们可以同样的代理appendChild方法,如果发现script标签被加到页面上,那么就**它的加载进度。不过实际情况当然复杂的多,还有insertBefore,甚至innerHTML等方法都可以创建script标签加入文档并开始js的加载(当然一般就是appendChild和insertBefore啦)。
相似的还有css的加载,还要注意处理css的加载事件,在一些老的浏览器上,css并没有加载事件,还有加个定时不断检查css节点的cssRules和name来确定加载状况。
小结:
通过阅读*****的源码我们了解了*****的原理,同时学习了鸡贼的代理模式。比如在ja*******中也有个spy,可以监控制定函数被调用了几次,被什么参数调用了等等。灵活应用这种代理模式,可以方便我们扩展功能、进行调试等等。
我对JS异步执行机制的理解
***隐藏网址***
***隐藏网址***
由此可见,关于异步执行机制到底是怎么回事,因为涉及到浏览器底层机制,所以不容易彻底了解清楚,就算是大牛阮一峰,也只是通过英文文献来了解,而且一知半解。我的这篇文章只是试图尽可能简单的描述一下JS的异步执行机制,坦白说,我现在并不能完全弄懂这个机制,所以也不能完全解释清这个机制,所以,如果我写的越严谨,就越容易出错,我只能简单但是较模糊的描述一下:
JS的运行环境是一个很复杂的环境,它里面有非常多的复杂的名词事物,用简单又不严谨的说法来说,运行环境里至少有下面这些事物:
有一个国外的web app,专门用来讲解异步事件的门道 Loupe ,这个更接近真实情况。为什么我不讲解这个?因为更复杂了,我们并不打算研究浏览器的底层,不是么?
然后说一下任务队列里的任务。所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。同步任务指的是,靠主线程自己就可以执行完成的任务;异步任务指的是,主线程执行开始之后,需要靠主线程之外的线程才能完成的任务。由主线程决定是否要动用其他线程。以下内容,不再提栈,只说主线程。
现在说重点:
异步任务的执行机制是:
当主线程遇到一个异步任务,比如一个ajax请求,当主线程执行到 *****() 的时候,这个send命令是立即执行的, 并不会像一些人想象的,拖到所有同步任务的最后面。***隐藏网址***
***隐藏网址***
***隐藏网址*** 回调 。 注意,并不是异步任务在全体同步任务结束之后才开始,而是异步任务的回调通常在全体同步任务结束之后才开始!异步任务跟异步任务的回调是两回事!是两个任务!一个鲜明的例子就是 setTimeout(fn, 1000) ,计时是从主线程遇到 setTimeout() 任务,然后分配给计时器线程,计时器线程开始干活的时候就开始计时了!只不过要1秒之后 fn 才执行! setTimeout() 和 fn 是两个任务! setTimeout() 是立即执行, fn 才是1秒之后执行。但是 setTimeout() 的执行,人眼是感受不到的,因为并没有什么地方有一个秒表告诉你 setTimeout() 开始执行了;而fn的执行,人眼能感受到,所以人们会错误的以为fn才是异步任务,其实fn并不是, fn 是个回调任务,往往 fn 是同步任务,比如 fn 可能是 c********(123) ,这怎么会是异步任务。
所以,异步机制是浏览器的两个或以上常驻线程共同完成的,异步请求是JS主线程和其他某个线程共同完成的,JS的执行线程发起异步请求(这时浏览器会开一条新的HTTP请求线程来执行请求,这时JS自己的任务已完成,继续执行线程队列中剩下的其他任务),然后在未来的某一时刻"任务队列"线程监视到之前的发起的HTTP请求已完成, "任务队列"就会把完成事件插入到JS执行队列的尾部等待JS处理 。
最后专门说说定时触发(settimeout和setinterval)。
定时触发是由浏览器的定时器线程执行的定时计数, 然后在定时时间到达之后,定时器线程把定时处理函数的执行请求插入到JS回调队列的尾端。
这个1到底是100毫秒之后弹出,还是1000毫秒(或更多时间)后弹出呢?又或是1100毫秒之后弹出?
答案是,1000毫秒多一点点之后弹出。
原因:浏览器会先执行setTimeout,也就是开始计时,然后开始执行sometask,执行了1000毫秒,然后去回调队列里看回调任务,alert(1);早就恭候了,因为定时100毫秒之后alert(1)就可以执行了。所以,等1000毫秒的任务完成,1就会立即弹出,所以答案是1000毫秒多一点点之后弹出。
所以用这两个函数的时候,实际的执行时间是大于或等于指定时间的,不保证能准确定时的。
最后强调一下setInterval。比如我希望每100毫秒打印一个1。然后,又有极端情况,就是sometask耗时1000毫秒。你以为sometask结束之后会打出10个1么?并不会,只会打出1个1,因为setInterval第一次读秒结束之后,回调队列出现了一个alert(1),根据之前的理论,并不会执行。又过了100毫秒之后,计时器线程会去观察回调队列是不是已经有了alert(1),如果有,就不再往回调队列里加alert(1),目的就是为了避免回调叠加执行。
总之,你需要记住,异步任务就是主线程在任务队列里看到了这个任务,看了一眼之后就然后安排别的线程帮忙,然后主线程就忙别的去了,别的线程帮忙完事之后,再在队列末尾放一个新任务叫“帮忙完毕”,到此异步任务本身就完事。主任务看到“帮忙完毕”任务之后,就去执行回调,回调执行完,这个异步任务连同回调就全都完事。然后,如果并没有回调。。。没有就没有呗。
JS模块加载器加载原理是怎么样的
加载器的基本原理就是在html的head中插入script标签,通过这种方式加载远程js代码。
其中。模块加载器通过依赖关系来保证代码的加在顺序以及执行顺序。因此开发人员在使用加载器时,不需要关注加载细节。
@ 功能 JS 实现原理详解
***隐藏网址***
1.取以前保留先来的信息
2.删除 @ 符号
3.生成需要显示的内容,一个 span
4.将生成内容打包放在 Fragment 中
5.将 Fragment 中的内容放入 range 中,并将光标放在空格之后
6.成功选人后让输入框获取焦点
1.按下了@键,保存按下@时的光标信息
2.获取当前文本。 有@时,获取@参数,展示@列表;没有有@时,纯文本展示。
1.光标点击时,@列表不展示
2.按下键盘时触发,
1.按下enter键发送
2.按下@键保存变量为true,执行后续操作;
3.按下删除键,删除@xx整体
a :因为在建立时默认会在 @xxx 后添加一个空格,
因此当得知光标位于 @xxx 以后的一个第一个字符后并按下删除按钮时,
应该将光标前的 @xxx 给删除
b :当光标位于 @xxx 中间时,按下删除按钮时应该将整个 @xxx 给删除。
js运行机制以及如何动态获取input的值
最近在写一个手机APP,其中一个页面中onload加载的是一个函数A,经过这个函数处理,页面中的Input的value值会被 赋值,此时,函数B是一个定时函数,他需要获取这个input 的value值,然后定时执行,来更新这个input的value值。
当时学js的时候,没太注意js的程序加载顺序。现在无论如何也拿不到Input的value值,不管怎么写,都是空。由此也引出了如下的知识
****是单线程的,他的加载分成两部分,第一个部分是预处理,主要是操作声明式函数,就是function aaa(){} 这类和变量,虽然说处理,也只是拿出来,并没有赋值,赋值是第二阶段运行的。
2.如果要实现上述我们的目标,就需要,使用赋值式函数,即 var f=function(){} 这样操作,会被js拿到,然后处理。
3.要获得Input value的值,这个获取,不能写在函数外,需要写在函数内。因为写在外面,只会加载input的value原值,就是空值。写在里面,就走到了执行阶段。有了执行,就能够获取
js的工作原理
JavaScript就是所谓的客户端脚本语言,是一种在互联网浏览器(浏览器也称为Web客户端,因为它连接到Web服务器上,以下载页面)内部运行的计算机编程语言。 也就是说,如果一个网页里有js代码,那么,在打开这个网页的时候,js代码就会被自动下载到我们的浏览器里。它是在本地执行的程序,这样可以减少服务端得压力。
js中的this原理
this 指的是 函数运行时 所在的环境。(不是定义时所在的环境)
怎么理解这句话呢?看如下例子:
在这个例子中,首先第一个输出,因为wFunction是运行在全局环境中,所以 this 指向全局环境,所以输出1;第二个输出,因为wFunction运行在wObj环境中,所以 this 指向wObj,而wObj的w属性值为2,所以输出2;
那么问题来了,为什么第二个说是在wObj环境中运行呢,运行环境是怎么来判别的呢?这得从内存的数据结构说起。
首先,是考虑到内存的数据结构,才设计这个this的。那内存的数据结构是怎样的呢,先看个例子:
这里,javaScript引擎会在内存里先生成一个对象{w:5},然后把这个对象的内存地址(reference)赋值给wObj变量。
如果通过*****来读取这个对象的属性w,则引擎会先从wObj这个变量拿到对象的内存地址,然后从该地址读出这个对象,返回w属性。
在看下面一个例子:
这里,javaScript引擎会先将函数function(){...} 单独保存在内存中 。然后javaScript引擎会在内存里生成一个对象{wFunc:wFunction},而wFunction则是函数的内存地址(reference)。然后把这个对象的内存地址赋值给wObj变量。
如果通过*****()执行函数,则引擎会先从wObj这个变量拿到对象的内存地址,然后从该地址读出这个对象,返回wFunc属性,而wFunc属性的值是函数function(){...}的内存地址(reference),因为这个 是单独的一个值,所以可以在不同环境(上下文)执行 。在这里函数是通过wObj找到的(wObj的属性wFunc),所以在wObj环境中执行的。
这里解释了为啥函数是可以在不同的环境中执行。那么问题来了,这动态的执行环境,怎么获取呢?
针对获取执行环境问题, this 就这么设计出来了,它的设计目的就是在函数体内部,指代函数 当前的运行环境 。
回到文章开头例子,并加以深化如下:
第一个输出,函数执行时,是通过全局变量wFunction找到函数引用地址的,它的执行环境则是全局,因此输出全局w,为1;
第二个输出,函数执行时,是通过wObj指向的对象里的wFunction属性找到的函数引用地址的,因此,它的执行环境则是wObj指向的对象,因此输出对象的w,为2;
第三个输出,函数执行时,是通过全局变量wObjFunc找到函数引用地址的,(因为let wObjFunc = *****;这直接把函数的引用地址直接赋给了全局变量wObjFunc,所以wObjFunc直接指向函数本身)它的执行环境则是全局,因此输出全局w,为1;
Js闭包的原理(图解)
什么是闭包(Closure)?
网上流传各种说法,在Javascript语言中,我的理解是: 保存着其他函数内部变量的函数,就是闭包。
挺绕的,但不虚,让我们一步步揭开它的神秘面纱!
要理解闭包,我们得先搞清楚以下几个概念:
JS的作用域分两种:全局作用域、局部作用域(也可称为函数作用域)
总的来说,Js作用域的一般机制就是:内部可访问外部的变量,外部无法访问内部的变量。
那么这套作用域机制是如何实现的呢?答案是:通过作用域链
在Js中,每当一个函数被执行,都会产生三个对象:
我们通过实例配图讲解,例如有如下 js 文件:
当浏览器运行解析 ***** 后,首先创建了全局执行环境 (Window 对象)、Window 作用域链和 Global 全局活动对象,如图:
搞明白了作用域链,离弄清楚什么是闭包就仅一步之遥了! 我们来看看下面这个实例:
当执行 var func = outer() 时,情况如图:
接下来,当执行 c********(func()) 时, 情况如图:
更多文章:
today()函数的使用方法(Excel中today函数的使用方法及日期计算应用)
2026年5月4日 21:00
typedef enum和enum详细用法(C语言笔记(五)----struct,enum,typedef等)
2026年5月4日 20:40
spill over into(急死了!!英文好的请进来帮帮忙啊)
2026年5月4日 20:20
多层json的解析(如何用Python解析多层嵌套的JSON)
2026年5月4日 19:40
opengl+和directx+哪个好(OpenGL跟Direct**哪个比较好)
2026年5月4日 19:00
json文件怎么编写(macbook air怎么创建json文件)
2026年5月4日 18:40





