在我们js中存储数据的空间可以分为两种,堆内存和栈内存
堆内存:我们定义的那些引用数据类型的数据都会在堆内存中开辟空间。
栈内存:我们运行的js代码还有我们定义的基本数据类型,都直接在栈内存中存储
基本类型 Undefined ,Null ,Boolean ,Number,String 该五种类型在内存中占用空间,即
值保存在栈内存中,可以提高查询变量速度,我们说他们是按值访问的
引用类型
引用类型,内存地址存在栈中,实际值存在堆内存。在堆内存中为这个值分配空间,因为这个值不确定大小,因此不能把它保存在栈内存中,因此把内存地址保存在栈内存中。
带function关键字的(就是定义函数),在整个脚本执行之前,就已经把函数名(其实就是变量名)在内存里安排好了,并且给这个函数名赋了值(就是函数体)
//var a; //遇见var,先把a声明,分配地址。 即var=a;
alert(a);
var a=10;
alert(a);
---------------------------------------------------------------------------------------
fn();//1
function fn() {
//alert(arguments.callee); //arguments.callee 函数自己 function fn() { alert(arguments.callee); //arguments.callee 函数自己
alert(1);
}
fn();
预解释是发生在作用域下的,刚开始进来的时候,我们预解释的是全局作用域(Global),在js中我们的globe就是window。
如果在当前作用域下的一个变量,没有预解释。就向他的上一级去找,直到找到window为止,如果window也没有定义,就被报错误。xxx is not defined
function:我们运行函数的时候,会生成一个新的私有作用域(我们可以理解为开辟一个新的栈内存),在这个内存中,我们也要执行我们的预解释机制
当我们的函数执行后,这个内存或者作用域就会被销毁。
function的运行周期,一个function从window下的预解释的时就声明并定义了,当function执行的时候会产生新的作用域,当function执行完成,
通常这个这个作用域会被销毁。
预解释只发生在var 和 function 上。
-------------------------------------------------------------------------------------------------------------------------------------------
fn(); //报错 找不到fn
var fn1= function fn() {
//alert(arguments.callee); //arguments.callee 函数自己 function fn() { alert(arguments.callee); //arguments.callee 函数自己
alert(1);
}
fn(); //报错 找不到fn
//预解释只会对等号左边有效果,所以这里fn1会预解释,但右边不会被预解释 相当于 fn1=function(){ 函数体}
-------------------------------------------------------------------
var n = 9;
var s = "str";
function fn() {
alert(n);
alert(s);
n = 7;
s = "rts";
var n = 6;
alert(n);
}
fn();
alert(n);
alert(s);
undefind ,str ,6,9 , rts
-------------------------------------------------------------------------------------------------------
<script>
alert(a); // a is not defined
</script>
<script>
var a = 1;
</script>
<script>
alert(a);//1
</script>
预解释只对当前脚本块中起作用
-------------------------------------------------------------------------------------------------------
alert(a);
var a=12;
var a;
var a;
alert(12);
undefined , 12
------------
function a(){}
var a;
alert(a);
弹出 function a(){} 因为function a也是预解释
预解释不会在同一变量上重复发生。即var a=12;已经预解释,后面的var a不会在执行。
--------------------------------------------------------------------------------------------------------
alert (f);
fn(); //没有预解释,所以报错
var f=funcion fn(){ alert("ok");}
undefind, fn is not defined
系统找不到fn,所以调用fn会报错。
JS只对等号左边带var 和 function 预解释 。等号右边是赋值,不会预解释。
-------------------------------------------------------------------------------------------------------
var a = 12;
function a() {alert(1);}
alert(a);
a(); //a is not a function
在项目中,切记不要让函数名和变量名相同
function a和 变量 a (没赋值),function a会覆盖变量a
var a ;
function a() {alert(1);}
alert(a); //function a() {alert(1);}
a(); //弹出1
function a和 变量 a ( 赋值),变量a会覆盖function a
var a = 12;
function a() { alert(1); }
alert(a); //弹出12
a(); //a is not a function
--------------------------------------------------------------------------------------------------------
alert(a);
if(1==2)
{
var a=12;
}
预解释是不受if获其他判断条件影响的。即使条件不成立,条件里只要有var或function也会被预解释。
-----------------------------------------------------------------
if(!("a" in window)){ var a= "珠峰培训";}
alert(a);
//undefined 因为a在预编译中声明了
----------------------------------------------------------------
我们的预解释也不会受到function的return影响
function fn() { alert(1); };
function fn2() {
alert(fn);
fn = 3;
alert(fn);
return;
function fn() { alert(2); }
}
fn2(); //function fn() { alert(2); } -- 3
定义一个function,如果我们只是return,没有返回任何东西外面接收的也是undefined。
不加return,也是undefined
-----------------------------------------------------------------
var f=function fn(){ alert(); }
//等号右边当成一个值,不会被预解释,所以系统找不到这个函数。
alert(typeof f); //function
预解释,f是个变量
function fn(){}
预解释 fn是个function
-----------------------------------------------------------------
闭包:当我们的一个函数返回一个新的函数,我们在外面定义一个变量来接收,这样这个函数的内存就不能在执行完成之后自动销毁,也就是所谓的函数内存被占用了。(前提是我们返回的function里面有需要外面函数的东西)
在函数总可以(嵌套)定义令一个函数时,如果内部的函数引用外部函数的变量,就会产生闭包
闭包其实就是函数在运行的时候产生的那个私有作用域。
闭包的作用说的更直白一些就是为了让变量更安全,让一个环境中的变量与其它环境中的变量隔离开不产生冲突
闭包是形成的私有作用域 ,让内部的变量不受外部函数影响
最外层的function内,内存被占用,得不到释放。
-------------------------------------------------------------------
var n = 0;
function a() {
var n = 10;
function b() {
n++;
alert(n);
}
b();
return b;
}
var c = a();
c();
alert(n);
11
12
0
相当于
var n = 0;
function a() {
var n = 10;
return function () {
n++;
alert(n);
}
}
var c = a();
c();
c();
----------------------------------------------------------------------
var n = 99;
function ourer() {
var n = 0;
return function inner() {
return n++; //return 直接返回的那个,其实是一个结果或者是值,是不需要预解释的。
//this.n++;
}
}
var c = ourer();
//var c=funciton inner(){return n++;} 直接去上一级找,n=0 this.n=99
var num1 = c(); //0
var num2 = c(); //1
alert(num1);
alert(num2);
-------------------------------------------------------------------
;()();
;(funtion(){ 函数体 })();
如果我们想要在闭包中使用我们的全局变量
1,传参数
2,window
3,私有作用域下声明同名的变量
--------------------------------------------------------------
this只存在于function中
this表示谁,由当前调用这个方法的主体来决定
this关键字和在哪个作用域下执行也没关系,和调用的主体有关系
就看这个点前面是什么,什么都没有就是window。
var point = {
x: 10,
y: 20,
moveTo: function (x, y) {
var moveX = function (x) { this.x = x; }
var moveY = function (y) { this.y = y; }
moveX(x); //主体是window
moveY(y);
}
}
point.moveTo(100, 200);
alert(point.x);//10
alert(point.y);//20
alert(x);//100
alert(y); //200
-----------------------------------------------------------------------------------------------------------------------
var number=2;
var obj={
number:4;
fn1:(funciton(){ //在这样一个闭包内,this指向window
this.number*=2; //这个this 是window
number=number*2; //迷惑人
var number=3;
return function(){
this.number*=2;
number*=3; //指向外面的那个number=3
alert(number);
}
})()
}
var fn1=obj.fn1;
//fn1= function(){
// this.number*=2;
// number*=3; //指向外面的那个number=3
// alert(number);
// }
alert(number);//4
fn1(); //alert(9); window.number=4,
obj.fn1();
// function(){
// this.number*=2;
// number*=3; //指向外面的那个number=3
// alert(number);
// }() this.number=obj.number*2=8 alert(27) window.number=8,
alert(window.number);
alter(obj.number);
---------------------------------------------------------------------------------
具体的应用实例:
有如下html代码,要求:点击下面的li,会弹出对应的索引号。
<ul>
<li>列表一</li>
<li>列表二</li>
<li>列表三</li>
<li>列表四</li>
<li>列表五</li>
</ul>
很多人给出了如下错误的代码(点击li时弹出的是5):
<script>
var oLis=document.getElementsByTagName('li');
for(var i=0; i< oLis.length; i++){
oLis [i].onclick=function() {
//注意:这里的这个匿名方法,在循环运行的时候这个匿名方法本身并不运行,当点击某个li的时候,这个方法才运行呢。
alert(i);
//这里的这个i不是在这个匿名方法里定义的,而是上一级作用域里定义的。当这句代码运行的时候,循环早已经结束,并且i已经等于oLis.length了。
这里的问题出在这个方法里用到的是上一级作用域里定义的变量,如果要解决这个问题。
};
}
//事件绑定相当于做计划,当点击的时候才相当于执行计划,请参考第一天教材的事件绑定部分的描述
</script>
正确的代码一:
<script>
var oLis=document.getElementsByTagName('li');
for(var i=0; i< oLis.length; i++){
;(function(i){//这里的这个i,已经不是外面的那个i变量了。
oLis [i].onclick=function() { alert(i); };
})(i);
}
</script>
把上面的代码分解一下:
当第一次循环运行的时候,i的值为0,则实际运行的代码如下:
;(function(i){//这里的这个i,已经不是外面的那个i变量了。
oLis [i].onclick=function() {
alert(i);
};
})(0);//因为i第一次是0,那么这里就相当于把0做为实参传给这个要运行的匿名函数,当这个匿名函数运行的时候,实际执行的就是这句代码了:
oLis [0].onclick=function() {
alert(0);
};//alert里已经是一个具体的数值了,第一次是0,依次是1、2、3、4。
正确代码二:
<script>
function fn(i){
oLis [i].onclick=function() { alert(i); };
}
var oLis=document.getElementsByTagName('li');
for(var i=0; i< oLis.length; i++){
fn(i); //相当于在fn()里形成私有作用域,相当于形成闭包
}
</script>
var m=999;
function fn(){
var n=m=i=9;
}
alert(m); //999
这里面,m和i是全局变量
----------------------------------------------------------------------
var a=1;
if(!"a" in window)
{
alert(a);
var a=2;
}
undefined //没有弹出
var a=1;
if("a" in window)
{
alert(a);
var a=2;
}
弹出1
如果你觉得我的文章对您有帮助,给点鼓励,谢谢