1-1、作用域深入和面向对象
课时1:预解释
JS中的数据类型
number、string、 boolean、null、undefined
JS中引用数据类型
object: {}、[]、/^$/、Date
Function
var num=12; var obj={name:'白鸟齐鸣',age:10}; function fn(){ console.log('勿忘初心方得始终!') } console.log(fn);//把整个函数定义部分(函数本身)在控制台输出 console.log(fn());//把当前函数返回的结果(return后面写的是啥,返回值就是啥,如果没有return,默认返回值是undefined);
window全局作用域:
1、当浏览器加载HTML页面的时候,首先会提供一个全局JS代码执行的环境–>全局作用域(global / window)
2、预解释(变量提升)
在当前的作用域中,JS代码执行之前,浏览器首先会默认的把所有带var 、function的进行提前的声明或者定义
1)理解声明和定义
var num=12;
声明(declare):var num;->告诉浏览器在全局作用域中有一个num的变量了,如果一个变量只是声明了但是没有赋值,默认的值是undefined
定义(defined):num=12; ->给我们的变量进行赋值
2)对于带var和function关键字的在预解释的时候操作还是不一样的
var -> 在预解释的时候只是提前的声明
function -> 在预解释的时候提前的声明+定义都完成了
3)预解释只发生在当前的作用域下,例如:开始只对window下的进行预解释,只有函数执行的时候才会对函中的进行预解释
console.log(num);//undefined var num=12; console.log(num);//12 var obj={ name:'白鸟齐鸣', age:100 } fn(100,200);//可以在上面执行,因为在预解释的时候声明+定义已经完成了 function fn(num1,num2){ var total=num1+num2; console.log(total); }
3、JS中内存的分类
栈内存:用来提供一个供JS代码执行的环境 –> 作用域(全局作用域、私有作用域)
堆内存:用来存储引用数据类型的值 -> 对象存储的是属性名和属性值,函数存储的是代码字符串…
课时2: 作用域链
1、如何区分私有变量和全局变量?
1) 在全局作用域下声明(预解释的时候)的变量是全局变量
2) 在“私有作用域中声明的变量”和“函数的形参”都是私有变量;
在私有作用域中,我们代码执行的时候遇到了一个变量,首先我们需要确定它是否为私有的变量,如果是私有的变量,那么和外面的任何东西都没有关系;如果不是私有,则往当前作用域的上级作用域进行查找,如果上级作用域也没有则继续查找,一直找到window为止……作用域链2、当函数执行的时候(直接目的:让函数体中的代码执行),首先会形成一个私有的作用域,然后按照如下的步骤执行
1)如果有形参,先给形参赋值
2)进行私有作用域中的预解释
3)私有作用中的代码从上到下执行
……
函数形成一个新的私有作用域保护了里面的私有变量不受外界的干扰(外面修改不了私有的,私有的也修改不了外面的)–>闭包
console.log(total);//undefined var total=0; function fn(num1,num2){ console.log(total); //undefined var total=num1+num2; console.log(total);//300 } fn(100,200); console.log(total);//0
console.log(total);//undefined var total=0; function fn(num1,num2){ console.log(total); //total不是私有的,找全局下的total,也就是在这里出现的所有的total其实应该都是全局->0 total=num1+num2; //全局的total=300 console.log(total);//300 } fn(100,200); console.log(total);//300
课时3:全局变量的细节问题
在全局作用域中,带var 和不带var的关系?
区别:带var的可以进行预解释,所以在赋值的前面执行不会报错;不带var的是不能进行预解释的,在前面执行会报错:
console.log(num);//undefined var num=12; console.log(num2);//报错;num2 is not defined,不执行下面 num2=13; console.log(num2);
关系:num2=12 –>相当于给window增加了一个叫做num2的属性名,属性值是12
var num=12;–>首先它相当于给全局作用域增加了一个全局变量num,但是不仅如此,它也相当于给window增加了一个属性名num2,属性值是12
console.log(num);//undefined var num=12; console.log(num);//12 num2=13; console.log(num2);//13-->window.num2
第一种: var total=0; function fn(){ console.log(total);//undefined var total=100; } fn(); console.log(total);//0 第二种: var total=0; function fn(){ console.log(total);//0 total=100; } fn(); console.log(total);//100 第三种: 私有作用域中出现了一个变量不是私有的,则向上级作用域进行查找,上级没有则继续向上查找,一直找到window为止,如果window下也没有呢? 我们是获取值:console.log(total);-->报错 我们是设置值: total=100;->相当于给window增加了一个属性名total,属性值100 function fn(){ //console.log(total);//total is not defined total=100; } fn(); console.log(total);//100;window.total=100; JS中如果在不进行任何特殊处理的情况下,上面的代码报错,下面的代码都不在执行了
课时4:预解释是一种毫无节操的机制
预解释是毫无节操的一种机制
自从学了预解释,从此节操是路人
in:”num” in window 判断num是否为window这个对象的一个属性,是的话返回true,不是的话返回false
1、预解释的时候不管你的条件是否成立,都要把带var的进行提前声明 window的预解释:var num;-->window.num; if(!('num' in window)){//'num' in window ->true->取反是false var num=12; } console.log(num);//undefined 2、预解释的时候只预解释"="左边的,右边的是值,不参与预解释 匿名函数之函数表达式:把函数定义的部分当做一个值赋值给我们的变量/元素的某一个事件 window下的预解释:var fn; fn();//fn is not a function var fn=function (){ console.log('OK'); } fn();//OK;函数声明+定义完成 function fn(){ console.log('OK'); } fn();//OK 3、自治行函数定义的那个function在全局作用域下不进行预解释,当代码执行到这个位置的时候定义和执行一起完成了 自执行函数:定义和执行一起完成了 (function (num){})(100); ~function (num){}(100); +function (num){}(100); -function (num){}(100); !function (num){}(100); 4、函数体中return下面的代码虽然不在执行了,但是需要进行预解释;return后面跟着的都是我们返回的值,所以不进行预解释 function fn(){ //预解释:var num; console.log(num);//undefined return function (){ } var num=100; }; fn(); 5、如果在预解释的时候,如果名字已经声明过了,不需要从新的声明,但是需要新的赋值; 在JS中如果变量的名字和函数的名字重复了,也算冲突 预解释:var fn; window.fn; fn=xxxfff000 window.fn=xxxfff000 var fn=13; function fn(){ console.log('OK'); } window预解释: 声明+定义 fn=xxxfff111 声明 var fn;(不需要重新声明) 声明(不重复进行)+定义 fn=xxxfff222 -> fn=xxxfff222 fn();//2 function fn(){console.log(1);} fn();//2 var fn=10;//fn=10 fn();//fn() ERROR:fn is not a function,以下不执行 function fn(){cosole.log(2);} fn();
课时5:如何查找上级作用域
如何查找当前作用域的上一级作用域?
看当前函数是在哪个作用域下定义的,那么它的上级作用域就是谁 –> 和函数在哪执行的没有任何的关系
var num=12; function fn(){ var num=120; return function (){ console.log(num);//120 } } var f=fn(); f() ~function (){ var num=1200; f();//120 }()
课时6:关于内存释放和作用域销毁的研究
堆内存
对象数据类型或者函数数据类型在定义的时候首先会开辟一个堆内存,堆内存有一个引用的地址,如果外面有变量等知道了这个地址,我们就说这个内存被占用了,就不能销毁了
var obj1={name:'张三'}; var obj2=obj1; 我们想要让堆内存释放/销毁,只需要把所有引用它的变量值赋值为null即可,如果当前的堆内存没有任何东西被占用了,那么浏览器会在空闲的时候把它销毁.... obj1=null; obj2=null;
栈内存
1)全局作用 (栈内存)
只有当页面关闭的时候全局作用域才会销毁2)私有作用域(只有函数执行会产生私有作用域) (堆内存)
一般情况下,函数执行会形成一个新的私有作用域,当私有作用域中的代码执行完成后,我们当前作用域都会主动的进行释放和销毁但是还是存在特殊情况的:
当前私有作用域中的部分内存被作用域以外的东西占用了,那么当前的这个作用域就不能销毁了
a、函数执行返回了一个引用数据类型的值,并且在函数的外面被一个其他的东西给接收了,这种情况下一般形成的私有作用域都不会销毁
function fn(){ var num=100; return function(){ } } var f=fn(); 解析: 返回的是一个函数 为引用数据类型
b、在一个私有的作用域中,给DOM元素的事件绑定方法,一般情况下我们的私有作用域都不销毁
var oDiv=document.getElementById('div1'); ~function (){ oDiv.onclick=function (){ } }()//当前自执行函数也不销毁
c、下述情况属于不立即销毁->fn返回的函数没有被其他的东西占用,但是还需要执行一次呢,所以暂时不销毁,当返回的值执行完毕后,浏览器会在空闲的时候销毁了–>’不立即销毁’
function fn(){ var num=100; return function (){ } } fn()(); //首先执行fn,返回一个小函数对应的内存地址,然后紧接着让返回的小函数在执行
课时7:作用域练习题
++i和i++
都是自身累加1,在和其他的值进行运算的时候是有区别的
i++:先拿i的值进行运算,运算完成本身在加1
++i:先本身累加1,然后拿累加完成的结果去运算
var i=5; //console.log(1+i++)//6 i=6 //console.log(1+(++i))//i=6 7 console.log(2+(i++)+(++i)+(++i)+(i++));//30 cosnle.log(i);//9
function fn(){ var i=10; return function (n){ console.log(n+(++i)); } } var f=fn(); f(10);//21 i=11 f(20);//32 i=11+1 fn()(10);//21 i=11 新作用域 fn()(20);//31 i=11 新作用域
function fn(i){ return function (n){ console.log(n+i++) } } var f=fn(13); f(12);//25 f(14);//28 fn(15)(12);//27 fn(16)(13);//29
课时8:this关键字
我们在JS中主要研究的都是函数中的this
JS中的this代表的是当前行为执行的主体:JS中的context代表的是当前行为执行的环境(区域):
this是谁和函数在哪定义的和在哪执行的都没有任何关系:如何的区分this呢?
1、函数执行,首先看函数名前面是否有” . “,有的话” . “前面是谁this就是谁;没有的话this就是window
function fn(){ console.log(this); } var obj={fn:fn}; fn();//this-->window obj.fn()//this-->obj
function sum(){ //this-->window fn();//this-->window } sum(); var oo={ sum:function (){ //this-->oo fn();//this-->window } } oo.sum();
2、 自执行函数中的this永远是window
3、给元素的某一个事件绑定方法,当事件触发的时候,执行对应的方法,方法中的this是当前的元素
function fn(){ console.log(this); } var obj={fn:fn}; fn();//this-->window obj.fn();//this-->obj document.getElementById('div1').onclick=fn;//fn 中的this是div1 document.getElementById('div1').onclick=function (){ //this-->#div1 fn();// this-->window };
4、 在构造函数模式中,类中(函数体中)出现的this.xxx=xxx中的this是当前类的一个实例
课时9 综合练习题
var num=20; //20*3=60 var obj={ num:30, fn:(function (num){ this.num*=3;//num=60 num+=15; //85 var num=45; return function (){ this.num*=4;//240 num+=20; console.log(num); } })(num)// --> 把全局变量num的值20赋值给了自执行函数的形参,而不是obj下的30,如果想是obj下的30,我们需要写obj.num }; var fn=obj.fn; fn();//65 obj.fn();//65+20=85 console.log(window.num,obj.num);//240 120