JS作用域详解
来源:互联网 发布:webgl高级编程pdf下载 编辑:程序博客网 时间:2024/06/15 10:35
参考文章书籍:
JavaScript高级程序设计-第四章
深入了解JavaScript,从作用域链开始(1)
JS作用域面试题总结
索引:
- 一涉及概念知识点
- 执行环境
- 作用域
- 全局作用域
- 局部作用域函数作用域
- 作用域链
- 作用域链的用途
- 作用域链搜索方法
- 作用域链访问不可逆
- JavaScript没有块级作用域
- 二作用域习题测试
- 纯作用域作用域链类
- 作用域变量函数提升类
- 三延长扩展作用域
- 延长作用域链
- 扩展作用域
- PS
- 一涉及概念知识点
一、涉及概念、知识点
1.执行环境
- 执行环境定义了变量或者函数有权访问的其他数据,执行环境有与之相关联的变量对象。
执行环境会在环境内所有代码执行完毕后,销毁该环境。(全局执行环境会等到应用程序退出或者浏览器窗口关闭才会销毁)
- 全局执行环境
全局执行环境也即window对象,因此所有的全局变量、函数都是window对象的属性和方法。 - 局部执行环境(函数执行环境)
当执行流进入一个函数时,执行环境变为这一特定函数的局部执行环境。待函数执行完后,栈将环境弹出,转而进入下一个执行环境。
- 全局执行环境
正由于不同执行环境间的切换,因此产生了变量和函数的作用域
2.作用域
- 作用域代表变量与函数的可访问范围,可以说作用域控制着变量与函数的可见性和生命周期。
- 在JavaScript中,变量的作用域有全局作用域和局部作用域两种,局部作用域又称为函数作用域。
全局作用域
拥有全局作用域的对象:
1.程序最外层定义的函数或者变量
var a = "tsrot";function hello(){ alert(a);}function sayHello(){ hello();}alert(a); //能访问到tsrothello(); //能访问到tsrotsayHello(); //能访问到hello函数,然后也能访问到tsrot
2.所有末定义直接赋值的变量(不推荐)
function hello(){ a = "tsrot"; var b = "hello tsrot";}alert(a); //能访问到tsrotalert(b); //error 不能访问
3.所有window对象的属性和方法
如window.name、window.location、window.top等等。
局部作用域(函数作用域)
局部作用域在函数内创建,在函数内可访问,函数外不可访问。
function hello(){ var a = "tsrot"; alert(a);}hello(); //函数内可访问到tsrotalert(a); //error not defined
3.作用域链
▷作用域链的用途:
保证该执行环境下有权访问的所有变量、函数的有序访问!
▷作用域链搜索方法:
目标标识符的解析是从执行环境的最前端开始,沿着作用域链一级一级向后回溯,直到找到标识符为止。
var color = "blue";function changeColor(){ if(color=="blue"){ color = "red"; } else{ color = "blue"; } }changeColor();console.log(color); //red
以上代码解释了标识符color
的搜索过程。在调用函数changeColor()
后,执行到if
判定时需要用到变量color
。于是在当前执行环境下先于函数内部作用域搜索变量color
;未找到后向外层检索,此时访问到了全局变量color="blue"
,if
判定符合条件,执行颜色修改为red
。
▷作用域链访问不可逆。
整个搜索访问的过程中,可以通过作用域链从内部环境访问外部环境,但不可逆转,是线性有向的过程。
var color = "blue";function changeColor(){ var anotherColor = "red"; function swapColor(){ var tempColor = anotherColor; anotherColor = color; color = tempColor; //这里可以访问color、anotherColor和tempColor } //这里可以访问color和anotherColor}//这里只能访问colorchangeColor();
以上代码中,全局环境
、changgeColor()
和swapColor()
为三个不同的执行环境,可以由内层向外层搜索访问,但不可向内层访问。
★JavaScript没有块级作用域
JavaScript与其它语言不同的一点是,没有块级作用域。
块级作用域:由花括号封闭的代码块都有自己的作封闭执行环境(作用域)。JavaScript中只有函数具有块级作用域。
其它if、for、while等
具有花括号的语句没有块级作用域,也即语句执行结束后,内部变量、函数仍可以在外层执行环境中访问到!
for (var i;i<10;i++) { ------for示例 dosomething(i);}alert(i); //10//依旧可以访问iif(true){ ------if示例 var color = "blue";}alert(color); //"blue"//同样可以访问到colorfunction add(a,b){ ------function示例 var sum = a+b; return sum;}var result = add(10,20);alert(sum); //error:sum is not defined
二、作用域习题测试
1.纯作用域、作用域链类
1.
var x = 10;function foo() { var y = 20; function bar() { var z = 30; console.log(x + y + z); }; bar()};foo();
代码的输出结果为”60″。**函数bar可以直接访问”z”,然后又通过作用域链访问上层
的”x”和”y”。**
2.
var y = 'global'; function test(x){ if(x){ var y ='local'; } return y; } console.log(test(true)); //local
在当前作用域(test()
函数)内,可以找到目标标识符y
,因此不需要向上访问全局变量y=“global”
。
3.
var y = 'global'; function test(x){ (function(){ if(x){ var y = 'local'; } })(); return y; } console.log(test(true)); //global
在当前作用域(test()
函数)内,找不到标识符y
,因此按照向上搜索的规则,沿着作用域链访问全局变量y=“global”
。
★不能向内层访问自执行函数中的y
4.函数嵌套类
var a=10; function aaa(){ alert(a);}; function bbb(){var a=20;aaa();}bbb(); //10
因为bbb()
访问不了内层作用域的变量a
,因此向上访问全局变量a = 10
.
再进行稍微修改:
var a = 10;function aaa() { //step-4 var a=30; alert(a); //step-5->执行alert,此时只能找到外面的a=10故弹框10}; function bbb() { //step-2 var a = 20; aaa(); //step-3}//定义了函数没啥用,调用才是真格的所以这里是step-1bbb(); //30 //step-1
★5. a=b=10类特殊情况
function aaa(){ var a=b=10; } aaa(); alert(a);//结果为,无法访问到 alert(b);//结果为10;
var a=b=10;
可以解析成 b=10;var a=b;
也就是b为全局变量,a为局部变量,所以外部访问a访问不到,访问b结果为10;
2.作用域+变量(函数)提升类
1.
var y = 'global'; function test(x){ console.log(y); //undefined if(x){ var y = 'local'; } return y; } console.log(test(true)); //local
这里涉及到JavaScript中的变量提升,JavaScript中会自动把变量生命的语句提升到当前作用域的最前方 。
以上代码可以这样来理解
var y = 'global'; function test(x){ var y; //声明提前了 console.log(y); if(x){ y = 'local'; //赋值仍留着原地 } return y; } console.log(test(true));
当test函数中打印y时,变量y只是被声明了,并没有赋值,所以先打印出了undefined;
当程序继续向下执行,则输出local
2.
var y = 'global'; function test(x){ console.log(y); //undefined var y = 'local'; return y; } console.log(test(true)); //local
3.
var a = 1; function b(){ a = 10; console.log(a); //10 return; var a = 100; } b(); console.log(a); // 1
变量a
先是全局声明,在调用b()
函数时,在内部改变其值为10
.在执行完毕后,退出函数环境,因此a的值又变回1
。
4.
var a = 100; function testResult(){ var b = 2 * a; var a = 200; var c = a / 2; alert(b); alert(c); } testResult() //NaN 100
同样是基于变量声明提前,原理参考第1题过程。局部变量a在函数中声明提前到第一行,值为undefined
。因此b值为NaN
,在a赋值后,c值为100。
5.
var getName = function(){ console.log(2);}function getName (){ console.log(1);}getName();
这个例子涉及到了变量声明提升和函数声明提升。
上例等同于:
var getName; //变量声明提升function getName(){ //函数声明提升到顶部 console.log(1);}getName = function(){ //变量赋值依然保留在原来的位置 console.log(2);}getName(); // 最终输出:2
由于变量和函数声明均提升到顶部,因此后面getName又被函数表达式的赋值操作给覆盖了,所以输出2
三、延长、扩展作用域
1.延长作用域链
实现原理:在作用域链的前端增加一个变量对象,当执行流进入下列语句时,作用域链就得到加长:
-try-catch
语句的catch
块
-with
语句
1.对于with
语句来说,会将指定的对象添加到作用域链中。
2.对于catch
语句来说,会创建一个新的变量对象。
例:
function alterUrl() { var qs = "?debug=true"; with(location) { var url = href + qs; } return url;}
with语句接收location
对象,等同于with语句的作用域链扩展添加了location对象作用域部分。
因此,在with语句中可以调用所有的location对象的属性和方法。此时的href默认将获取浏览器的href,无需赋值。
2.扩展作用域
扩展作用域的方法常用有两种:
- call()
方法
- apply()
方法
PS:
call
和apply
方法是函数本身具有的非继承的方法,不仅可以传递参数,还可以扩充函数运行的作用域。
★apply()
方法能劫持另外一个对象的方法,继承另外一个对象的属性.
Function.apply(obj,args)`方法接收两个参数:
obj:这个对象将代替Function类里this对象
args:这个是数组,可以是Array,也可以是arguments对象。总之它将作为参数传给 Function(args–>arguments)
★ call:
和apply的意思一样,只不过是将参数数组改为了参数列表.
Function.call(obj,[param1[,param2[,…[,paramN]]]])
obj:这个对象将代替Function类里this对象.
params:这个是一个参数列表.
示例:
1.call()
:
(1)不传递参数,只改变this
指向,以扩充作用域。
window.color = 'red'; document.color = 'yellow'; var s1 = {color: 'blue' }; function changeColor(){ console.log(this.color); } changeColor.call(); //red (默认传递参数为window对象参数) changeColor.call(window); //red changeColor.call(document); //yellow changeColor.call(this); //red changeColor.call(s1); //blue
★(2)call的参数列表必须一一对应,否则将会出现赋值交叉。
function food(name,price){ this.name = name; this.price = price;}function fruits(name,price,weight){ food.call(this,name,price,weight); this.weight = weight;}var result = new fruits("pingguo",5,2)alert(result.name+"--"+result.price+"--"+result.weight); //pingguo--5--2
以上代码参数列表与food的变量顺序是保持一致。如果将参数列表顺序打乱(或者food函数参数顺序打乱),就会出现赋值交叉的情况,如:
function food(name,price){ this.name = name; this.price = price;}function fruits(name,price,weight){ food.call(this,price,name,weight); this.weight = weight;}var result = new fruits("pingguo",5,2)alert(result.name+"--"+result.price+"--"+result.weight); //5--pingguo--2
2.apply()
:
(1)不传递参数,只改变this
指向,扩展作用域
window.number = 'one';document.number = 'two';var s1 = {number: 'three' };function changeColor(){ console.log(this.number);}changeColor.apply(); //one (默认传参为window对象下参数)changeColor.apply(window); //onechangeColor.apply(document); //twochangeColor.apply(this); //onechangeColor.apply(s1); //three
(2)函数间作用域扩展,实现方法调用。
function Person(name,age){ //定义一个人类 this.name = name; this.age = age;}function Student(name,age,grade){ //定义一个学生类 Person.apply(this,arguments); //扩展Student的作用域到Person。 //或者Person.call(this,name,age); 也可实现作用域扩展! this.grade = grade;}var student1 = new Student("xiaowang",21,90);alert(student1.name+student1.age+student1.grade); //xiaowang2190
- JS作用域详解
- js变量以及其作用域详解
- js变量以及其作用域详解
- js变量以及其作用域详解
- js变量以及其作用域详解
- js变量以及其作用域详解
- js变量以及其作用域详解
- js变量以及其作用域详解
- js变量以及其作用域详解
- JS之执行环境,作用域详解
- js变量以及其作用域详解
- js变量、作用域及内存详解
- js变量以及其作用域详解
- 详解JS作用域和this关键字
- js变量以及其作用域详解
- 详解js变量、作用域及内存
- js变量以及其作用域详解
- JS变量以及其作用域详解
- CentOS7连接WiFi
- JavaScript 变量命名规范(个人)
- 周中训练笔记13
- NYOJ 12 喷水装置(二)
- request,response,session,application四者之间的作用域联系以及各自的运行机制
- JS作用域详解
- Shiro使用步骤
- easyui combox数据的动态绑定
- ItelliJ IDEA开发工具使用—创建一个web项目
- Marriage Ceremonies--Light OJ 1011
- git 学习笔记及常用命令
- 关于获取本地系统时间问题
- 常微分实验(1.1) 变量分离方程与变量变换
- Java中ArrayList、Vector与LinkedList有什么区别