JavaScript提高篇之预解释作用域以及this原理及其应用
1.预解释
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>预解释1</title> 6 7 <script type="text/javascript"> 8 /*js中的数据类型:*/ 9 //1.基本数据类型:(按照值来进行操作的) 10 //number,string,Boolean,undefined,null 11 12 //2.引用数据类型:(是按照地址进行操作的) 13 //object:{} [] /^$/ Date 14 //function: 15 // 例1: 16 console.log(num);//num=undefined,为什么是未定义,因为声名了但是没有赋值,所以是未定义。 17 var num=12; 18 console.log(num);//定义了12;所以值是12; 19 var obj={name:"lizebo",age:23}; 20 fn(100,200);//300可以在上面执行,因为预解释的时候声明+定义已经完成了。 21 function fun(){ 22 console.log("easy js!") 23 } 24 console.log(fun)//->把整个函数的定义部分(函数本身)在控制台输出 25 console.log(fn(1,2))//->把当前函数执行的返回结果(return后面的值放回,如果没有return,默认返回的是undefined) 26 //图片见images中的基本数据类型和引用数据类型的存储方式.PNG 27 //1.当浏览器加载HTML页面的时候,首先会提供一个全局JS代码执行的环境>全局作用域 28 //(global/window) 29 30 //2.预解释(变量提声) 31 //在当前的作用域中,JS代码执行之前,浏览器首先会把默认的所有带var和function的进行提前声明或者(注意是或者,function就是声明+定义)定义(这是浏览器的 32 //机制). 33 //1)声明和定义: 34 //var num =12; 35 //声明(declare):var num;-->告诉浏览器在全局作用域中有一个num的变量。 36 //定义(defined):num=12;-->num=12;-->给我们的变量进行赋值。 37 38 //2)对于带var和function关键字的在预解释的时候操作还是不一样的 39 //var ->在预解释的时候只是提前的声明 40 //function->在预解释的时候提前的申明+定义都完成了。 41 //见图:浏览器预解释过程.png 42 //3)预解释只发生在当前的作用域下,列入:开始只对window下进行预解释,只有函数执行的时候才会对函数中的内容进行预解释。 43 44 //3.js中内存的分类 45 //堆内存:用来存储引用数据类型的值-》对象存储的是属性名和属性值,函数存储的是字符串代码块 46 //栈内存:用来提供一个js代码执行的环境-》作用域(全局作用域和私有作用域) 47 function fn(num1,num2){ 48 var total=num1+num2; 49 console.log(total); 50 } 51 </script> 52 </head> 53 <body> 54 55 </body> 56 </html>
1)基本数据类型和引用数据类型的存储方式
2)浏览器预解释过程
2.作用域
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>作用域链2</title> 6 7 <script type="text/javascript"> 8 //1.如何区分私有变量和全局变量(作用域链机制) 9 //1)在全局作用域下申明(预解释的时候)的变量是全局变量 10 //2)在私有作用域中声明的变量和函数的形参都是私有的变量 11 //在私有作用域中,我们代码执行的的时候遇到了一个变量,首先我们需要确定他是不是私有变量,如果是私有变量,那么和外面的没有关系。 12 //如果不是私有的,则往当前作用域的上级作用域进行查找,如果上级作用域也没有则继续查找,一直找到window。 13 14 //2.当函数执行的时候(直接目的:让函数体中的代码执行),首先会形成私有的作用域(在栈内存中) 15 //然后按照如下步骤执行: 16 //1)如果有形参,先给形参赋值 17 //2)进行私有作用域中的预解释 18 //3)私有作用域中的代码从上到下执行 19 //...未完待续 20 //函数形成一个新的私有作用域保护了里面的私有变量不受外界的干扰(外面修改不了私有的,私有的也修改不了外面的。 21 // 这种机制就是“闭包“--简单说就是互不影响。 22 // ) 23 console.log(totlal);//undefined 24 var total=0; 25 function fn(num1,num2) { 26 console.log(total);//undefined 27 var total=num1+num2; 28 console.log(total);//300 29 } 30 fn(100,200); 31 console.log(total)//0 32 33 //=========另外一个例子======== 34 console.log(total1);//undefined 35 var total1=0; 36 function fn22(num2,num3) { 37 console.log(total1);//0; 38 total1=num3+num2; 39 console.log(total1);//300:这里为什么是300不是其他值,就是因为作用域链的机制。。 40 } 41 fn22(100,200); 42 console.log(total1) 43 </script> 44 </head> 45 <body> 46 47 </body> 48 </html>
3.全局变量的细节问题
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>全局变量的细节问题3</title> 6 7 <script type="text/javascript"> 8 //在全局作用域中,带var和不带var的关系? 9 //区别:带var的可以进行预解释,所以在赋值的前面执行不会报错;不带var的是不能进行预解释的 10 //在前面执行会报错。 11 console.log(num);//->undefined 12 var num=12; 13 14 //console.log(num2);//->Uncaught ReferenceError: num2 is not defined 15 num2=12; 16 17 //============例子2=========== 18 //关系:num2=12->相当于给window增加了一个叫做num2的属性名,属性值是12; 19 //var num=12;->首先它相当于给全局作用域增加了一个全局变量num,但是不仅如此, 20 //它也相当于给window增加了一个属性名,属性值是12; 21 var num3=12; 22 console.log(num3); 23 num4=12; 24 console.log(num4); 25 console.log(window.num4) 26 </script> 27 </head> 28 <body> 29 30 </body> 31 </html>
4.total加var和不加var的区别
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>total加var和不加var的区别4</title> 6 <!--js中迷幻赋值,到底是给变量赋值,还是给属性赋值?--> 7 <script type="text/javascript"> 8 //私有作用域中出现的一个变量不是私有的,则往上级作用域进行查找,上级作用域没有则继续向上 9 //进行查找,一直知道到window为止3,如果window下也没有呢? 10 //我们是获取值:console.log(total);-->报错,没有声明变量 11 //我们是设置值total=100;->相当于window增加了一个属性名为total,属性值为100; 12 13 function fn(){ 14 total=100; 15 //这里不是变量赋值,他是看私有域-没有var声明的变量-》向上找-没有—》window也没有就只能给window赋值,使得属性名字是total,值是100; 16 } 17 fn(); 18 console.log(total) 19 //这一这里的total不是变量,是window的属性值,这是2个不同的概念,但是值是相同的。 20 //相当于: 21 console.log("这一这里的total不是变量,是window的属性值,这是2个不同的概念,但是值是相同的。") 22 console.log(window.total); 23 </script> 24 </head> 25 <body> 26 27 </body> 28 </html>
5.预解释是一种毫无节操的机制
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>预解释是一种毫无节操的机制5</title> 6 7 <script type="text/javascript"> 8 //预解释是一种毫无节操的机制 9 10 //in:"num" in window 判断num是否为window这个对象的属性,是的话返回true不是的话返回false 11 12 var obj={name:"lizbe",age:23}; 13 console.log("name" in obj);//true 14 console.log("eat" in obj);//false; 15 16 //1.预解释的时候,不管条件是否成立都要把带var的进行提前声明。 17 //window的预解释:var num;->window.num;说白了就是先进性预解释在执行代码,而且不会管条件判断,因为在一个域中。 18 if(!("num" in window)){ 19 var num=12; 20 } 21 console.log(num);//undefined 22 console.log("========例2======="); 23 //2.预解释的时候只预解释”=“左边的,右边的值,不参与预解释 24 //匿名函数之函数表达式:把函数定义的部分当作一个值赋值给我们的变量或者元素的某一个事件。 25 26 //window下的预解释:var fn; 27 //fun();//fn is not a function 28 var fun=function(){ 29 console.log("ok"); 30 }; //这也再次正式了fun存的是一个地址指向堆内存中的字符串,function(){ 31 //console.log("ok"); 32 //}; 33 //正式由于js(预解释的存在,全球唯一的机制),所以可以多次使用这种模式,他可以定义一个函数,这个函数的上面是不可以执行的。 34 35 36 // fn(); 37 // function () { 38 // console.log("ok") 39 // } 40 // fn(); 41 42 //3.自执行函数定义的那个function在全局作用下不进行预解释,当代码执行到了这个位置的时候定义和执行一起完成了。 43 //自执行函数:定义和执行一起完成了 44 // 45 (function (num) {})(100); 46 //以下是装逼写法: 47 +function (num) {}(100); 48 -function (num) {}(100); 49 ~function (num) {}(100); 50 !function (num) {}(100); 51 52 //4.函数体中,return下面的代码虽然不在执行了,但是浏览器一运行就已经进行了预解释,所以预解释优先一切。 53 //return后面跟着的是返回的值,所以不进行预解释; 54 function funct() { 55 console.log(num);//-》undefined 56 return function(){ 57 58 }; 59 var num =100; 60 } 61 funct(); 62 63 //5.在预解释的时候,如果名字已经声明过;不需要重新的声明;但是需要重新的赋值。 64 //在js中如果变量的名字和函数的名字重复了,也算冲突。 65 //预解释:var fn,window.fn; fn=xxxabbb23,window.fn=xxxabbb23; 66 var fn1=13; 67 function fn1() { 68 console.log("ok") 69 } 70 71 72 //======例子======= 73 //window预解释: 74 //声明+定义 fn333=xxxfff111 75 //声明var fn333;(不需要重新声明) 76 //声明(不重复进行)+定义(但是会定义) fn333=xxxfff222 77 //-》fn=xxxfff222 78 //=====牛逼的例子:========= 79 console.log("牛逼的例子关于预解释!!!!!!") 80 fn333();//->2 81 function fun333() {console.log(1);}; 82 fn333();//->2 83 var fn333=10; 84 fn333();//->error: fn is not a function. 85 function fn333() {console.log(2);}; 86 fn333(); 87 </script> 88 </head> 89 <body> 90 91 </body> 92 </html>
6.如何查找上级作用域
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>如何查找上级作用域</title> 6 7 <script type="text/javascript"> 8 //如何查找当前作用域的上级作用域? 9 //看当前函数实在那个作用域下定义的,那么它的上级作用域就是谁。 10 //和函数在哪里执行没有任何关系, 11 var num=12; 12 function fn() { 13 var num=120; 14 //见图作用域链.png你就知道这里为什么可以不写函数名称,因为这是一个指针链条。简称作用域链。 15 return function () { 16 console.log(num) 17 }; 18 } 19 var f= fn(); 20 f();//问执行结果:120; 21 22 //这个结果又是什么呢? 23 !function () {var num=1200;f()}();//120; 24 25 </script> 26 </head> 27 <body> 28 29 </body> 30 </html>
7.关于内存释放和作用域销毁的研究
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>7.关于内存释放和作用域销毁的研究.html</title> 6 7 <script type="text/javascript"> 8 //1.堆内存 9 //对象数据类型或者函数数据类型在定义的时候首先都会开启一个堆内存,并且堆内存有一个引用地址,如果外边有变量知道了这个地址 10 //我们就说这个内存被占用了,不能销毁。 11 var obj1={name:"张三"}; 12 var obj2=obj1; 13 //内存销毁 14 obj1=null; 15 obj2=null;//null是空对象指针。专门释放空间的,但是不是立即释放,浏览器空闲的时候回收。换句话说,刚刚等于null的时候,并没有销毁;只是有一个机制 16 //在浏览器中,过一段时间,进行销毁。不只是赋值为null,为1,2...也行。 17 //表示的就是堆内存中没有任何东西占用了内存。 18 19 20 //2.栈内存: 21 //1)全局作用域: 22 // 只有当页面关闭的时候,全局作用域才会销毁 23 //2)私有作用域:(只有函数执行会产生私有作用域) 24 // 一般情况下,函数执行会形成一个新的私有作用域,私有作用域中当代码执行完成后,我们当前作用域都会进行主动释放和销毁。 25 //但是还是存在特殊情况的: 26 // 当前私有作用域中的部分内容被作用域以外的东西占用了,那么当前作用域就不能销毁了。 27 // a.函数执行返回了一个引用数据类型的值,并且在函数的外面被其他东西给接收了,这种情况下一般形成的私有作用域都不会销毁。 28 // 例子: 29 function fn() { 30 var num=100; 31 //返回的是一个引用数据类型的值 32 return function (){ 33 }; 34 } 35 //一个东西接收了函数的值。 36 var f=fn();//fn形成的私有作用域不能销毁。 37 var odiv1=document.getElementById("div1"); 38 !function () { 39 odiv1.onclick=function () { 40 41 }; 42 }();//当前自执行函数也不能销毁。 43 44 //通过DOM方法获得的元素或者是元素集合类型都是对象数据类型的值 45 // b.在一个私有的作用域中绑定方法,一般情况下我们的私有作用域都不销毁。 46 47 48 // c.下述情况属于不立即销毁。--》fn返回的函数没有被其他的东西占用。但是还需要执行一次,所以暂时不销毁,当返回的值执行完毕后, 49 // 浏览器才会销毁。 50 function fnD() { 51 var num=100; 52 return function () { 53 54 }; 55 } 56 funD()();//首先执行fn,返回一个小函数对应的内存地址,紧接着让对应的小函数再执行。 57 // 58 </script> 59 </head> 60 <body> 61 <div id="div1" >有本事点我啊</div> 62 </body> 63 </html>
8.作用域练习题
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>作用域练习题</title> 6 7 <script type="text/javascript"> 8 //++i和i++ 9 //都是自身加1,再和其他的值进行运算的时候有区别 10 //i++;是先拿i的值进行运算,运算完成本身+1 11 //++i;先本身累加1,然后那累加完成的结果去运算 12 var i=5; 13 console.log(1+(i++));//->6 i=6 14 console.log(1+(++i));//->8 i=7 15 console.log(2+(i++)+(++i)+(++i)+(i++));//38 16 console.log(i);//11 17 console.log("===========================") 18 19 function fn() { 20 var i=10; 21 return function (n) { 22 console.log(n+(++i)); 23 } 24 } 25 //见图 练习题.jpn 26 var f=fn(); 27 f(10);//21 28 f(20);//32 29 fn()(10);//21 30 fn()(20);//31 31 //再来一题 32 console.log("=============="); 33 function fn2(i){ 34 return function (n) { 35 console.log(n+(i++)); 36 } 37 } 38 var f2=fn2(13); 39 f2(12);//25; 40 f2(14);//28 41 fn2(15)(12);//27 42 fn2(16)(13);//29 43 </script> 44 </head> 45 <body> 46 47 </body> 48 </html>
9.this
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <!--说明以下:想要自己开发一个js框架,而不是通过造轮子开发;this必须能够灵活运用。--> 5 <meta charset="UTF-8"> 6 <title>this</title> 7 8 </head> 9 <body> 10 <div id="div1" >有本事点我啊!this总共有5个知识点,就是这样。</div> 11 12 <script type="text/javascript"> 13 //我们再js中主要研究的是1.函数中的this 14 console.log(this); 15 console.log("==============="); 16 //2.js中的this代表的是当前行为执行的主体;js中的context代表的是 17 //当前行为执行的环境或者(区域): 18 19 20 //this是谁和函数再哪里定义的和再哪里执行的都没有任何的关系。 21 //如何区分this? 22 //1.函数执行,首先看函数名前面是否有”.“,有的话前面是谁this就是谁;没有的话就是window 23 function fnct() { 24 console.log(this); 25 } 26 var obj={fnct:fnct}; 27 fnct();//this->window 28 obj.fnct();//this-->obj 29 30 function sum() { 31 fnct();//this-->还是window 32 } 33 sum(); 34 35 var oo={ 36 // 37 sum:function () { 38 fnct(); 39 } 40 }; 41 oo.sum();//this-->window 42 43 //2.自执行函数中的this永远是window 44 //3.给元素的某一个事件绑定方法,当事件触发的时候,执行对应的方法,方法中的 45 //this是当前的函数。 46 function fnctDemo() { 47 console.log(this) 48 } 49 document.getElementById("div1").onclick=fnctDemo;//fn中的this是#div1 50 document.getElementById("div1").onclick=function () { 51 //this--->#div1 52 console.log("=====") 53 console.log(this); 54 console.log("========="); 55 fnctDemo(); 56 };//当点击的时候this的值是window, 57 </script> 58 </body> 59 </html>
10.综合是实战题
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>综合是实战题</title> 6 </head> 7 <body> 8 <div class="box"> 9 <h1>点击投票</h1> 10 <div id="btn">你投票吗?<span id="spanNum">0</span></div> 11 </div> 12 <div class="box1"> 13 <h1>点击投票</h1> 14 <div id="btn1">你投票吗?<span id="spanNum1">0</span></div> 15 </div> 16 17 <script type="text/javascript"> 18 var oBtn=document.getElementById("btn"); 19 var spanNum=document.getElementById("spanNum"); 20 //1.利用全局作用域不销毁的原理,把需要累加的数字定义为全局变量 21 var count=0; 22 oBtn.onclick=function () { 23 count++; 24 spanNum.innerHTML=count; 25 }; 26 //2.为了放置全局变量的相互冲突,在实际项目当中应该尽量不使用全局变量,或者说禁止使用全局变量。 27 28 var oBtn1=document.getElementById("btn1"); 29 var spanNum1=document.getElementById("spanNum1"); 30 //自己形成一个不销毁的作用域来保护我们需要累加的数字 31 +function () { 32 var count1=0; 33 oBtn1.onclick=function () { 34 count1++; 35 spanNum1.innerHTML=count1; 36 } 37 }(); 38 // 另外一种形式: 39 // oBtn1.onclick=(function () { 40 // var count=0; 41 // return function () { 42 // count++; 43 // spanNum1.innerHTML=count; 44 // } 45 // })(); 46 47 //弊端:有一个不销毁的私有作用域,所以占那么一丢丢内存。 48 //另外一种:利用innerHtml的方式处理,每一次点击的时候都到页面中获取最新的值,累加,把最后累加的结果重新放回去。 49 // oBtn1.onclick=function () { 50 // //spanNum1获取的页面中的内容返回的是一个字符串。 51 // //spanNum1.innerHTML=spanNum1.innerHTML+1; 52 // spanNum1.innerHTML++; 53 // }; 54 //弊端:每一次都需要把页面中的内容先转换为字符串然后在累加,累加完在重新添加回去, 55 //当重新添加回去的时候浏览器都要重新渲染以下。 56 57 //4.利用自定义属性存储:比较好的方式。 58 oBtn1.count=0; 59 oBtn1.onclick=function () { 60 spanNum1.innerHTML=++this.count; 61 }; 62 63 </script> 64 65 </body> 66 </html>
11.练习题
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>练习题</title> 6 </head> 7 <body> 8 <h1>见图练习题.png</h1> 9 <script type="text/javascript"> 10 var num=20; 11 var obj={ 12 num:30, 13 fn:(function(num){ 14 this.num*=3; 15 num+=15; 16 var num=45; 17 return function () { 18 this.num*=4; 19 num+=20; 20 console.log(num); 21 }; 22 })(num),//->把全局变量num的值20赋值给了自执行函数的形参,而不是 23 //obj下的30,如果是obj下的30,我们需要写obj.mum. 24 }; 25 var fn=obj.fn; 26 fn(); 27 obj.fn(); 28 console.log(window.num,obj.num); 29 </script> 30 </body> 31 </html>