对作用域、作用域链的理解

合集下载

JS核心知识梳理

JS核心知识梳理

JS核⼼知识梳理前⾔本⽂⽬标从JS的运⾏,设计,数据,应⽤四个⾓度来梳理JS核⼼的知识点主题⼤纲1. JS运⾏变量提升执⾏上下⽂作⽤域let作⽤域链闭包事件循环2. JS设计原型原型链thiscallapplybindnew继承3. JS数据数据类型数据的存储(深浅拷贝)数据类型判断(隐式转换,相等和全等,两个对象相等)数据的操作(数组遍历,对象遍历)数据的计算(计算误差)4. JS应⽤防抖,节流,柯⾥化⼀. JS运⾏⼤概分为四个阶段1. 词法分析:将js代码中的字符串分割为有意义的代码块,称为词法单元浏览器刚拿到⼀个JS⽂件或者⼀个script代码段的时候,它会认为⾥⾯是⼀个长长的字符串这是⽆法理解的,所以要分割成有意义的代码块,⽐如: var a = 12. 语法分析:将词法单元流转换成⼀颗抽象语法树(AST),并对⽣成的AST树节点进⾏处理,⽐如使⽤了ES6语法,⽤到了let,const,就要转换成var。

为什么需要抽象语法树呢?抽象语法树是不依赖于具体的,不依赖于语⾔的细节,⽅便做很多的操作另⼀⽅⾯说,现在有许多语⾔,C,C++,Java,Javascript等等,他们有不同的语⾔规范但是转化成抽象语法树后就都是⼀致的了,⽅便编译器对其进⾏进⼀步的增删改查等操作,3. 预解析阶段:会确定作⽤域规则变量和函数提升4. 执⾏阶段:创建执⾏上下⽂,⽣成执⾏上下⽂栈执⾏可执⾏代码,依据事件循环1.作⽤域指定了函数和变量的作⽤范围分为全局作⽤域和函数作⽤域,JS不像C,JAVA语⾔⼀样,没有块级作⽤域,简单说就是花括号的范围2.变量和函数提升全局变量和函数声明会提升函数声明⽅式有三种,function foo() {}var foo = function () {}var foo = new Function()可归为两类,直接创建和变量赋值变量赋值函数和赋值普通变量的优先级按位置来,变量名相同前者被覆盖函数直接创建优先级⾼于变量赋值,同名取前者,与位置⽆关,也就是说函数直接创建即使再变量声明后⾯,也是优先级最⾼3. 执⾏上下⽂有不同的作⽤域,就有不同的执⾏环境,我们需要来管理这些上下⽂的变量执⾏环境分为三种,执⾏上下⽂对应执⾏环境全局执⾏环境函数执⾏环境eval执⾏环境(性能问题不提)1. 全局执⾏上下⽂先找变量声明,再找函数声明2. 函数执⾏上下⽂先找函数形参,和变量声明把实参赋值给形参找函数声明多个函数嵌套,就会有多个执⾏上下⽂,这需要执⾏上下⽂栈来维护,后进先出执⾏上下⽂⾥包含着变量环境和词法环境变量环境⾥就包含着当前环境⾥可使⽤的变量当前环境没有⽤哪的, 这就说到了作⽤域链4. 作⽤域链引⽤JS⾼程的定义:作⽤域链来保证对执⾏环境有权访问的变量和函数的有序访问变量的查找顺序不是按执⾏上下⽂栈的顺序,⽽是由词法作⽤域决定的词法作⽤域也就是静态作⽤域,由函数声明的位置决定,和函数在哪调⽤⽆关,也就js这么特殊5. 静态作⽤域和动态作⽤域词法作⽤域是在写代码或者定义时确定的⽽动态作⽤域是在运⾏时确定的(this也是!)var a = 2;function foo() {console.log(a); // 静态2 动态3}function bar() {var a = 3;foo();}bar();复制代码闭包由于作⽤域的限制,我们⽆法在函数作⽤域外部访问到函数内部定义的变量,⽽实际需求需要,这⾥就⽤到了闭包引⽤JS权威指南定义:闭包是指有权访问另⼀个函数作⽤域中的变量的函数1. 闭包作⽤for循环遍历进⾏事件绑定输出i值时为for循环的长度+1这结果显⽰不是我们想要的, 因为JS没有块级作⽤域,var定义的i值,没有销毁,存储与全局变量环境中在事件具体执⾏的时候取的i值,就是全局变量中经过多次计算后的i值for(var i = 0;i < 3;i++){document.getElementById(`item${i+1}`).onclick = function() {console.log(i);//3,3,3}}复制代码闭包特性:外部函数已经执⾏结束,内部函数引⽤外部函数的变量依然保存在内存中,变量的集合可称为闭包在编译过程中,对于内部函数,JS引擎会做⼀次此法扫描,如果引⽤了外部函数的变量,堆空间创建换⼀个Closure的对象,⽤来存储闭包变量利⽤此特性给⽅法增加⼀层闭包存储当时的i值,将事件绑定在新增的匿名函数返回的函数上for(var i = 0;i < 3;i++){document.getElementById(`item${i+1}`).onclick = make(i);}function make(e) {return function() {console.log(e)//0,1,2};复制代码闭包注意我们在不经意间就写成了闭包,内部函数引⽤外部函数的变量依然保存在内存中,该销毁的没有销毁,由于疏忽或错误造成程序未能释放已经不再使⽤的内存,就造成了内存泄漏同时注意闭包不会造成内存泄漏,我们错误的使⽤闭包才是内存泄漏事件循环JS代码执⾏依据事件循环JS是单线程,通过异步保证执⾏不被阻塞1. 执⾏机制简单说就是,⼀个执⾏栈,两个任务队列发现宏任务就放⼊宏任务队列,发现微任务就放⼊微任务队列,执⾏栈为空时,执⾏微任务队列所有微任务,再取宏任务队列⼀个宏任务执⾏如此循环2. 宏&微任务 macroTask: setTimeout, setInterval, I/O, UI rendering microTask: Promise.then⼆. JS设计1. 原型1. JS的设计有new操作符,构造函数,却没有类的概念,⽽是使⽤原型来模拟类来实现继承2. JS设计⼼路历程JS在设计之初,给的时间较短,并且定义为简单的⽹页脚本语⾔,不⽤太复杂,且想要模仿Java的理念,(这也是为什么JS叫JavaScript的原因)因此就借鉴了Java的对象、构造函数、new操作符理念,⽽抛弃掉了了复杂的class(类)概念3. 继承机制需要有⼀种继承的机制,来把所有对象联系起来,就可以使⽤构造函数但是构造函数⽣成实例对象的缺点就是⽆法共享属性和⽅法4. prototype属性为解决上⾯问题,就引⼊了prototype属性,就是我们常说的原型为构造函数设置⼀个prototype属性,实例对象需要共享的⽅法,都放在此对象上,整个核⼼设计完成后,后⾯的API也就顺理成章原型每⼀个js对象在创建的时候就会与之关联另⼀个对象这个对象就是原型,每个对象都会从原型继承属性proto每个对象都有⼀个属性叫proto,该属性指向对象的原型构造函数的prototype属性等于实例化对象的proto属性此属性并不是ES5 中的规范属性,只是为了在浏览器中⽅便获取原型⽽做的⼀个语法糖,我们可以使⽤Object.getPrototype()⽅法获取原型constructor 原型没有指向实例,因为⼀个构造函数可以有多个对象实例但是原型指向构造函数是有的,每个原型都有⼀个constructor属性指向关联的构造函数function Per() {} // 构造函数const chi = new Per() // 实例对象chi.__proto__ === Per.prototype // 获取对象的原型也是就构造函数的prototype属性Per.prototype.constructor === Per // constructor属性获取当前原型关联的构造函数复制代码实例与原型读取实例属性找不到时,就会查找与对象关联的原型的属性,⼀直向上查找,这种实例与原型之间的链条关系,这就形成了原型链function Foo() {} = 'tom'const foo = new Foo() = 'Jerry'console.log(); // Jerrydelete console.log(); // tom复制代码2.原型链⾸先亮出⼤家熟悉的⽹图就是实例与构造函数,原型之间的链条关系实例的 proto 指向原型构造函数的 prototype 属性指向原型原型的 constructor 属性指向构造函数所有构造函数的 proto 指向 Function.prototypeFunction.prototype proto 指向 Object.prototypeObject.prototype proto 指向 null函数对象原型(Function.prototype)是负责造构造函数的机器,包含Object、String、Number、Boolean、Array,Function。

JavaScript经典语录

JavaScript经典语录

JavaScript经典语录Js的解析与执⾏过程:全局中的解析和执⾏过程:⼀:预处理:创建⼀个此法环境LE,扫描JS:1.⽤声明的⽅式声明的函数(不是函数表达式),2.⽤var定义的变量。

加到预处理阶段的此法环境中全局环境中的预处理:预处理创建的词法作⽤域LE相当于windows⼆:命名冲突的处理:1.处理函数声明有冲突时,会覆盖,处理变量声明有冲突时,会忽略。

2.两者都有时,函数声明的权重⾼⼀些,最终结果指向函数声明的引⽤三:全局函数的执⾏1.⾸先进⾏全局预处理2.执⾏完预处理后,代码⼀步步解析补充:运⽤词法的作⽤域,可以很好的解释⼀个带多个参数的函数只传递⼀个参数的例⼦。

函数中的解析和执⾏过程:函数中的解析和执⾏过程区别不是很⼤,有⼀个(arguments)注意⼀下:1.函数的预处理和全局的预处理类似(只是加了⼀个arguments,调⽤函数时实际调⽤的参数个数)如果不是var 声明的变量,会变成最外部LE的成员,即全局变量JS的作⽤域和作⽤域链:作⽤域⼀般分为四类:块级作⽤域、函数作⽤域、动态作⽤域、词法作⽤域(静态作⽤域)块级作⽤域:(js没有块级作⽤域)函数作⽤域:没有纯粹的函数作⽤域动态作⽤域:没有动态作⽤域词法作⽤域(静态作⽤域)javascript的作⽤域为静态作⽤域f[[scope]]==LE==window分析:1.创建⼀个作⽤域对象f[[scope]]==创建它时的词法环境LE(LE==window)2.真正执⾏的时候(⼀步⼀步往上找)LE--->f.[[scope]]==window在词法解析阶段,就已经确定了相关的作⽤域。

作⽤域还会形成⼀个相关的链条,称为作⽤域链new Function的情况⼜不⼀样问题:多个函数都想要⼀个变量,每次都要写⼀个好⿇烦⽅法:将变量设置为全局变量问题:不是说要减少全局⽤量的使⽤么?因为在我们做⼤项⽬的时候难免要引⼊多个JS库,变量间的命名可能会有冲突,且出错后不易查找,这个时候我们该怎么办呢?⽅法:将变量设置在⼀个function中,问题:在外⾯⼜访问不到了,怎么办?⽅法:我们使⽤匿名函数的⽅法Javascript中的作⽤域链:当执⾏⼀段JavaScript代码(全局代码或函数)时,JavaScript引擎会创建为其创建⼀个作⽤域⼜称为执⾏上下⽂(Execution Context)在页⾯加载后会⾸先创建⼀个全局的作⽤域,然后每执⾏⼀个函数,会建⽴⼀个对应的作⽤域,从⽽形成了⼀条作⽤域链。

闭包的深度理解

闭包的深度理解

闭包的深度理解
闭包是JavaScript中一个非常重要的概念,它也是许多开发者在学习JavaScript时遇到的一个难点。

简单来说,闭包就是在一个函数内部定义另一个函数,并返回这个函数,使得这个函数可以访问到父函数的变量,即使父函数已经执行完毕。

理解闭包需要掌握以下几个概念:
1. 函数作用域:每个函数都有自己的作用域,作用域中的变量只能在该函数内部访问。

2. 作用域链:当访问一个变量时,JavaScript引擎会先在当前函数的作用域中查找,如果找不到就会沿着作用域链往上查找,直到找到全局作用域。

3. 内存管理:JavaScript使用垃圾回收机制来管理内存,当一个变量不再被引用时,JavaScript引擎就会将其从内存中删除。

当一个函数内部定义了另一个函数并返回时,返回的函数就形成了一个闭包。

闭包中的函数可以访问到外部函数中的变量,这是因为JavaScript引擎会在闭包中保存对外部变量的引用,使得这些变量不会被垃圾回收机制删除。

使用闭包可以实现许多有趣的功能,例如封装私有变量、实现模块化开发等。

但是闭包也有一些需要注意的地方,如可能导致内存泄漏和影响性能等问题。

总之,理解闭包是学习JavaScript的关键之一,掌握了闭包的使用方法和注意事项,可以让我们编写出更加优秀的JavaScript代
码。

详细图解作用域链与闭包

详细图解作用域链与闭包

详细图解作⽤域链与闭包因此本⽂的⽬的就在于,能够清晰明了得把闭包说清楚。

⼀、作⽤域与作⽤域链在详细讲解作⽤域链之前,我默认你已经⼤概明⽩了JavaScript中的下⾯这些重要概念。

这些概念将会⾮常有帮助。

基础数据类型与引⽤数据类型内存空间垃圾回收机制执⾏上下⽂变量对象与活动对象作⽤域在JavaScript中,我们可以将作⽤域定义为⼀套规则,这套规则⽤来管理引擎如何在当前作⽤域以及嵌套的⼦作⽤域中根据标识符名称进⾏变量查找。

这⾥的标识符,指的是变量名或者函数名JavaScript中只有全局作⽤域与函数作⽤域(因为eval我们平时开发中⼏乎不会⽤到它,这⾥不讨论)。

作⽤域与执⾏上下⽂是完全不同的两个概念。

我知道很多⼈会混淆他们,但是⼀定要仔细区分。

JavaScript代码的整个执⾏过程,分为两个阶段,代码编译阶段与代码执⾏阶段。

编译阶段由编译器完成,将代码翻译成可执⾏代码,这个阶段作⽤域规则会确定。

执⾏阶段由引擎完成,主要任务是执⾏可执⾏代码,执⾏上下⽂在这个阶段创建。

作⽤域链⽣命周期,如下图。

执⾏上下⽂⽣命周期我们发现,作⽤域链是在执⾏上下⽂的创建阶段⽣成的。

这个就奇怪了。

上⾯我们刚刚说作⽤域在编译阶段确定规则,可是为什么作⽤域链却在执⾏阶段确定呢?之所有有这个疑问,是因为⼤家对作⽤域和作⽤域链有⼀个误解。

我们上⾯说了,作⽤域是⼀套规则,那么作⽤域链是什么呢?是这套规则的具体实现。

所以这就是作⽤域与作⽤域链的关系,相信⼤家都应该明⽩了吧。

我们知道函数在调⽤激活时,会开始创建对应的执⾏上下⽂,在执⾏上下⽂⽣成的过程中,变量对象,作⽤域链,以及this的值会分别被确定。

之前⼀篇⽂章我们详细说明了变量对象,⽽这⾥,我们将详细说明作⽤域链。

作⽤域链,是由当前环境与上层环境的⼀系列变量对象组成,它保证了当前执⾏环境对符合访问权限的变量和函数的有序访问。

为了帮助⼤家理解作⽤域链,我我们先结合⼀个例⼦,以及相应的图⽰来说明。

执行环境,作用域理解

执行环境,作用域理解

Javascript学习---2、执行环境,作用域作者:名刘天下来源:博客园发布时间:2010-12-10 17:03 阅读:155 次原文链接[收藏]在javascript的学习中,执行环境、作用域是2个非常非常重要和基本的概念,理解了这2个概念对于javsacript中很多脚本的运行结果就能明白其中的道理了,比如搞清作用域和执行环境对于闭包的理解至关重要。

一、执行环境(exection context,也有称之为执行上下文)所有JavaScript 代码都是在一个执行环境中被执行的。

执行环境是一个概念,一种机制,用来完成JavaScript运行时在作用域、生存期等方面的处理,它定义了变量或函数是否有权访问其他数据,决定各自行为。

在javascript中,可执行的JavaScript代码分三种类型:1. Global Code,即全局的、不在任何函数里面的代码,例如:一个js文件、嵌入在HTML页面中的js代码等。

2. Eval Code,即使用eval()函数动态执行的JS代码。

3. Function Code,即用户自定义函数中的函数体JS代码。

不同类型的JavaScript代码具有不同的执行环境,这里我们不考虑evel code,对应于global code和function code存在2种执行环境:全局执行环境和函数执行环境。

在一个页面中,第一次载入JS代码时创建一个全局执行环境,全局执行环境是最外围的执行环境,在Web浏览器中,全局执行环境被认为是window对象。

因此,所有的全局变量和函数都是作为window对象的属性和方法创建的。

当调用一个JavaScript 函数时,该函数就会进入与该函数相对应的执行环境。

如果又调用了另外一个函数(或者递归地调用同一个函数),则又会创建一个新的执行环境,并且在函数调用期间执行过程都处于该环境中。

当调用的函数返回后,执行过程会返回原始执行环境。

函数级作用域和块级作用域

函数级作用域和块级作用域

函数级作用域和块级作用域变量作用域是指在程序中一些特定位置,变量被声明后的可访问范围。

它决定了变量在哪些地方可以被访问和使用。

变量作用域可以分为函数级作用域和块级作用域。

函数级作用域是指变量在函数内部声明后,只能在这个函数内部被访问和使用。

函数级作用域的变量在函数执行结束后会被销毁,无法在函数外部被访问。

一个经典的例子是在JavaScript中使用关键字var声明的变量,它的作用域就是函数级作用域。

举个例子来说明函数级作用域:```javascriptfunction examplvar x = 10;console.log(x);example(; // 输出10console.log(x); // 报错,x未定义```在这个例子中,变量x只能在函数example内部使用,无法在函数外部访问。

当函数执行结束后,变量x也会被销毁,因此在函数外部访问x会报错。

块级作用域是指变量在代码块内部声明后,只能在这个代码块内部被访问和使用。

代码块是由一对花括号{}包裹的一段代码。

在ES6之前的JavaScript版本中,块级作用域是不存在的,变量的作用域只能是函数级别的。

但是,在ES6中引入了let和const关键字,它们具有块级作用域的特性。

举个例子来说明块级作用域:```javascriptfunction examplif (true)var x = 10;let y = 20;}console.log(x); // 输出10console.log(y); // 报错,y未定义example(;```在这个例子中,变量x是用var关键字声明的,它的作用域是全局的,所以在if语句块内部声明的x可以在if语句块外部访问。

而变量y是用let关键字声明的,它的作用域只限于if语句块内部,所以在if语句块外部无法访问。

函数级作用域的优点是可以有效地控制变量的作用范围,避免变量冲突和污染全局作用域。

它适用于那些需要在函数内部执行一些特定任务的情况,可以避免函数之间的变量互相干扰和影响。

作用域和作用域链的理解

作用域和作用域链的理解

作用域和作用域链的理解1.作用域的定义作用域是一个定义了变量和函数可访问性的规则集合。

它决定了在特定区域内定义的变量和函数在哪里可见和可访问。

2.作用域的分类作用域通常分为两种类型:全局作用域和局部作用域。

- 全局作用域:全局作用域是在整个程序中都可访问的作用域。

在JavaScript中,全局作用域是指在脚本中定义的变量和函数可以在整个脚本中的任何位置被访问。

-局部作用域:局部作用域是指在函数或块级作用域中定义的变量和函数只能在其所在的函数或块级作用域中被访问。

3.作用域链的定义作用域链是指在嵌套的作用域中,变量和函数的访问规则。

当一个程序中存在多个嵌套的作用域时,每个作用域都有一个连接到父级作用域的链,这些链组成了作用域链。

4.作用域链的工作原理在JavaScript中,函数在创建时会保存一个内部属性[[Scope]],该属性包含了函数所在的作用域,即创建时的上下文环境。

当函数被调用时,会创建一个称为活动对象的对象,它包含了函数的参数和局部变量。

同时,JavaScript解释器会通过作用域链来查找变量的值。

作用域链的查找过程是由内向外的,即从当前作用域开始一级一级向上查找,直到找到所需的变量或到达全局作用域。

如果在局部作用域中找不到变量,它会沿着作用域链向上查找,直到找到变量或到达最外层的全局作用域。

以下是一个示例来说明作用域链的工作原理:```var a = 1; // 全局变量function outevar b = 2; // 外部函数变量function inner(var c = 3; // 内部函数变量console.log(a + b + c);}inner(;outer(; // 输出结果:6```在以上示例中,全局作用域中有一个变量a。

在outer(函数中,有一个变量b。

在inner(函数中,有一个变量c。

当inner(函数被调用时,它会先从自身作用域中查找变量c,如果找不到,就会向上一级作用域outer(中查找变量b,再找不到就继续向上查找全局作用域,直到找到全局作用域中的变量a。

什么是闭包?请举例说明(面试题目)

什么是闭包?请举例说明(面试题目)

什么是闭包?请举例说明(⾯试题⽬)⼀、变量的作⽤域要理解闭包,⾸先必须理解Javascript特殊的变量作⽤域。

变量的作⽤域⽆⾮就是两种:全局变量和局部变量。

Javascript语⾔的特殊之处,就在于函数内部可以直接读取全局变量。

Js代码 var n=999; function f1(){ alert(n); } f1(); // 999另⼀⽅⾯,在函数外部⾃然⽆法读取函数内的局部变量。

Js代码 function f1(){ var n=999; } alert(n); // error这⾥有⼀个地⽅需要注意,函数内部声明变量的时候,⼀定要使⽤var命令。

如果不⽤的话,你实际上声明了⼀个全局变量!Js代码 function f1(){ n=999; } f1(); alert(n); // 999--------------------------------------------------------------------------------------------------------⼆、如何从外部读取局部变量?出于种种原因,我们有时候需要得到函数内的局部变量。

但是,前⾯已经说过了,正常情况下,这是办不到的,只有通过变通⽅法才能实现。

那就是在函数的内部,再定义⼀个函数。

Js代码 function f1(){ n=999; function f2(){ alert(n); // 999 } }在上⾯的代码中,函数f2就被包括在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。

但是反过来就不⾏,f2内部的局部变量,对f1 就是不可见的。

这就是Javascript语⾔特有的“链式作⽤域”结构(chain scope),⼦对象会⼀级⼀级地向上寻找所有⽗对象的变量。

所以,⽗对象的所有变量,对⼦对象都是可见的,反之则不成⽴。

既然f2可以读取f1中的局部变量,那么只要把f2作为返回值,我们不就可以在f1外部读取它的内部变量了吗!Js代码function f1(){ n=999; function f2(){ alert(n); } return f2; } var result=f1(); result(); // 999--------------------------------------------------------------------------------------------------------三、闭包的概念上⼀节代码中的f2函数,就是闭包。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

对作用域、作用域链的理解
作用域和作用域链是JavaScript中非常重要的概念,对于理解JavaScript的运行机制和编程思想有着至关重要的作用。

作用域是指变量和函数的可访问范围,也就是说,每个变量和函数都有自己的作用域。

在JavaScript中,作用域分为全局作用域和局部作用域。

全局作用域是指在整个程序中都可以访问的变量和函数,而局部作用域则是指在函数内部定义的变量和函数,只能在函数内部访问。

作用域链是指在JavaScript中,每个函数都有一个作用域链,用于查找变量和函数。

当一个函数需要访问一个变量或函数时,它会先在自己的作用域中查找,如果找不到,就会沿着作用域链向上查找,直到找到为止。

作用域链的顶端是全局作用域,也就是说,所有的变量和函数都可以在全局作用域中访问。

作用域和作用域链的理解对于编写高质量的JavaScript代码非常重要。

首先,我们需要避免变量和函数名的冲突,因为在JavaScript 中,变量和函数名的作用域是全局的。

其次,我们需要合理地使用作用域链,避免出现不必要的性能问题。

例如,在循环中定义函数会导致每次循环都会重新创建一个函数,从而影响性能。

作用域和作用域链是JavaScript中非常重要的概念,对于理解JavaScript的运行机制和编程思想有着至关重要的作用。

我们需要深
入理解作用域和作用域链的概念,合理地使用它们,才能编写出高质量的JavaScript代码。

相关文档
最新文档