JavaScript学习笔记(八)——作用域
2022/6/16 1:21:20
本文主要是介绍JavaScript学习笔记(八)——作用域,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
作用域
1.概述
作用域:标识符(变量和函数名)在哪些地方(函数的代码块内部和外部)能够被访问,哪些地方就是这个标识符的作用。
规则:函数内部的代码可以访问函数内部的标识符,也可以访问函数外部的标识符,但是反过来不行,也就是说外部不能访问函数内部的标识符。
//案例: var a = 200; function fn(a) { //var a=90//210 function fm() { console.log(a, 122)//210 a = 90 console.log(a, 123)//90 } fm(a)//fm(210) console.log(a, 124)//90 } fn(a+10)//fn(210) console.log(a, 125)//200
//案例 var total=0; function increment () { var total=0 total=total+2 } function decrease() { total=total-2 } increment() increment() decrease() console.log(total)//0
//案例: 函数的调用是运行一次代码:每一次调用都会重新执行所有代码 var total = 0; function increment() { // var total = 0 total = total + 2 console.log(total) } increment()//{var total=0; total = total + 2} increment()//{var total=0; total = total + 2}
2.同名标识符提升问题
总结:每一个作用域在运行时,js引擎会先把作用域内部的关键字隐式提前扫描 并声明 。
var a = 10 function fn() { //隐式操作:把var 修饰的变量名提前声明 console.log(a)//声明了却没有赋值的变量undefined a = 40//给隐式声明的变量赋初始化值 var a = 20//更新a的值 40==>20 console.log(a)//20 } console.log(a)//10 fn()// console.log(a)//10
-
变量函数同名时 变量然后函数
console.log(a) var a=20 function a () { console.log(100) } console.log(a) /* 1. var a function a () { console.log(100) } console.log(a) a=20 console.log(a) */
-
变量变量同名时
console.log(a)//unf var a=10; console.log(a)//10 var a=20; console.log(a)//20 /* 1.var a;var a; 2.console.log(a)//undef 3.a=10; 4.console.log(a)//10 a=20; console.log(a)//20 */
-
函数和函数同名时
function fn () { console.log(111) } function fn () { console.log(2222) } fn()
-
同名标识符提升顺序问题:记住四个字
法则:形(形参和变量)实函运
标识符有三种写法: var function function(a){}
var a=20 function fn(a){ console.log(a,1)//函数 a=90 console.log(a,2) var a=100 console.log(a,3) function a () { console.log(6666) } console.log(a,4) } fn(a) /* var a; a=20 function a () { console.log(6666) } //打印函数 a=90 console.log(a,2)//打印90 a=100 console.log(a,3)//100 console.log(a,4) //100 */
这个流程就是:
1.先隐式提升当前作用域内部的所有形参变量和局部变量 (只是声明提升,不提升赋值)
2.再把实参赋值给形参变量
3.然后执行函数的隐式提前声明
4.再按照代码顺序运行代码
3.函数运行时的作用域
函数运行时 ,是在写函数代码的地方运行代码 , 不是在调用代码的地方运行代码。
function fn () { var a=90 function fm () { console.log(a) } return fm//a//200 } var a=100 var re=fn() // console.log(re) re()//console.log(a)
function fn(a) { function fm() { a = a + 1 console.log(a) } return fm } var f1=fn(10) f1() f1() /* { var a=12 return fm { a = a + 1 console.log(a)//11 } { a = a + 1 console.log(a)//12 } } f1()==>fm() */
function fn(a) { function fm() { a = a + 1 console.log(a) } return fm } var f1=fn(10) f1() var f2=fn(10) f2() /* { var a=10 { a = a + 1 console.log(a)//11 } } { var a=10 { a = a + 1 console.log(a)//11 } } */
4.js函数预编译
什么是预编译?
函数运行时,代码的运行步骤就是函数预编译。
js完成解释执行分为三个步骤:
1.语法分析;
2.预编译(全局预编译、函数预编译);记住四个字:形(形参和变量)实函运;
3.执行语句。
深度理解一下上面的第二步 预编译:
函数每调用一次,会生成一个新的对象,自调用且无终止条件的话生产无数个对象,浏览器就会瘫痪。
-
给这个AO对象添加成员,函数内部的局部变量和形参变量 名 作为AO对象的属性名。形参名和实参名冲突时,并不影响。
-
把传入的实参赋值给AO对象的属性;
-
把局部函数的名字 让AO对象也有相同的成员名,把函数赋值给这个属性;
-
运行代码。
全局作用域运行代码时,也有预编译。
-
生产一个对象 Global Object(GO);
-
把所有的全局变量,设置为GO的属性名;
-
把所有函数名作为GO的程序名,把函数名赋值给这个成员;
-
看是不是浏览器环境中的js脚本,如果是浏览器,还会执行异步,GO给window对象共享成员;
-
运行代码。
全局预编译还有一步: 不同的环境中运行js代码不一样,GO的成员全部浅拷贝给环境对象window。node.js中没有这一步。
拓展知识:
关于访问成员,console.log(a)访问的时GO对象的成员。
console.log(window.a);原型链中没找到的话不报错,浏览器返回undifined;但是作用域链中GO中找不到的话就会报错。
5.作用域链
执行期上下文:当函数执行时,会创建一个称为执行期上下文的内部对象。一个执行期上下文定义了一个函数执行时的环境,函数每次执行时对应的执行上下文都是独一无二的,所以多次调用一个函数会导致创建多个执行上下文,当函数执行完毕,它所产生的执行上下文被 "销毁"。
js对象有两种成员:
-
一种是上文成员(js语法可以访问的成员);
-
一种是下文成员(底层语言,只能看不能运用)。
-
[[]] 括起来的成员名 就是下文成员。
-
例如:[[scope]]这个对象内部保存的就是函数的作用域。现在我们知道作用域就是一个对象,函数在定义或者申明的时候就有了作用域[[scope]]。里面保存了上文的AO对象。
function fn(n){ var a=20; function fm(){ } } fn(100);//AO{n:undifined==> 100, a:undif==>20,function fm(){}} fn();//AO{n:undif,a:undif ==>20,function fm(){}}
function fn(n){ var a=20; function fm(){ } } fn(100); fn(200);
函数生成了就会有个属性[[scope]]作用域“数组”(只能引擎使用)
函数调用时生成AO对象 , 会把AO对象放在scope
每次调用都会放在scopes前面(顶部)
每个函数scopes数组中天生就有一个AO对象 就是这个函数的上层的AO
function fn(a){ var a2=100; function fm(b){ var b2=200; } fm(20); } fn(10);
分析上面这段代码的作用域链
如下:
/* Go:{fn函数创建了 放在Go对象内部} fn.[scope]=[ Go:{fn函数创建了 放在Go对象内部} ] fn(10)=> fn.[scope]=[ AO-fn(10):{ a:10, a2:100, fm创建了 }, Go:{fn函数创建了 放在Go对象内部} ] fm.[scope]=[ AO-fn(10):{ a:10, a2:100, fm创建了 }, Go:{fn函数创建了 放在Go对象内部} ] fm(20) fm.[scope]=[ AO-fn(10)-fm(20):{ b:20, b2:200 console.log(a,a2,b,b2,c) }, AO-fn(10):{ a:10, a2:100, fm创建了 }, Go:{fn函数创建了 放在Go对象内部,c} ] fm(90) fm.[scope]=[ AO-fn(10)-fm(90):{ b:90, b2:200 console.log(a,a2,b,b2,c) }, AO-fn(10):{ a:10, a2:100, fm创建了 }, Go:{fn函数创建了 放在Go对象内部} ] */
难题分析:
function fun(n, o) { console.log(o); return { fun: function (m) { return fun(m, n); } }; } var a = fun(0); a.fun(1); a.fun(2); a.fun(3); var b = fun(0).fun(1).fun(2).fun(3); var c = fun(0).fun(1); c.fun(2); c.fun(3);js
结果:
分析:
/* Go:{fun函数创建了,a:und,b:und,c:und} fun.[scope]=[ Go:{fun函数创建了,a:und,b:und,c:und} ] //a = fun(0); fun.[scope]=[ AO-fun(0):{ n:0, o:und return {fun:function(m) {return fun(m, n)}} }, Go:{fun函数创建了,a:und,b:und,c:und} ] a.fun.[scope]=[ AO-fun(0):{ n:0, o:und //第一次打印undefined return {fun:function(m) {return fun(m, n)}} }, Go:{fun函数创建了,a:und,b:und,c:und} ] //a.fun(1); a.fun.[scope]=[ AO-fun(0)-a.fun(1):{ m:1 return fun(1, 0) }, AO-fun(0):{ n:0, o:und, return {fun:function(m) {return fun(m, n)}} }, Go:{fun函数创建了,a:und,b:und,c:und} ] fun.[scope]=[ AO-fun(1, 0):{ n:1, o:0, //第二次打印0 return {fun: function(m) {return fun(m, n)}} }, Go:{fun函数创建了,a:und,b:und,c:und} ] //a.fun(2) a.fun.[scope]=[ AO-fun(0)-a.fun(2):{ m:2, return fun(2, 0); }, AO-fun(0):{ n:0, o:und return {fun:function(m) {return fun(m, n)}} }, Go:{fun函数创建了,a:und,b:und,c:und} ] fun(2, 0); fun.[scope]=[ AO-fun(2,0):{ n:2, o:0 //第三次打印0 return {fun: function(m) {return fun(m, n)}}; }, Go:{fun函数创建了,a:und,b:und,c:und} ] a.fun(3)同理==>第四次打印0 */ /* Go:{fun函数创建了,a:und,b:und,c:und} fun.[scope]=[ Go:{fun函数创建了,a:und,b:und,c:und} ] //var b = fun(0).fun(1).fun(2).fun(3); ==> fun(0) fun.[scope]=[ AO-fun(0):{ n:0, o:und return {fun: function(m) {return fun(m, n)}} }, Go:{fun函数创建了,a:und,b:und,c:und} ] fun(0).fun.[scope]=[ AO-fun(0):{ n:0, o:und //第五次打印undefined return {fun: function(m) {return fun(m, n)}} }, Go:{fun函数创建了,a:und,b:und,c:und} ] fun(0).fun(1) fun(0).fun.[scope]=[ AO-fun(0)-fun(0).fun(1):{ m:1 return fun(1, 0); }, AO-fun(0):{ n:0, o:und return {fun: function(m) {return fun(m, n)}} }, Go:{fun函数创建了,a:und,b:und,c:und} ] fun(1, 0) fun.[scope]=[ AO-fun(1, 0):{ n:1, o:0, //第五次打印0 return {fun: function(m) {return fun(m, n)}}==>fun(0).fun(1) }, Go:{fun函数创建了,a:und,b:und,c:und} ] fun(0).fun(1).fun.[scope]=[ AO-fun(0)-fun(0).fun(1):{ m:1 return fun(1, 0); }, AO-fun(0):{ n:0, o:und return {fun: function(m) {return fun(m, n)}} }, Go:{fun函数创建了,a:und,b:und,c:und} ] fun(0).fun(1).fun.[scope]=[ AO-fun(0).fun(1):{ m:1 return fun(1, 0); }, AO-fun(0):{ n:0, o:und return {fun: function(m) {return fun(m, n)}} }, Go:{fun函数创建了,a:und,b:und,c:und} ] fun.[scope]=[ AO-fun(1,0):{ n:1 o:0 return {fun: function(m) {return fun(m, n)}} }, Go:{fun函数创建了,a:und,b:und,c:und} ] fun(0).fun(1).fun(2) [scope]=[ AO-fun(0).fun(1).fun(2):{ m:2, return fun(2,1) }, AO-fun(1,0):{ n:1 o:0 return {fun: function(m) {return fun(m, n)}} }, Go:{fun函数创建了,a:und,b:und,c:und} ] fun(2,1) AO:{ m:2 o:1 }
这篇关于JavaScript学习笔记(八)——作用域的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-12-28一步到位:购买适合 SEO 的域名全攻略
- 2024-12-27OpenFeign服务间调用学习入门
- 2024-12-27OpenFeign服务间调用学习入门
- 2024-12-27OpenFeign学习入门:轻松掌握微服务通信
- 2024-12-27OpenFeign学习入门:轻松掌握微服务间的HTTP请求
- 2024-12-27JDK17新特性学习入门:简洁教程带你轻松上手
- 2024-12-27JMeter传递token学习入门教程
- 2024-12-27JMeter压测学习入门指南
- 2024-12-27JWT单点登录学习入门指南
- 2024-12-27JWT单点登录原理学习入门