什么是js 闭包函数传入变量,变量的作用域和自执行函数

一句话告诉你闭包是什么【javascript吧】_百度贴吧
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&签到排名:今日本吧第个签到,本吧因你更精彩,明天继续来努力!
本吧签到人数:0成为超级会员,使用一键签到本月漏签0次!成为超级会员,赠送8张补签卡连续签到:天&&累计签到:天超级会员单次开通12个月以上,赠送连续签到卡3张
关注:151,613贴子:
一句话告诉你闭包是什么收藏
闭包就是函数里面定义函数,完结。
javascript?-华为软件开发云CloudIDE免费公测,为开发者提供一站式开发环境 .轻量,快速,智能的云化WebIDE平台,提升研发效率,立即体验!
下面谈谈闭包的注意事项:1. 闭包能不用就不用。2. 闭包可能有严重的性能损失和内存占用。3. 闭包尽量用在只调用一次或者少数调用的时候,因为闭包每调用一次,意味着创建很多的变量不会被释放,如果是一个循环内,内存会急剧增加。4. 没有必须用闭包才能解决的问题,所以如果你不会闭包,也没什么问题,不过闭包确实很方便。关于闭包导致的性能下降,可能很多人没有意识到。闭包可能导致几十倍的速度损失,但是因为通常不会有大量的循环,所以可以说99%的情况下,察觉不到。JavaScript 是一个垃圾收集机制的语言,和Java 一样。但是垃圾收集器可能不是一个非常优雅的设计,苹果已经抛弃它了。垃圾收集机制会导致程序的速度成倍的降低,随便打开几个网页,你会看到 chrome浏览器一个页面就需要上百Mb的内存,因为频繁的调用垃圾收集器是不可忍受的,所以就干脆使用大内存。如果你的程序没有触动垃圾收集器,可能运行的很好,但是一旦达到临界点,速度可能降低10倍。好在很多时候网页应用不会长时间的存在,它不像应用程序,比如游戏,是需要长时间运行的,这就是为什么大家能够忍受垃圾收集器。
闭包使局部变量这个概念变得混乱。局部变量一般是指,函数运行完会被自动释放的变量,JavaScript没有释放的概念,一切都是自动的,但是事实上新的解释器能够对局部变量进行优化。function abc(){
var obj = {};}如果abc函数被调用1亿次,是不是需要创建1亿个Object对象呢?或者垃圾收集器会在某个时候启动收集这些对象。事实上,不是,obj是即时释放的,因为解释器能发现obj是一个局部变量,没有必要在内存中保留它,这是极大的优化,如果这个过程中启动垃圾收集器,你会发现1亿次的执行时间延长了几倍。旧的JavaScript引擎可能就没有这样的优化。如果在abc里面使用了闭包,一切都变了,obj可能被闭包的函数使用,它不再是一个局部变量,你会看到,内存急剧增加,其实是瞬间的,因为毕竟1亿次执行其实也用不了一秒。
JavaScript有多快?同样很多人可能不知道,JavaScript函数比 C 函数还要快。如果你进行简单的算术运算,你就会发现这一点。JavaScript的算术运算,字串处理和C语言一样快,而函数调用比C还要快。这可能吗?事实上是因为JavaScript的函数和C函数的机制完全不同,JavaScript的函数只需要初始化运行环境一次,当然第一次肯定比C要慢,但是这属于编译阶段,一旦开始执行,JavaScript的函数是不需要压栈和出栈操作的,也就是你把代码写在函数体内还是另写一个函数调用它,是完全相同的。但是C函数必须要压栈出栈,这个过程有时候比执行函数本身的代码还要慢。
JavaScript总是这么快吗?No,JavaScript本身能做的事非常有限,比如算术运算,字串处理等等。但是大量的其它工作不得不求助于本地代码。事实除了简单的JavaScript运算,JavaScript要做任何事都需要本地代码,比如创建窗口部件,调用http等等。JavaScript调用本地代码会导致速度成百倍的下降,一般来说,JavaScript调用本地代码,比C函数调用本地代码要慢很多(当然,这需要看具体的应用)。这是因为需要大量的进行JavaScript数据和C数据的转换,JavaScript的字串和数字,或者属性,数组这些东西,C函数是不能直接访问的,需要进行相应的拆包操作来获取C使用的数据形式,而C函数的数据传给JavaScript也需要进行打包,这种转换非常慢,如果进行了大量的这种转换,效率就会低很多。举例来说,数组这种东西无论是JavaScript还是C语言都大量使用,C调用数组非方便,也非常快,访问数组使用整数索引几乎和使用变量没什么差别。但是,JavaScript数组实际上就是一系列的Object,每个对象都需要进行转换,才能得到一个C使用的数组,如果是一个大数组,这和C语言直接使用自己的数组相比可能会导致几个数量级的速度下降。
非常精辟~
火钳刘明@( ̄- ̄)@顺便十五字十五字十五字十五字
闭包的概念本来就多余,JavaScript函数可以调用外部的或者说任何父级的变量,直到全局变量。那么允许函数套函数的语法,就必然导致函数的局部变量保持而不能释放。其实释放不释放,根本就不用管,那是引擎的事情啊?我们用函数就可以了。换句话说,你根本就不必研究什么闭包,你只需要理解JavaScript是语法就可以了,闭包既没有什么特殊规则,也没有什么新的概念,那干嘛要引入闭包这种概念呢?
时间差都是1.1秒左右,直接计算和函数调用没有差别,如果这种简单计算用在C函数上,直接计算和函数调用可以有几倍的差距。
不理解 怎样避免自己不知不觉就用了闭包这种情况?
javascript教材,亚马逊网上书城,满59元免运费,好书不间断!850多万种中外正版精品图书一网打尽!买javascript教材,就上Z.CN!正版图书,天天低价特惠,让您挚爱阅读!
补充一点,闭包引起的性能和内存占用问题一般无需担心,至少现代的JavaScript引擎非常智能,无论是速度还是内存占用都优化的很好,除非你刻意的使用闭包,保存了没有必要保存的变量。因为他们是作为局部变量存在,可能不容易被发现。比如一个全局变量,本来只需要一个实例,但是你把它放到闭包里边,并且被内部函数使用了,它就可能有很多个实例。我觉得学习闭包的基本方法就是忘掉闭包这个词,你只需要理解JavaScript的变量作用域就足够了。
写过前端框架的话,就知道闭包的重要性了。闭包的理解不是学习出来的,写多了自然能理解并运用自如。我2年前的理解,跟楼主差不多~·~到现在才算能够合理运用闭包了。楼主喜欢javascript的话,还是多写写吧。前端性能的话,现代浏览器js加法运算每秒上亿次了,性能是次要的了,逻辑的优化,代码的可读性,远远比苛刻的追求性能重要多了。任何东西部要说的太死,容易被喷,找工作也一样,低调点好~
闭包就是函数里面定义函数,完结。类中的成员不止有函数,还有字段,字段同也可以使闭包;那么你的结论是个谬论.
登录百度帐号推荐应用今天我将会来浅谈一下关于JavaScript的立即执行函数以及闭包问题。
首先我们先要了解一下关于立即执行函数:
( function(){…} )()和( function (){…} () )是两种javascript立即执行函数的常见写法,最初我以为是一个括号包裹匿名函数,再在后面加个括号调用函数,最后达到函数定义后立即执行的目的,后来发现加括号的原因并非如此。要理解立即执行函数,需要先理解一些函数的基本概念。
函数声明、函数表达式、匿名函数
函数声明:function fnName () {…};
使用function关键字声明一个函数,再指定一个函数名,叫函数声明。
函数表达式 var fnName = function (){…};
使用function关键字声明一个函数,但未给函数命名,最后将匿名函数赋予一个变量,叫函数表达式,这是最常见的函数表达式语法形式。
匿名函数:function () {};
使用function关键字声明一个函数,但未给函数命名,所以叫匿名函数,匿名函数属于函数表达式,匿名函数有很多作用,赋予一个变量则创建函数,赋予一个事件则成为事件处理程序或创建闭包等等。
函数声明和函数表达式不同之处在于:
一、Javascript引擎在解析javascript代码时会‘函数声明升’(Function declaration Hoisting)当前执行环境(作用域)上的函数声明,而函数表达式必须等到Javascirtp引擎执行到它所在行时,才会从上而下一行一行地解析函数表达式;
二、函数表达式后面可以加括号立即调用该函数,函数声明不可以,只能以fnName()形式调用 。以下是两者差别的两个例子。
function fnName(){
//正常,因为‘提升'了函数声明,函数调用可在函数声明之前
var fnName=function(){
//报错,变量fnName还未保存对函数的引用,函数调用必须在函数表达式之后
在理解了一些函数基本概念后,回头看看( function(){…} )()和( function (){…} () )这两种立即执行函数的写法,最初我以为是一个括号包裹匿名函数,并后面加个括号立即调用函数,当时不知道为什么要加括号,后来明白,要在函数体后面加括号就能立即调用,则这个函数必须是函数表达式,不能是函数声明。
(function(a){
console.log(a)
闭包,简单来说就是函数嵌套函数,或者说定义在一个函数内部的函数,它是将函数内部和函数外部连接起来的一座桥梁。
闭包可以用在许多地方。它的最大用处有两个,一个是可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。
例1:请看下面代码:
function t2() {
var b=100;
function t3() {
console.log(b);
return t3();
运行结果为:
在这段代码中,在函数t2内部声明的变量b本来是一个局部变量,为什么在调用时t3函数能打印出b变量的值呢?原因如下:
在上面的代码中,函数t3就被包括在函数t2内部,这时t2内部的所有局部变量,对t3都是可见的。但是反过来就不行,t3内部的局部变量,对t2就是不可见的。这就是Javascript语言特有的”链式作用域”结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。
这就是闭包的其中一个作用,可以读取函数内部的一个变量。
例2:请看下面代码:
function f1(){
    var n=999;
    nAdd=function(){n+=1}
    function f2(){
      alert(n);
    return f2;
  var result=f1();
  result();
  nAdd();
  result();
在这段代码中,result实际上就是闭包f2函数。它一共运行了两次,第一次的值是999,第二次的值是1000。这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。
为什么会这样呢?原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。
这段代码中另一个值得注意的地方,就是”nAdd=function(){n+=1}”这一行,首先在nAdd前面没有使用var关键字,因此nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。
为了深入理解以上所讲内容,请看以下代码段:
var elems = document.getElementsByTagName('a');
for (var i = 0; i & elems. i++) {
elems[i].addEventListener('click', function (e) {
e.preventDefault();
alert('I am link #' + i);
}, 'false');
var elems = document.getElementsByTagName('a');
for (var i = 0; i & elems. i++) {
(function (lockedInIndex) {
elems[i].addEventListener('click', function (e) {
e.preventDefault();
alert('I am link #' + lockedInIndex);
}, 'false');
var elem = document.getElementsByTagName('div');
for(var i = 0; i & elem. i++) {
elem[i].onclick = function () {
上方是一个很常见闭包问题,点击任何div弹出的值总是5,因为当你触发点击事件的时候i的值早已是5,可以用下面方式解决:
var elem = document.getElementsByTagName('div');
for(var i = 0; i & elem. i++) {
(function (w) {
elem[w].onclick = function () {
部分代码参考:
本文已收录于以下专栏:
相关文章推荐
javascript知识点
0 == undefined false
0 == null false
函数中的隐藏数组arguments
定义函数声明
function 函数名(形参)
1)函数定义、
函数:一种代码的分组形式,可以通过这种形式赋予某组代码一个名字,以便日后重用时调用。2)、函数组成、
a. function 子句(数据类型)。
b. 函数名字,也可以没有函数,...
刚开始用vue或者react,很多时候我们都会把ES6这个大兄弟加入我们的技术栈中。但是ES6那么多那么多特性,我们需要全部都掌握吗?秉着二八原则,掌握好常用的,有用的这个可以让我们快速起飞。
一、前奏在谈回调函数之前,先看下下面两段代码:
不妨猜测一下代码的结果。function say (value) {
alert(value);
alert(say);
alert(sa...
var div = document.getElementsByTagName(&div&);
for(var i = 0; i & div. i++)...
参考原文出处1:立即执行函数
参考原文出处1:闭包函数(不全)
1.立即执行函数
1.1 立即执行函数是什么
立即执行函数就是
声明一个匿名函数
马上调用这个匿名函数
上面是一个典型的立即执行函...
js闭包以及立即发生函数
前言. 闭包的理论
  首先必须了解以下几个概念:
  执行环境
  每调用一个函数时(执行函数时),系统会为该函数创建一个封闭的局部的运行环境,即该函数的执行环境。函数总是在自己的执行环境中...
1. 全局污染
在变量声明的时候有一个规则:重复声明无效
var num = 123;
var num = 'abc';
重复声明的代码不会报任何错误,但是其声明特征就没有了,...
他的最新文章
讲师:王哲涵
讲师:韦玮
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)深入Javascript函数、递归与闭包(执行环境、变量对象与作用域链)使用详解
字体:[ ] 类型:转载 时间:
本篇文章对Javascript中函数、递归与闭包(执行环境、变量对象与作用域链)的使用进行了详细的分析介绍。需要的朋友参考下
函数表达式
1、JavaScript中定义函数有2钟方法:
  1-1.函数声明: 代码如下:function funcName(arg1,arg2,arg3){& //函数体}    ①name属性:可读取函数名。非标准,浏览器支持:FF、Chrome、safari、Opera。    ②函数声明提升:指执行代码之前会先读取函数声明。即函数调用可置于函数声明之前。
  1-2.函数表达式: 代码如下:var funcName = function(arg1,arg2,arg3){& //函数体};    ①匿名函数(anonymous function,或拉姆达函数):function关键字后无标识符,name属性值为空字符串。在把函数当成值使用时,都可用匿名函数。    ②类似其他表达式,函数表达式使用前需先赋值,故不存在"函数声明提升"那样的作用。    ③ECMAScript中的无效函数语法: 代码如下:if判断中的重复函数声明
if(condition){&&& function sayHi(){&&&&&&& alert("Hi!");&&& }} else {&&& function sayHi(){&&&&&&& alert("Yo!");&&& }}      浏览器JavaScript引擎修正错误差异:大多浏览器会返回第二个声明,忽略condition;FF则会在condition为true时返回第一个声明。      使用函数表达式可解决并实现: 代码如下:if判断 函数表达式
var sayHi;if(condition){&&& sayHi = function(){&&&&&&& alert("Hi!");&&& }} else {&&& sayHi = function(){&&&&&&& alert("Yo!");&&& }}2、递归  递归函数,是在一个函数中通过名字调用自身的情况下构成的。 代码如下:function factorial(num){&& //一个经典的递归阶乘函数&&& if (num &= 1){&&&&&&& return 1;&&& } else {&&&&&&& return num * factorial(num-1);&&& }}&    ①若使用下列代码调用该函数,会出错: 代码如下:var anotherFactorial =factorial =alert(anotherFactorial(4));      将factorial()函数保存到变量anotherFactorial中后,将factorial变量设为null后不再引用函数,而anotherFactorial(4)中要执行factorial()函数,故出错。      使用argument.callee(指向正在执行的函数的指针)可解决: 代码如下:解决方案
function factorial(num){&&& if (num &= 1){&&&&&&& return 1;&&& } else {&&&&&&& return num * arguments.callee(num-1);&&& }}
var anotherFactorial =factorial =alert(anotherFactorial(4));& //24    在非严格模式,使用递归函数时,用argument.callee代替函数名更保险    在严格模式下,使用argument.callee会出错,可用函数表达式 代替 函数声明: 代码如下:函数表达式代替函数声明
var factorial = function f(num){&&& if (num &= 1){&&&&&&& return 1;&&& } else {&&&&&&& return num * f(num-1);&&& }}4、闭包
  指有权访问另一个函数作用域中的变量的函数。(常见形式为函数嵌套) 代码如下:function wai(pro){&&& return function(obj1,obj2){&&&&&&& var val1 = obj1[pro];&&&&&&& var val2 = obj2[pro];&&&&&&& if(val1&val2){&&&&&&&&&&& return -1;&&&&&&& }else if(val1&val2){&&&&&&&&&&& return 1;&&&&&&& }else{&&&&&&&&&&& return 0;&&&&&&& };&&& }}    return匿名函数时,匿名函数的作用域链初始化为包含函数的活动对象和全局变量对象。即匿名函数包含wai()函数的作用域。  每个函数被调用时,会创建一个执行环境、一个变量对象 及 相应的作用域链。
4-1.执行环境 及 作用域
  执行环境execution context简称环境,定义了变量和函数有权访问的其他数据,并决定他们的各自行为。  ①每个执行环境都有一个变量对象variable object,保存环境定义的所有变量和函数。该对象无法编码访问,但解析器在处理数据时会在后台使用它。  & 全局变量对象是最外围的一个执行环境。在Web浏览器中被认为是window对象,故所有全局对象和函数都是window对象的属性和方法创建的。  & 执行环境中的代码执行完后,该环境就被销毁,保存其中的变量和函数定义也随之销毁。
  ②代码在环境中执行时,会创建变量对象的一个作用域链scope chain,用于保证对执行环境有权访问的所有变量和函数的有序访问。  & 作用域链前端,始终是当前执行的代码所在环境的变量对象。当该环境为函数时,会将活动对象作为变量对象。  & 活动对象最开始只包含一个变量,即argumnt对象。  & 作用域链中的下一个变量对象来自包含环境,而下一个变量对象来自下一个包含环境,直至延续到全局执行环境。
  ③标识符解析:从前段开始,沿着作用域链一级一级地搜索标识符的过程。【找不到通常会导致错误发生】
4-2.函数创建、执行时: 代码如下:function compare(val1,val2){&&&& if(val1&val2){&&&&&&& return -1;&&& }else if(val1&val2){&&&&&&& return 1;&&& }else{&&&&&&& return 0;&&& };}var result = compare(5 , 10);  ①创建函数compare()时,会创建一个预先包含全局变量对象的作用域链,并保存在内部[[scope]]属性中。  ②局部函数compare()的变量对象,只在函数执行的过程中存在。   当调函数时,会创建一个执行环境,再通过复制函数的[[scope]]属性中的对象 构建起执行环境的作用域链。  ③第一次调用函数时,如compare(),会创建一个包含this、argument、val1 和 val2的活动对象。  ④全局执行环境的变量对象(包括this、result、compare)在compare()执行环境的作用域链中处于第二位。  ⑤作用域链 本质是一个指向变量对象的指针列表,只引用但不实际包含变量对象。  ⑥无论什么时候在函数中访问一个变量,都会行作用域链中搜索具有相应名字的变量。
4-3.闭包的作用域链
  在另外一个函数内部定义的函数会将包含函数的活动对象添加到它的作用域链中。  ①将函数对象赋值null,等于通知垃圾回收例程将其清除,随着函数作用域链被销毁,其作用域链(不除了全局作用域)也会被安全销毁。  ②由于闭包会携带包含函数的作用域,所以会比其他函数占用更多内存。
4-4.闭包与变量
  作用域链的一个副作用:闭包只能取得包含函数中任何变量的最后一个值。 代码如下:function createFunctions(){&&& var result = new Array();&&& for (var i=0; i & 10; i++){&&&&&&& result[i] = function(){&&&&&&&&&&&&&&&&&& };&&& }&&&}  ①createFunctions()函数,将10个闭包赋值给数组result,再返回result数组。每个闭包都返回自己的索引,但实际上都返回10。   因为每个函数(闭包)的作用域链中都保存着createFunctions()函数的活动对象,所以它们引用的是同一个变量i,当createFunctions函数执行完后i的值10,故闭包中的i也都为10。  ②解决办法,不使用闭包,创建一个匿名函数,将i值赋值给其参数: 代码如下:function createFunctions(){&&& var result = new Array();&&& for (var i=0; i & 10; i++){&&&&&&& result[i] = function(num){&&&&&&&&&&& return function(){&&&&&&&&&&&&&&&&&&&&&&&&&& };&&&&&&& }(i);&&& }&&&}  创建一个每次循环都会执行一次的匿名函数:将每次循环时包围函数的i值作为参数,存入匿名函数中。因为函数参数是按值传递的,而非引用,所以每个匿名函数中的num值 都为每此循环时i值的一个副本。
4-5.this对象
  this对象是在运行时基于函数的执行环境绑定的。    在全局函数中,this等于window;当函数被某对象调用时,this为该对象。    匿名函数的执行环境有全局性,其this对象通常指window。通过call()或spply()改变函数执行环境时,this指向其对象。  ①每个函数在被调用时,都会自动取得两个特殊变量:this和argument。内部函数在搜索这两个变量时,只会搜索到期活动对象为止,永远不可能访问外部函数的这两个变量。    不过将外部作用域的this对象保存在一个闭包能访问的变量里,就可让闭包访问该对象。 代码如下:闭包 访问外部函数的this对象
var name = "The Window";
var object = {&&& name : "My Object",
&&& getNameFunc : function(){&&&&&&& var that =&&&&&&& return function(){&&&&&&&&&&& return that.&&&&&&& };&&& }};
alert(object.getNameFunc()());& //"MyObject"   包围函数的argument对象 也可通过此方法被闭包访问。
5、函数声明 转换为 函数表达式
  JavaScript将function关键字昨晚函数声明的开始,但函数声明后面不能跟圆括号,所以function(){......}();会出错。  要将函数声明转换为函数表达式,需为函数声明加一对圆括号: 代码如下:(function(){&& //块级作用域})();
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具为什么javascript中匿名自执行函数可以算作闭包? - 知乎1被浏览441分享邀请回答11 条评论分享收藏感谢收起0添加评论分享收藏感谢收起

我要回帖

更多关于 js 立即执行函数 闭包 的文章

 

随机推荐