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


Posted in Javascript onSeptember 05, 2016

一、函数作用域

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中最容易混淆的作用域、提升、闭包知识详解,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
javascript fullscreen全屏实现代码
Apr 09 Javascript
javascript的数组和常用函数详解
May 09 Javascript
JavaScript实现数字数组正序排列的方法
Apr 06 Javascript
JS实现鼠标滑过显示边框的菜单效果
Sep 21 Javascript
Java  Spring 事务回滚详解
Oct 17 Javascript
jQuery+json实现动态创建复杂表格table的方法
Oct 25 Javascript
详解JS中定时器setInterval和setTImeout的this指向问题
Jan 06 Javascript
Vue.js实现多条件筛选、搜索、排序及分页的表格功能
Nov 24 Javascript
图片懒加载imgLazyLoading.js使用详解
Sep 15 Javascript
JavaScript中CreateTextFile函数
Aug 30 Javascript
vue v-on:click传递动态参数的步骤
Sep 11 Javascript
design vue 表格开启列排序的操作
Oct 28 Javascript
Vuejs第六篇之Vuejs与form元素实例解析
Sep 05 #Javascript
Vue表单实例代码
Sep 05 #Javascript
AngularJS实现数据列表的增加、删除和上移下移等功能实例
Sep 05 #Javascript
JS实现的手机端精简幻灯片效果
Sep 05 #Javascript
jQuery实现页面下拉100像素出现悬浮窗口的方法
Sep 05 #Javascript
Vuejs第九篇之组件作用域及props数据传递实例详解
Sep 05 #Javascript
AngularJS实现标签页的两种方式
Sep 05 #Javascript
You might like
php md5下16位和32位的实现代码
2008/04/09 PHP
php模仿asp Application对象在线人数统计实现方法
2015/01/04 PHP
mod_php、FastCGI、PHP-FPM等PHP运行方式对比
2015/07/02 PHP
Yii框架实现记录日志到自定义文件的方法
2017/05/23 PHP
使用prototype.js 的时候应该特别注意的几个问题.
2007/04/12 Javascript
JavaScript CSS菜单功能 改进版
2008/12/20 Javascript
js去空格技巧分别去字符串前后、左右空格
2013/10/21 Javascript
jQuery中on()方法用法实例
2015/01/19 Javascript
Angularjs编写KindEditor,UEidtor,jQuery指令
2015/01/28 Javascript
使用JS中的exec()方法构造正则表达式验证
2016/08/01 Javascript
Angular中使用ui router实现系统权限控制及开发遇到问题
2016/09/23 Javascript
源码分析Vue.js的监听实现教程
2017/04/23 Javascript
vue如何在自定义组件中使用v-model
2018/05/14 Javascript
vue 组件中添加样式不生效的解决方法
2018/07/06 Javascript
vue-resource请求实现http登录拦截或者路由拦截的方法
2018/07/11 Javascript
使用vue-router为每个路由配置各自的title
2018/07/30 Javascript
JavaScript Canvas实现验证码
2020/08/02 Javascript
跟老齐学Python之让人欢喜让人忧的迭代
2014/10/02 Python
python 2.7.14安装图文教程
2018/04/08 Python
Pycharm无法使用已经安装Selenium的解决方法
2018/10/13 Python
Python 实现取矩阵的部分列,保存为一个新的矩阵方法
2018/11/14 Python
PyTorch搭建一维线性回归模型(二)
2019/05/22 Python
python异步实现定时任务和周期任务的方法
2019/06/29 Python
深入了解Django中间件及其方法
2019/07/26 Python
pytorch载入预训练模型后,实现训练指定层
2020/01/06 Python
Python实现投影法分割图像示例(一)
2020/01/17 Python
Python如何使用PIL Image制作GIF图片
2020/05/16 Python
Python单元测试及unittest框架用法实例解析
2020/07/09 Python
冰淇淋店的创业计划书
2014/02/07 职场文书
《桃花心木》教学反思
2014/02/17 职场文书
离婚协议书该怎么写
2014/10/04 职场文书
预备党员群众路线思想汇报2014
2014/10/25 职场文书
班主任寄语2015
2015/02/26 职场文书
深度好文:50条没人告诉你的人生经验,句句精辟
2019/08/22 职场文书
安装Ruby和 Rails的详细步骤
2022/04/19 Ruby
如何使用注解方式实现 Redis 分布式锁
2022/07/23 Redis