函数作用域链
来源:互联网 发布:河南安全教育网络平台 编辑:程序博客网 时间:2024/06/05 06:43
函数作用域:
函数作用域可以分为两个内容来理解:
1.函数作用域的基础概念:函数内定义的变量,不能在函数之外的任何地方访问,而这个函数可以访问其范围内的任意变量和函数,以及其父函数有权访问的任何其他变量。
2.特性:函数作用域内,依旧遵从变量提升(包括函数提升)和顺序执行。
PS:变量提升是块级作用域和函数作用域的主要区别。
第一点,很容易理解,就是函数不仅可以访问自己内部定义的变量,还可以访问外部函数中的变量,以及外部函数的外部函数的变量....比如这样
//如果你不喜欢用控制台,可以删了下面这行代码 // 并把后面的log改为alert var log = console.log.bind(this); var x = 4; function add() { var i = 1; log(i+x); } add();
第二点,就是顺序执行和变量提升(了解的可以直接跳过)
顺序执行:从上到下,一条一条的执行
变量提升:函数体内变量声明和函数声明,会被提升到函数体的顶部,所以你可以在变量声明之前访问数据,函数声明之前调用函数。这样,表面上看起来违背了顺序执行,其实并不是的
var log = console.log.bind(this); //函数声明提升 add(); //add function add() { log("add"); } //函数声明提升的效果是这样 function add(){ log("add") } add();
如果只是简单的顺序执行,应该会抛出异常(add is not defined),其实,Js是在函数提升之后,才顺序执行的,一切正常,变量提升,又有些不同
var log = console.log.bind(this); //变量提升 log(i); //undefined var i = 3; log(i); //3 //变量提升的效果是这样 var i; log(i); i = 3; log(i);
同样的,简单的顺序执行,在第一个alert(i)处,会抛出异常(i is not defined),不过,Js在提升变量后的,但是变量的提升并不会将它的赋值过程也提升,赋值是在顺序执行中完成的,所以你在赋值操作之前访问i,它只能拿出一个undefined来敷衍你了,所以一般建议将变量声明和赋值操作,全部写在函数体的顶部,防治出现undefined。
总的来说呢,就是先提升,后顺序执行。
那么,还有个问题就是
var func = function(){ alert("我是谁?") }这个函数表达式是个什么提升?你可以自己试一下。
那么函数作用域是什么,就是把2的特性,用到1里面(函数体内),下面是一个简单的测试:
var log = console.log.bind(this); var x = "global x"; function add() { log(x); //1 var x = "local x"; log(x); //2 } add();
那么,1会输出什么,2会输出什么?不要忘记特性中的提升!!!在全局作用域内,你会用,在函数体内,你也会用了吧。
1输出:undefined
2输出:local x
总结两句,刚开始我一直会理解错,就是因为,会自然而然的用到从上到下的顺序执行,上面1的结果就变成了global x,但JS拥有函数作用域,它有变量提升,那么,只要你在变量提升之后,再顺序执行,就不会错了。当然,eval()是不会有变量提升,这里不做深入讨论。
还有一点,就是if语句和for循环语句,使用了C语言风格的语法把多条语句组合到一个代码块中,而Js没有块级作用域(在块级作用域中,变量在离开定义的块级代码后被立即回收),所以,zhe语句代码块中的数据,在整个函数内部可以被访问。
function f() { alert("1. i:"+i+" a:"+a); for(var i = 0; i<3; i++){ var a = 2; } alert("2. i:"+i+" a:"+a); } f();两次输出结果就是:
1. i:undefined a:undefined
2. i:3 a:2
好像没什么多余的要说的,let语句以后再说吧。
函数作用域链:
执行环境:一个函数执行时的环境。定义了变量或者函数有权访问的其他数据
变量对象:环境中定义的所有变量和函数都保存在这个对象中(我们无法访问变量对象,但后台处理数据时会用到它)
活动对象[[scope]]:如果环境是一个函数,那就将活动对象作为变量对象,活动对象中还包含arguments对象
作用域链:当代码在一个环境中执行时,会创建变量对象的一个作用域链
三者的的关系就是,函数执行时,就产生执行环境,执行环境中的所有函数、变量成为变量对象中的属性,而这个变量对象+外部执行环境中的变量对象+外外部的...+全局对象,就成了这个函数的作用域链(就是一个变量对象的有序集合)。
尽力去理解上面几个概念,不理解也没关系,毕竟概念和例子一起食用,见效最快。
var x = 1; function func(i){ var y = 2; alert(x); //undefined var x = i; function add() { alert(x+y); //6 } add(); } func(4);
如果你不是很清楚作用域链的话,可以分三步来分析
第一步,它是如何“链”的
先说func函数,我们可以轻易地找到它的所有外部函数,那就是func()→全局,而作用域链也是如此,当func函数被调用时,作用域链就是func(4)活动对象→全局变量对象。第二步,将什么东西“链”起来了
上面也说了,其实链起来的是变量对象(活动对象和变量对象的区别,见上面的概念),那活动对象中,有什么内容呢?内容如下:函数内部定义的局部变量以及传入的参数和arguments对象都会作为活动对象的属性。活动对象本身并不是一个对象,更像一个对象列表。声明一点,活动对象,是存在有JS引擎中的,我们并不能够查看它,所以你才会头疼。对于上面这个例子,func(4)的作用域链:
看吧,就是简单的把变量对象“链”了起来。前面的第一步,是为了在你不熟悉的时候,快速找到作用域链的一个野路子,正确的路子,应该是用执行环境来描述的,像这样:对于func(),我们可以轻易的找到它自己的执行环境以及包含它的父执行环境,那就是func()执行环境→全局环境。基本上,两者是一一对应的,只是概念上的差距比较大。
第三步,作用域链的用途
作用域链保证了执行环境有权访问的所有变量和函数的有序访问。这句话,关键词就是“有权”和“有序”,有权就是子执行环境可以访问父执行环境(访问活动对象中的属性),但父环境不能访问子环境中的任何变量和函数。有序,向上搜索,是为了处理同名变量的问题。比如func(4)执行到alert(x)时,两个活动对象中都有x,向上搜索,就是从自己的活动对象开始搜索,搜索到了直接弹出,搜索不到,就搜索父执行环境,一次次向上。
大概就是这么个步骤,例子是比较简单的,现在可以看一下add()的作用域链:
因为add函数内部,没有局部变量,也没有传入的参数,更没有局部函数,所以变量对象只有一个系统指定的对象arguments。在alert(x+y)是,要引用变量x和y,然后就顺着作用域链往上走搜索,在func(4)的活动对象中找到了变量x=4和y=2,不再继续往上搜索,最后弹出6。
还有一点要注意的是,在函数内部直接为变量赋值,不进行声明,将作为全局变量对象中的属性,如下:
function f() { x = 4; } alert(x); //error f(); alert(x); //4就是这个结果了,alert(x)是在全局环境中的,无权访问f函数内部的数据,第二次弹出了4,所以说,x在f函数执行之后,作为全局变量对象的属性。
总结下,刚开始,我不懂作用域链的时候也觉得挺难的,后来查了一些书和博客,以为差不多懂了,然后就写了这篇博客,前后删改了3、4次了,拖了一周,发现自己还是有不太懂的地方,又是漫漫搜索路,最后终于写完了,删改的过程也是蛮痛苦的,好几次不想写了,都想直接把博客全删了。关于为什么写博客:为什么呢应该写博客|刘未鹏
- 函数作用域链
- 函数作用域和作用域链
- 函数作用域和作用域链
- JavaScript 变量作用域、函数作用域、作用域链
- js作用域(函数作用域、变量作用域、作用域链、with语句)
- JavaScript的函数作用域与作用域链详解
- js函数3-作用域与作用域链
- JavaScript中的函数作用域和作用域链
- JavaScript作用域和作用域链/变量,函数提升
- 轻松搞定--作用域,变量、函数提升,作用域链
- JavaScript:函数的作用域链
- JavaScript中函数的作用域链
- 函数表达式-闭包,作用域链
- 闭包和函数作用域链
- 嵌套的函数(作用域链)
- 构造函数/原型/作用域链
- 函数定义作用域
- 函数的作用域
- 洛谷 P1944 最长括号匹配_NOI导刊2009提高(1)
- jsp九大类值对象四大作用域
- Spring与Hibernate整合(配置模式)
- HDU
- 树梅派 内存交换空间swap 更改
- 函数作用域链
- 数据结构与算法:图
- POJ 1696 Space Ant(凸包变形)
- Call to undefined function imagecreatefromjpeg()
- MOOC清华《面向对象程序设计》第7章:负载监视器的设计v2.0(采用基于模板的策略模式)
- zencart 批量删除无图片产品 优化网站
- Json 接收问题
- 用jstl和el表达式获取List<Map<String,Object>>中的值
- Linux