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面向对象程序设计三 原型模式(上)
Dec 21 Javascript
让低版本浏览器支持input的placeholder属性(js方法)
Apr 03 Javascript
使表格的标题列可左右拉伸jquery插件封装
Nov 24 Javascript
js实现感应鼠标图片透明度变化的方法
Feb 20 Javascript
asp.net中oracle 存储过程(图文)
Aug 12 Javascript
js判断数组key是否存在(不用循环)的简单实例
Aug 03 Javascript
jQuery操作css样式
May 15 jQuery
jQuery Form插件使用详解_动力节点Java学院整理
Jul 17 jQuery
使用vue构建移动应用实战代码
Aug 02 Javascript
浅谈在fetch方法中添加header后遇到的预检请求问题
Aug 31 Javascript
Vue组件之自定义事件的功能图解
Feb 01 Javascript
VUE2 前端实现 静态二级省市联动选择select的示例
Feb 09 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
Symfony2框架创建项目与模板设置实例详解
2016/03/17 PHP
PHP微信开发之微信消息自动回复下所遇到的坑
2016/05/09 PHP
PHP经典算法集锦【经典收藏】
2016/09/14 PHP
(仅IE下有效)关于checkbox 三态
2007/05/12 Javascript
网上应用的一个不错common.js脚本
2007/08/08 Javascript
CLASS_CONFUSION JS混淆 全源码
2007/12/12 Javascript
Javascript数组的排序 sort()方法和reverse()方法
2012/06/04 Javascript
Fixie.js 自动填充内容的插件
2012/06/28 Javascript
jQuery基本选择器选择元素使用介绍
2013/04/18 Javascript
php+ajax+jquery实现点击加载更多内容
2015/05/03 Javascript
学习JavaScript设计模式之迭代器模式
2016/01/19 Javascript
Vue组件之全局组件与局部组件的使用详解
2017/10/09 Javascript
详解设置Webstorm 利用babel将ES6自动转码成ES5
2017/12/20 Javascript
JS实现带导航城市列表以及输入搜索功能
2018/01/04 Javascript
详解vue项目打包后通过百度的BAE发布到网上的流程
2018/03/05 Javascript
vue实现组件之间传值功能示例
2018/07/13 Javascript
Vue.js的动态组件模板的实现
2018/11/26 Javascript
JavaScript数据结构与算法之基本排序算法定义与效率比较【冒泡、选择、插入排序】
2019/02/21 Javascript
JS匿名函数内部this指向问题详析
2019/05/10 Javascript
详解微信UnionID作用
2019/05/15 Javascript
Python中无限元素列表的实现方法
2014/08/18 Python
python复制文件的方法实例详解
2015/05/22 Python
理解生产者消费者模型及在Python编程中的运用实例
2016/06/26 Python
Python实现合并同一个文件夹下所有txt文件的方法示例
2018/04/26 Python
python输出决策树图形的例子
2019/08/09 Python
pygame实现俄罗斯方块游戏(基础篇1)
2019/10/29 Python
PyTorch加载自己的数据集实例详解
2020/03/18 Python
pycharm解决关闭flask后依旧可以访问服务的问题
2020/04/03 Python
tensorflow/core/platform/cpu_feature_guard.cc:140] Your CPU supports instructions that this T
2020/06/22 Python
财务总经理岗位职责
2014/02/16 职场文书
计算机售后服务承诺书
2014/05/30 职场文书
一年级语文上册复习计划
2015/01/17 职场文书
2014年终个人总结报告
2015/03/09 职场文书
行政助理岗位职责范本
2015/04/11 职场文书
物业接待员岗位职责
2015/04/15 职场文书
低版本Druid连接池+MySQL驱动8.0导致线程阻塞、性能受限
2021/07/01 MySQL