JavaScript中最容易混淆的作用域、提升、闭包知识详解(推荐)_javascript技巧

一、函数作用域

1.函数作用域

就是作用域在一个“Function”里,属于这个函数的全部变量都可以在整个函数的范围内使用及复用。

function foo(a) {
var b = 2;
function bar() {
// ...
}
var c = 3;
}
bar(); // 失败
console.log( a, b, c ); // 三个全都失败

上面的“foo”函数内的几个标识符,放到函数外面访问就都会报错。

2.立即执行函数表达式

在任意代码片段外部添加包装函数,可以将内部的变量和函数定义“隐藏”起来,外部作用域无法访问包装函数内部的任何内容。

例如上面的bar、a等几个标识符。这样能够保护变量不被污染。

在写插件的时候经常会用到立即执行函数表达式,为的就是保护里面的变量。

var a = 2;
(function foo() {
var a = 3;
console.log( a ); // 3
})();
console.log( a ); // 2

“foo”中第一个( )将函数变成表达式,第二个( )执行了这个函数。

有一个专用术语:IIFE,代表立即执行函数表达式(Immediately Invoked Function Expression);

1. 进阶用法是把它们当作函数调用并传递参数进去

(function IIFE( global ) {
var a = 3;
console.log( a ); // 3
console.log( global.a ); // 2
})( window );

2. 一种变化的用途是倒置代码的运行顺序,在CMD或AMD项目中被广泛使用。

(function IIFE(factory) {
factory( window );
})(function def( global ) {
var a = 3;
console.log( a ); // 3
console.log( global.a ); // 2
});

二、块作用域

JavaScript不支持块作用域。

for(var i=0; i<10; i++) {
console.log( i );
}

上面的代码中的“i”相当于下面的

var i;
for(i=0; i<10; i++) {
console.log( i );
}

但也有例外,“try/catch”,catch就是一个块作用域。

try{
undefined(); // 执行一个非法操作来强制制造一个异常
}
catch(err) {
console.log( err ); // 能够正常执行!
}
console.log( err ); // ReferenceError: err not found

ES6改变了现状,引入了新的let关键字,let关键字可以将变量绑定到所在的任意作用域中(通常是{ .. }内部)。换句话说,let为其声明的变量隐式地了所在的块作用域。

三、提升

函数作用域和块作用域的行为是一样的,可以总结为:任何声明在某个作用域内的变量,都将附属于这个作用域。

1)编译与执行

变量和函数的所有声明都会在任何代码被执行前首先被处理,可以看下面的代码事例。

a = 2;
var a;
console.log(a);//2

这段代码等价于:

var a;//定义声明是在编译阶段进行
a = 2;//赋值声明会被留在原地等待执行阶段
console.log(a);

2)函数优先

函数会首先被提升,然后才是变量。

foo(); // 1
var foo;
function foo() {
console.log( 1 );
}
foo = function() {
console.log( 2 );
};

var foo函数表达式尽管出现在function foo()的声明之前,但它是重复的声明(因此被忽略了),因为函数声明会被提升到普通变量之前。

而上面的代码相当于:

function foo() {
console.log( 1 );
}
foo(); // 1
foo = function() {
console.log( 2 );
};

四、闭包

闭包是指有权访问另一个函数作用域中变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,
通过另一个函数访问这个函数的局部变量,利用闭包可以突破作用链域,将函数内部的变量和方法传递到外部

闭包的特性:

1.函数内在嵌套函数

2.内部函数可以引用外层的参数和变量

3.参数和变量不会被垃圾回收机制回收

1)定义

当函数可以记住并访问所在的作用域时,就产生了闭包,即使函数是在当前作用域之外执行。

function foo() {
var a = 2;
function bar() {
console.log( a );
}
return bar;
}
var baz = foo();
baz(); // 2 —— 这就是闭包的效果。

1. 将函数“bar”赋值给“baz”,执行“baz”,当前作用域并不在“bar”的作用域,但是可以执行。

2. 闭包还会阻止垃圾回收,当“foo”执行完后,内部作用域仍然存在。这样才能让“baz”执行。

2)将函数作为参数传递

function foo() {
var a = 2;
function baz() {
console.log( a ); // 2
}
bar( baz );
}
function bar(fn) {
fn(); //这就是闭包!
}

把内部函数baz传递给bar,当调用这个内部函数时(fn),它涵盖的foo()内部作用域的闭包就可以观察到了,因为它能够访问a。

如果将函数当作第一级的值类型并到处传递,你就会看到闭包在这些函数中的应用。

在定时器、事件监听器、Ajax请求、跨窗口通信、Web Workers或者任何其他的异步(或者同步)任务中,只要使用了回调函数,实际上就是在使用闭包!

3)循环和闭包

for (var i = 1; i <= 5; i++) {
setTimeout(function timer() {
console.log(i);
}, i * 1000);
}

每次打印出来都将会是6,延迟函数的回调会在循环结束时才执行

根据作用域的工作原理,实际情况是尽管循环中的五个函数是在各个迭代中分别定义的,但是它们都被封闭在一个共享的全局作用域中,因此实际上只有一个i。

现在用闭包来实现每次打印不同的i。

for (var i = 1; i <= 5; i++) {
(function(j) {
setTimeout(function timer() {
console.log(j);
}, j * 1000);
})(i);
}

IIFE会通过声明并立即执行一个函数来创建作用域。setTimeout中的回调可以记住当前的作用域,每个作用域中的参数“j”都是不同的。

