js的运行原理(js 闭包原理)

:暂无数据 2026-05-04 00:00:02 0
其实js的运行原理的问题并不复杂,但是又很多的朋友都不太了解js 闭包原理,因此呢,今天小编就来为大家分享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()) 时, 情况如图:

感谢你读到最后。这通常意味着你对js的运行原理是真正感兴趣的。这份兴趣,是最好的老师。
本文编辑:admin

更多文章:


today()函数的使用方法(Excel中today函数的使用方法及日期计算应用)

today()函数的使用方法(Excel中today函数的使用方法及日期计算应用)

本文是您理解today()函数的使用方法的最后一站。我们将通过剖析Excel中today函数的使用方法及日期计算应用,帮你打通任督二脉,彻底领悟。

2026年5月4日 21:00

typedef enum和enum详细用法(C语言笔记(五)----struct,enum,typedef等)

typedef enum和enum详细用法(C语言笔记(五)----struct,enum,typedef等)

想高效掌握typedef enum和enum详细用法的核心吗?本文将为你聚焦C语言笔记(五)----struct,enum,typedef等这一关键环节,帮你节省大量摸索时间。

2026年5月4日 20:40

spill over into(急死了!!英文好的请进来帮帮忙啊)

spill over into(急死了!!英文好的请进来帮帮忙啊)

各位老铁们好,相信很多人对spill over into都不是特别的了解,因此呢,今天就来为大家分享下关于spill over into以及急死了!!英文好的请进来帮帮忙啊的问题知识,还望可以帮助大家,解决大家的一些困惑,下面一起来看看吧!

2026年5月4日 20:20

学做网站论坛(怎样才能做网站)

学做网站论坛(怎样才能做网站)

各位朋友,关于学做网站论坛的讨论一直很多,今天咱们不聊复杂的,就聚焦于怎样才能做网站,用最直白的方式把它讲清楚。

2026年5月4日 20:00

多层json的解析(如何用Python解析多层嵌套的JSON)

多层json的解析(如何用Python解析多层嵌套的JSON)

想快速搞懂多层json的解析吗?本文将围绕如何用Python解析多层嵌套的JSON等核心问题,用最直白的语言为您提供一份实用指南,帮您节省大量摸索的时间。

2026年5月4日 19:40

css链接样式有几种(CSS设置链接的样式)

css链接样式有几种(CSS设置链接的样式)

曾几何时,我也觉得css链接样式有几种高不可攀,尤其CSS设置链接的样式更是一头雾水。后来才发现,只是没找对方法,希望我的经验能帮到你。

2026年5月4日 19:20

opengl+和directx+哪个好(OpenGL跟Direct**哪个比较好)

opengl+和directx+哪个好(OpenGL跟Direct**哪个比较好)

花费5分钟阅读本文,您将获得对opengl+和directx+哪个好和OpenGL跟Direct**哪个比较好的清晰认知,远超自己搜索数小时的效果。

2026年5月4日 19:00

json文件怎么编写(macbook air怎么创建json文件)

json文件怎么编写(macbook air怎么创建json文件)

有研究表明,成功掌握json文件怎么编写的学习者,普遍在macbook air怎么创建json文件这个环节投入了更多精力。其重要性不言而喻。

2026年5月4日 18:40

ui设计好找工作吗?(UI设计现状怎么样,好找工作吗)

ui设计好找工作吗?(UI设计现状怎么样,好找工作吗)

很多朋友初次接触ui设计好找工作吗?可能会觉得有点陌生,这很正常。今天这篇文章,咱们就一起把UI设计现状怎么样,好找工作吗这事儿聊透,希望能帮您理清思路。

2026年5月4日 18:20

python可以做软件吗(python能写软件吗)

python可以做软件吗(python能写软件吗)

老铁们,关于python可以做软件吗,你可能听过不少说法。今天,咱们就坐下来好好聊聊python能写软件吗,保证让你豁然开朗。

2026年5月4日 18:00

最近更新

学做网站论坛(怎样才能做网站)
2026-05-04 20:00:01 浏览:0
opengl+和directx+哪个好(OpenGL跟Direct**哪个比较好)
2026-05-04 19:00:01 浏览:0
热门文章

split函数 sql(求sql split函数的用法)
2026-03-26 20:40:01 浏览:1
标签列表