一、javascript作用域的理解
- 1、什么是作用域
(1)、作用域是根据名称查找变量的一套规则。如果查找的目的是对变量进行赋值,那么会使用LHS查询;如果目的是获取变量的值,就会使用RHS查询。
(2)、在当前作用域中无法找到某个变量时,会在外层嵌套的作用域中继续查找,直到找到或者到最外层的作用域(全局作用域)为止。
如:
function foo(a){
console.log(a+b);//b在函数作用域内无法找到,可以在上一级作用域找到b=2;
}
var b=2;//外层作用域
foo(2);//4
(3)、不成功的RHS引用会导致抛出ReferenceError异常;不成功的LHS引用会导致自动隐式的创建一个全局变量(非严格模式下),该变量使用LHS引用的目标作为标识符,或者抛出ReferenceError异常(严格模式下)
- 2、词法作用域
作用域分为词法作用域和动态作用域
(1)、词法作用域就是定义在词法阶段的作用域。换句话说,词法作用域是由你在写代码时将变量和块作用域写在哪里决定的。
如:
function foo(a){
var b=a*2;
function bar(){
console.log(a,b,c);
}
bar(b*3);
}
foo(2);//最终输出2,4,12
说明:
最外层包含整个全局作用域,其中只有一个标识符:foo。
foo层包含所创建的作用域,有三个标识符 a,bar,b。
bar层包含所创建的作用域,只有一个标识符 c。
(2)、欺骗词法 在运行时“修改”词法的作用域,有两种机制来实现整个目的,一、用eval()函数,二、用with语句。但不推荐使用这两种方法,欺骗词法作用域会导致性能下降。
如:
function(str,a){
eval(str);//欺骗,将屏蔽了外层var b=2;将b定义为3
console.log(a,b);//1,3
}
var b=2;
foo("var b=3;",1);
with 本质上是通过将一个对象的引用当作作用域来处理,将对象的属性当做作用域的标识符来处理,从而创建了 一个新的词法作用域(在运行时)。
这两个机制的副作用是引擎无法再编译时对作用域查找进行优化,将导致代码运行变慢。不要使用它们。
- 3、函数和块作用域
(1)、函数作用域是指属于这个函数的全部变量都可以在整个函数的范围内使用及复用。
任意代码片段外部添加包装函数,可以将内部的变量和函数定义“隐藏”起来,外部作用域无法访问包装函数内部的任何内容。
如:
var a=2;
function foo(){
var a=3;
console.log(a);//3 访问的是foo函数内部变量a.如果foo函数内部没有定义a,则访问外部var a=2.
}
foo();
console.log(a);//2 访问的是全局变量a,不能访问foo函数里面的变量 var a=3
(2)、函数表达式
(function foo(){
var a=3;
})();
作为函数表达式,意味着foo只能在所代表的位置中访问,外部作用域则不行。函数被包含在一对()括号内部,因此成为一个表示式,通过末尾加上另外一个()可以立即执行这个函数。
(3)、块作用域
块作用域是只变量在一个代码块中有效。
最熟悉的块作用域:
for(var i=0;i<10;i++){
console.log(i);//0,1,2,3,4,5,6,7,8,9
}
console.log(i);//10
可以看出i并不是在for块中有效,在外层作用域依然可以访问到。像if块里面的定义的变量也是一样,那么怎么样才是块作用域?
a、从with对象中创建的作用域
b、try/catch 的catch分句会创建一个块作用域,其中声明的变量仅在catch内部有效。
如:
try{
undefined();//执行一个非法操作来强制抛出异常
}catch(err){
console.log(err);//正常打印出错信息
}
console.log(err);//ReferenceError:err not found
c、let 关键字
let关键字可以将变量绑定到所在的任意作用域中。通常{}内部。
重新看回for循环的代码块:将var 变为let定义i
for(let i=0;i<10;i++){
console.log(i);//正常输出
}
console.log( i);//
ReferenceError: i is not defined 无法再外层引用,i只在for 代码块有效。
d、const 同样可以用来创建块作用域变量,但其值是固定的(常量)
- 4、作用域闭包
(1)、什么是闭包?当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。(能够读取其他函数内部变量的函数)
如:
function foo(){
var a=2;
function baz(){
console.log(a);//2
}
bar(baz);
}
function bar(fn){
fn();//妈妈快看啊!这就是闭包
};
只要使用回调函数,实际上就是在使用闭包。
(2)、闭包与循环
for循环是最常见的例子:
for(var i=1;i<=5;i++){
setTimeout(function(){
console.log(i);// 输出5次6
},i*1000);
}
预想情况,是分别输出1~5,每秒一次,每次一个。
实际情况,每秒一次输出6.
因为,试图假设循环中每个迭代在运行时都给自己“捕获”一个i的副本,但是根据作用域的工作原理,实际情况是尽管循环中的五个函数是在各个迭代中分别定义的,
但是他们都被封闭在一个共享的全局作用域中,因此实际上只有一个i。
要想按预期输出需要有自己的变量,用来在每个迭代中存储i的值:
for(var i=1;i<=5;i++){
(function(){
var j =i;
setTimeout(function(){
console.log(j);
},j*1000);
})();
}
或者 可以用let 块作用域
for(let i=1;i<=5;i++){
setTimeout(function(){
console.log(i);
},i*1000);
}
(3)、模块
模块模式需要具备两个必要条件:
1、必须有外部的封闭函数,该函数必须至少被调用一次(每次调用都会创建一个新的模块实例);
2、封闭函数必须返回至少一个内部函数,这样内部函数才能在私有作用域中形成闭包,并且可以访问或者修改私有的状态。
如:
var foo = (function CoolModule(id){
function change(){
publicAPI.identify = identify2;
}
function identify1(){
console.log(id);
}
function identify2(){
console.log(id.toUpperCase());
}
var publicAPI ={
change:change,
identify:identify1
}
return publicAPI;
})("foo module");
foo.identify();//foo module
foo.change();
foo.identify();//FOO MODULE
相关推荐
作用域链是JavaScript中用来查找变量和函数的一系列嵌套的作用域。当代码试图访问一个变量时,引擎会从当前作用域开始查找,如果未找到,则在上一级作用域继续查找,依此类推,直到作用域链的最顶端。这一机制使得...
在JavaScript编程中,作用域和作用域链是影响程序性能的关键因素之一。优化作用域链是提高JavaScript代码执行效率的重要手段。下面我们从多个方面来详细探讨作用域链的概念、作用域链的层级关系、变量访问的性能影响...
例如,闭包是JavaScript中的一个高级特性,它允许函数访问并操作其外部作用域的变量,常用于实现模块化和数据封装。 "JavaScript专题系列"则可能包含了如事件循环、异步编程(Promise、async/await)、DOM操作、...
JavaScript是一种广泛用于网页和网络应用的脚本语言,它的核心特性包括原型链和作用域链。...总结来说,JavaScript的原型链和作用域链是其灵活性和强大功能的基础,理解它们对于深入学习和优化JavaScript代码至关重要。
JavaScript内核系列是一份深入探讨JavaScript编程语言及其内核的宝贵资源,对于正在学习或从事JavaScript开发的学生和程序员来说,它提供了丰富的知识和技术指导。这份资料详细阐述了JavaScript的各个方面,包括语法...
JavaScript支持两种主要的作用域类型:全局作用域和函数作用域。闭包是一种特殊的作用域规则,允许一个函数访问并记住它外部函数的作用域内的变量,即使外部函数已经执行完毕。 **函数的一等公民地位:** 在...
JavaScript中的变量作用域和作用域链是编程中非常重要的概念,尤其对于JavaScript这种函数作用域的语言来说更是如此。本文将详细解析这两个知识点,并通过实例帮助理解。 **一、变量作用域** JavaScript中的变量...
- 在此阶段,JavaScript引擎会创建变量和函数的作用域链,并进行变量提升。 3. **执行**:根据预编译的结果执行代码。 通过以上介绍,我们可以看出JavaScript虽然是一门简单易学的语言,但在深入了解其内部原理后...
- 函数:函数是JavaScript中的可重用代码块,学习函数声明、函数表达式、参数传递以及作用域是必不可少的。 2. **对象与数组** - 对象:JavaScript中的对象是键值对的集合,可以使用字面量表示法或构造函数创建。...
本“JavaScript系列教程3”旨在为初学者提供全面而深入的JavaScript基础知识。在前两节教程中,我们可能已经介绍了变量、数据类型、操作符、控制流以及函数等基本概念。现在,我们将继续深化对JavaScript的理解。 ...
《深入理解JavaScript系列》是汤姆大叔精心编写的关于...通过学习《深入理解JavaScript系列》,无论是新手还是经验丰富的开发者,都能进一步提升对JavaScript的理解,掌握其精髓,从而在实际项目中发挥出更大的效能。
闭包则是JavaScript中的高级特性,它允许函数访问并操作其外部作用域的变量,即使在函数执行完毕后仍能保留这些状态。 4. **原型与继承**:JavaScript的面向对象特性体现在原型链上,通过原型可以实现对象间的继承...
以上只是《你不知道的JavaScript》中部分重要知识点的概述,深入学习这本书需要理解并掌握这些概念,以及它们在实际开发中的应用。通过实践和探索,开发者可以进一步提升JavaScript编程能力,成为一名更优秀的前端...
此外,还会讲解到作用域、闭包等进阶主题,这些都是编写高效、可维护代码的关键。 DOM(Document Object Model)编程是JavaScript应用的重要领域。DOM是HTML和XML文档的结构化表示,JavaScript通过DOM API可以查找...
2. **函数与作用域**:了解函数的定义和调用,以及作用域的概念,包括全局作用域、局部作用域和块级作用域。此外,还有闭包,它是JavaScript中的一个关键特性,能实现数据隐藏和记忆功能。 3. **对象与原型链**:...
4. 执行上下文:每次JavaScript代码执行时,都会创建一个新的执行上下文,分为全局执行上下文、函数执行上下文和块级作用域。 二、JavaScript面向对象基础 JavaScript的面向对象编程主要基于原型(prototype)、...
此外,闭包也是JavaScript中的一个核心概念,它允许函数访问并操作外部作用域的变量,即使在其外部函数已经执行完毕的情况下。 接下来,JavaScript中的事件处理是网页交互的关键。通过事件监听器,开发者可以响应...
JavaScript的语法特性方面,包括变量声明(var、let、const)、数据类型(基本类型与引用类型)、运算符(算术、比较、逻辑、赋值、三元)、流程控制(顺序、分支、循环)、函数(声明、表达式、参数、作用域)等。...
闭包提供了一种方式来访问外部作用域中的变量,即使在其定义的作用域之外。原型则涉及JavaScript的对象继承机制,所有对象都具有一个`__proto__`属性,可以通过原型链来共享属性和方法。 文件名“第1章”可能暗示了...