javascript 执行环境,作用域链和闭包
首先看下这条语句:
(function($) {…})(jQuery);
1.原理:
function(arg){…}
这就定义了一个匿名函数,参数为arg
而调用函数时,是在函数后面写上括号和实参的,由于操作符的优先级,函数本身也需要用括号,即:
(function(arg){…})(param)
这就相当于定义了一个参数为arg的匿名函数,并且将param作为参数来调用这个匿名函数
而(function($){…})(jQuery)则是一样的,之所以只在形参使用$,是为了不与其他库冲突,所以实参用jQuery
相当于
funtion output(s){…};
output(jQuery);
或者
var fn=function(s){…};fn(jQuery);
2.作用:
这种写法的最大好处是形成闭包。在(function($) {…})(jQuery)在内部定义的函数和变量只能在此范围内有效。
形成私有函数、私有变量的概念。
几个概念:
1、执行环境(execution context):
每个函数都有自己的执行环境,当执行流进入一个函数时,函数的环境就会被推入一个环境栈中。而在函数执行后,栈将其环境弹出,把控制权返回给之前的执行环境。
2、作用域链(scope chain):
函数的内部环境可以通过作用域链访问到所有的外部环境,但是外部环境却不可以访问外部环境,这就是作用域
ES5中只有全局作用域和函数作用域,没有块级作用域。
(但在ES6中多了一个let,他可以保证外层块不受内层块的影响。即内层块形成了一个块级作用域,这是let的一个特点。
它不简单,因为在许多的函数嵌套的情景下,只有对它理解深刻,才能更好的去分析。)
3、闭包 :指有权访问另一个函数作用域中的变量的函数
创建闭包的常见方式,就是在一个函数内部创建另一个函数(通常是匿名函数)。
在一个函数a内部定义的另一个函数b,当b在a之外被执行时,就会形成闭包。同时b函数仍然可以访问到a函数中的局部变量与函数。
弊端:闭包会携带包含他的函数的作用域,会比其他函数占用更多的内存。所以请慎用闭包
function fn(){ var array=[]; for(var i=0;i<10;i++){ array[i]=function(){ return i; } } return array; } fn();//[ƒ, ƒ, ƒ, ƒ, ƒ, ƒ, ƒ, ƒ, ƒ, ƒ]
我们的本意是得到这个数组中每个函数都能返回自己的索引值,可是得到的是每个函数却都返回了10.
闭包保存的是定义它的那个函数内部的局部变量丶参数和其他内部函数,也就是说保存的是这个函数执行上下文中的整个VO,而不是一个变量。上面代码中的函数作用域链中都保存着fn的活动对象,他们引用的都是一个i,当fn返回时,i的值是10,所以每个函数都引用保存i那个变量的同一个变量。我们如果想得到原先想得到的那个结果,可以加上另一个匿名函数改变他的父作用域(其实应该是创建它的作用域),将它包裹起来。
function fn(){ var array=[]; for(var i=0;i<10;i++){ array[i]=function(num){ return function(){ return num; }; }(i); } return array; }
这个匿名函数有一个参数num,同时是返回值。在调用每个匿名函数时,传入了变量i。由于参数是按值传递的,所以i就会复制给num,而这个匿名函数的内部又创建了一个访问num的闭包,返回后能够访问到该匿名函数中的VO(包括参数),于是每个函数返回的都是num的一个副本,所以可以得到不同的值。
其实,说了这么多,我们只要熟悉闭包的两个应用场景,就能比较好的理解闭包的意义。
一.作为函数的返回值.。作为函数返回值被执行后仍然可以访问定义它的那个函数环境的VO。
function f(){ var a=1; return function(){ console.log(a); } } var g=f(); g();//1;
二.作为一个函数的参数。作为函数返回值被当做另一个函数的参数传入时,仍然是访问定义它的那个函数环境的VO
function f(){ var a=1; return function(){ console.log(a); } } var g=f(); g();//1; function F(fn){ var a=2; fn(); } F(g);//1
上面两个小例子也正好说明了闭包可以访问定义它的那个函数作用域下的内部变量和内部函数。其实是整个VO,所以还包含参数。
关于this对象
我们知道,this对象是在运行时基于函数的执行环境绑定的,全局函数中,this=window;
当某个函数被作为某个对象的方法调用时,this=对象
匿名函数的执行环境具有全局性,因此其this对象通常指向window
所以在闭包中使用this对象或arguments对象,必须将该对象的引用保存到闭包能够访问到的另一个变量中。