LV之-----自由变量和作用域链(基石4)

23 篇文章 0 订阅
13 篇文章 0 订阅

自由变量和作用域

一、学习目标
1.自由变量
2.作用域及作用域链
3.作用域和执行环境的区别
二、自由变量
如果在当前作用域使用了一个变量,但这个变量却没在该作用域声明,那么对于当前作用域而言这个变量就是自由变量,如下代码中的a就是一个自由变量,因为在函数f内部使用了变量a,但变量a是在外层作用域内声明的。

	var a = 100;
	function() f{
		console.log(a)
	}
f();

三、作用域和作用域链
(一)什么是作用域
所谓作用域就是指变量的适用范围,javascript除了全局作用域之外,只有函数可以创建作用域。
作用域是个抽象概念,类似于“地盘”,全局作用域就是script标签之间的部分占据的“地盘”,函数作用域就是函数花括号之间部分占据的“地盘”,Js中没有块级作用域。

变量a,b和函数fn位于全局作用域内,变量c和函数f位于函数fun作用域范围内,变量d位于函数f作用域范围内。由于作用域的作用是划分变量的适用范围,所以在函数f作用域范围内能访问到所有变量和函数,在函数fn作用域范围内能访问到变量a,b和函数fn以及fn内部的变量c。
全局作用域内只能访问到变量a,b以及函数fn。
(二)函数变量的作用域
JS的变量可从作用域角度分为全局变量和局部变量。
作用域:就是变量声明的区域,就是变量和函数的可访问范围。在全局声明的变量为全局可见可访问的就是全局变量,如果在函数内部声明的变量只能在函数内部访问

函数的参数只能在函数内部访问,是局部变量

JavaScript没有块级作用域,只有函数作用域和全局作用域。for循环内部定义的变量是函数级别的作用域。
变量没有在函数内声明或者声明的时候没有带var就是全局变量,拥有全局作用域。特殊:var a = b = c = 0; b与c是全局变量。
全局作用域的变量可以在js中任何地方调用,函数作用域的变量只能在自己函数内部调用,包括自己内部定义的其他函数都可以直接调用。
变量的作用域是静态的,以它声明时的为准,因为变量的作用域在JS代码的解释阶段就已经完成规则的制定。

1.案例

<script>
var t = 9; // 全局作用域,全部都可以访问
function f1() { // f1 函数全局作用域
var t2 = 10; // t2 是f1函数内部可访问
console.log(t);
function f2() { // f2函数式 f1函数的作用域
var t3 = 200; // t3 只能在f2函数内部访问
console.log(t2);
return t2 * t2; // f2函数可以访问f1函数的作用域的变量及 f2自己内部的变量。
}
return f2();
}
var m = f1(); 
console.log(m);
</script>

(三)Js没有块级作用域
(四)作用域的作用
隔离变量,不同作用域下不同变量名不会有冲突

(五)变量提升(hositing)
如果一个声明的变量在函数体内,那么它的作用域就是函数内部。如果是在全局环境下声明的,那么它的作用域就是全局的。通过var声明的变量是无法用delete删除的。
函数内部的声明的变量会被提升到函数的头部。函数在解析执行的时候,先进行变量声明处理,然后再运行函数内部的代码。
变量和赋值语句一起书写,在js引擎解析时,会将其拆成声明和赋值2部分,声明置顶,赋值保留在原来位置
变量重复声明不会出错,后面的会覆盖前面的。
解读代码:

var a = 1, b = function a (x) { return x && a (--x); }; alert (a);  // a (--x)自己调用自己  递归 在函数内部有用,外部a不是函数 

function a (x) { return x * 2; } var a; alert (a);

function b (x, y, a) { arguments[2] = 10; alert (a); } b(1, 2, 3);

function a () { alert (this); } a.call (null);

案例1:

var a = 18;
function d() {
  console.log(a);
  var a = { age: 19};
  console.log(a);
}
d();  //  输出?
console.log(a);

案例2:

if (!("a" in window)) { 
	var a = 1;
 } 
 console.log(a);

案例3:

console.log(a);
var a = 20;
console.log(a);
function a() {
}

案例4:

f();
console.log(a);
console.log(b);
console.log(c);
function f() {
var a = b = c = 9;
console.log(a);
console.log(b);
console.log(c);
}

案例5:

f();
function f() {
for(var k = 0; k <10; k++) {
	console.log(k);
}
console.log(k);
}

(六)函数提升

只有声明式函数才会被提升,字面量函数不会被提升
总结:
1)函数声明会置顶
2)变量声明也会置顶
3)函数声明比变量声明更置顶:(函数在变量上面)
4)变量和赋值语句一起书写,在js引擎解析时,会将其拆成声明和赋值2部分,声明置顶,赋值保留在原来位置
5)声明过的变量不会重复声明
6)遗漏声明的变量是全局变量

(七)什么是作用域链
多个上下级关系的作用域形成的链条,他的方向是从下向上(从内到外)

1.作用域链是一个数组
2.作用域链是控制变量作用域的有序访问的js内部实现。
3.作用域链存储在函数的执行上下文中,作用域链中存放的是执行环境中的vo或者ao。
4.当前函数的作用域对象都是在最前端,而且全局的在最末端
5.变量(标识符)的搜索都是从作用域链的最前端向后搜索,直到全局作用域 ,标识符的解析是沿着作用域链一级一级搜索的过程,从第一个对象开始,逐级向后回溯,直到找到同名标识符为止,找到后不再继续遍历,找不到就报错。

由于自由变量的存在,在js代码执行的时候,我们有可能要跨作用域取值。取值的秘诀是要到创建这个函数的那个作用域中取值而不是调用这儿函数的作用域内去取值。
1.案例1

var x = 10;
	function fn(){
		console.log(x);
	}
	function show(fn){
		var x = 20;
		fn()
	}
	show(fn)

分析代码的执行流:

//全局代码执行
x = 10;
show(fn)
	// show 执行环境
		var x;
	// show 代码执行
	    x = 20;	
	    fn()
	    	// fn执行环境
	    	// fn代码执行
	    	 	console.log(x) //fn是在全局作用域声明的,找全局范围内的x x=10

因此以上代码打印的结果是10,而不是20。
上面描述的只是跨一步作用域去寻找。如果跨了一步,还没找到呢?——接着跨!——一直跨到全局作用域为止。要是在全局作用域中都没有找到,那就是真的没有了。这个一步一步“跨”的路线,我们称之为——作用域链。标准一点的定义就是:作用域链是javascript内部中一种变量、函数查找机制,它决定了变量和函数的作用范围。
我们拿文字总结一下取自由变量时的这个“作用域链”过程:(假设x是自由量)
第一步,先在当前作用域查找x,如果有则获取并结束。如果没有则继续;
第二步,如果当前作用域是全局作用域,则证明x未定义,结束;否则继续;
第三步,(不是全局作用域,那就是函数作用域)将创建该函数的作用域作为当前作用域;
第四步,跳转到第一步。

(1)console.trace()打印函数调用的栈信息
function a(argument) {
 function c(argument) {
    d()
 }
 function d() {
   b()
 }
 return c
}
    
function  b(argument) {
  console.trace()
}
a()()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值