以上所述是小编给大家介绍的JavaScript中最容易混淆的作用域、提升、闭包知识详解,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索js
, 闭包
, 作用域
提升
javascript闭包详解、js作用域与闭包的概念、从作用域链谈闭包、作用域链 闭包、闭包作用域,以便于您获取更多的相关知识。

时间: 2024-09-17 04:06:07

JavaScript中最容易混淆的作用域、提升、闭包知识详解(推荐)_javascript技巧的相关文章

jQuery中each()、find()和filter()等节点操作方法详解(推荐)_jquery

1.each(callback) 官方解释: 返回值:jQuery 概述 以每一个匹配的元素作为上下文来执行一个函数. 意味着,每次执行传递进来的函数时,函数中的this关键字都指向一个不同的DOM元素(每次都是一个不同的匹配元素).而且,在每次执行函数时,都会给函数传递一个表示作为执行环境的元素在匹配的元素集合中所处位置的数字值作为参数(从零开始的整型). 返回 'false' 将停止循环 (就像在普通的循环中使用 'break').返回 'true' 跳至下一个循环(就像在普通的循环中使用'

JavaScript中的原型和继承详解(图文)_javascript技巧

请在此暂时忘记之前学到的面向对象的一切知识.这里只需要考虑赛车的情况.是的,就是赛车. 最近我正在观看 24 Hours of Le Mans ,这是法国流行的一项赛事.最快的车被称为 Le Mans 原型车.这些车虽然是由"奥迪"或"标致"这些厂商制造的,可它们并不是你在街上或速公路上所见到的那类汽车.它们是专为参加高速耐力赛事而制造出来的. 厂家投入巨额资金,用于研发.设计.制造这些原型车,而工程师们总是努力尝试将这项工程做到极致.他们在合金.生物燃料.制动技术

javascript作用域和闭包使用详解_基础知识

作用域的嵌套将形成作用域链,函数的嵌套将形成闭包.闭包与作用域链是 JavaScript 区别于其它语言的重要特性之一. 作用域JavaScript 中有两种作用域:函数作用域和全局作用域. 在一个函数中声明的变量以及该函数的参数享有同一个作用域,即函数作用域.一个简单的函数作用域的例子: 复制代码 代码如下: function foo() {    var bar = 1;    {        var bar = 2;    }    return bar; // 2} 不同于C等其它有块

javascript中加var和不加var的区别 你真的懂吗_javascript技巧

Javascript是遵循ECMAScript标准下的一个产物,自然ECMAScript的标准其要遵循. 先来看下var关键字的定义和用法 var 语句用于声明变量. JavaScript 变量的创建也叫作"声明"一变量: 复制代码 代码如下: var carName; 变量声明后,变量为空 (没有值). 为变量复制,操作如下: 复制代码 代码如下: carName = "Volvo"; 声明变量时,你同样可以为变量赋值: 复制代码 代码如下: var carNam

JavaScript中变量声明有var和没var的区别示例介绍_javascript技巧

本文来论述JavaScript中变量声明有var和没var的区别,关于Js中的变量声明的作用域是以函数为单位,所以我们经常见到避免全局变量污染的方法是 (function(){ // ... })(); 在函数内部,有var和没var声明的变量是不一样的.有var声明的是局部变量,没var的,声明的全局变量,所以可以借此向外暴露接口东东. 在全局作用域内声明变量时,有var 和没var看起来都一样,我们知道,声明的全局变量,就是window的属性,究竟是否一样,我们通过ECMAScrpit5提供

JavaScript中如何使用cookie实现记住密码功能及cookie相关函数介绍_javascript技巧

cookie是网站设计者放置在客户端(浏览器)的小文本文件,cookie不仅能够实现保存密码功能,还可以通过cookie保存最近浏览记录增加用户体验. 在登录界面添加记住密码功能,我首先想到的是在java后台中调用cookie存放账号密码,大致如下: HttpServletRequest request HttpServletResponse response Cookie username = new Cookie("username ","cookievalue"

Javascript中判断一个值是否为undefined的方法详解_javascript技巧

前言 相信大家都知道当声明一个变量,并且没有给赋值的情况下,它的初始值是undefined.但是在javascript中,怎么检查一个值是否为undefined呢? 简单来说,在现代浏览器中,你可以安全的比较变量是否为undefined if (name === undefined) {...} 一些人反对直接使用undefined变量进行比较,因为在旧的浏览器中允许它的值被重新赋值,比如下面这样: undefined = "test" 在被重新赋值后,使用undefined指令将不能

由JavaScript中call()方法引发的对面向对象继承机制call的思考_javascript技巧

起因: 今天在阅读snandy大神的读jQuery之五(取DOM元素)时,看到有讲到toArray()方法,具体jQuery代码如下: 复制代码 代码如下: toArray: function() { return slice.call( this, 0 ); }, get: function( num ) { return num == null ? // Return a 'clean' array this.toArray() : // Return just the object ( n

JavaScript中style.left与offsetLeft的使用及区别详解_javascript技巧

如果父div的position定义为relative,子div的position定义为absolute,那么子div的style.left的值是相对于父div的值,这同offsetLeft是相同的,区别在于: 1. style.left 返回的是字符串,如28px,offsetLeft返回的是数值28,如果需要对取得的值进行计算,还用offsetLeft比较方便. 2. style.left是读写的,offsetLeft是只读的,所以要改变div的位置,只能修改style.left. 3. st