JS程序的执行过程.
1.词法分析 . 语法分析阶段:变量声明(赋值阶段在执行阶段进行);函数声明带函数赋值.
2.函数表达式的变形
//函数表达式的变形var fn=function foo(){console.log("abc");};fn();//"abc" foo();//报错
2.词法作用域,作用域链,变量查找过程
按照书写个还是决定了变量可访问的范围,跟调用方式无关
var height =10;setTimeout(function(){var height2=30;setTimeout(function(){var height1 =80;console.log(height2);//30});console.log(height1);//获取不到height1});
3.函数具有独立的作用域,在函数内部的程序执行过程,也会有一套完整的预解析的步骤.
4.拷贝创建对象
/*拷贝创建对象的高级使用方式*/function hightExtend(){var key =0, i =0, len = arguments.length;target =null;if(len ==0){returnfalse;}elseif(len ==1){return arguments[0];}else{i++;target = arguments[0];for(; i < len; i++){for(key in arguments[i]){target[key]= arguments[i][key];//把后面传入的对象遍历一下,但遍历每一个对象的属性//添加到target元素上}}}}
//5.历史遗留问题if(true){function foo1(){console.log(foo1);}}else{function foo2(){console.log(foo2);}}//在ff.chrome中打印结果为f1,在旧版本中打印结果为f2//规则规范,不允许在语法块中声明函数6.跨作用域访问变量
/** 查找作用域中的变量:首先查找当前作用域是否定义了该变量,若果定义了就不会再继续查找* */function foo(a,b){console.log(a);}foo(1,4);//函数的参数是在foo作用域执行的时候//预解析:分析a是一个采纳数所以会向当前作用域添加一个变量a并且给a赋值,值是实参的值//代码执行;参数已经有值了 可以直接获取var num =10;function fn(num){console.log(num);//undefined// 首先查找当前作用域是否定义了num}
3.闭包 模块化 闭包最常见的使用场景
//沙箱:模块化,沙箱是一个隔离的环境,最大的好处就是避免全局变量的污染.var model =(function(){//一个匿名的立即执行函数var price =900;//这是一个model局部的变量,外界无法访问,无法修改//有效的保障了属性的安全性return{pay:function(money){if(money < price){console.log("您的消费不够,请继继续消费");}else{console.log("恭喜成为VIP");}}}})();console.log(model.pay(800));//"消费不够.." undefinedmodel.pay(1000);//"VIP..."//千万注意不用打印,pay函数本身就有输出,如果在console.log中//会输出函数的返回值.
//在面向对象中使用沙箱//应用场景:在页面初始化的时候要用到f1,f2,f3三个函数//这三个函数不想让外界访问,需要把这单个方法声明成为构造函数//内部私有的变量var songManger =(function(){function f1(){console.log("函数f1的方法");}function f2(){console.log("函数f2的方法");}function f3(){console.log("函数f4的方法");}functionSongManger(){//声明一个构造函数}SongManger.prototype={//在原型对象上扩展方法init:function(){f1();f2();f3();}};// return SongManger;//注意这个返回的是构造函数,使用之前需要先实例化一下returnnewSongManger;//把构造函数返回,实例化一下,这样外边就不需要实例化了})();songManger.init();//
/** 闭包实现缓存* 属性:有个键--值 --->所以可以将缓存数据存放在一个对象中* 方法:缓存存储 setCache* 缓存的获取 getCache* */function configCache(){var obj={};//设置一个内部的对象 用来存储缓存数据;这个属性是私有的//对外暴露两个公共的方法return{setCache:function(k,v){//设置缓存obj[k]=v;},getCache:function(k){//获取缓存return obj[k];}};}var conf = configCache();console.log(conf);conf.setCache(1,'sdwqecwqv');console.log(conf.getCache(1));//sdwqecwqv/** 注意下面这种情况,两次configCache()会产生不同的执行环境* */configCache().setCache(1,'sdwqecwqv');console.log(configCache().getCache(1));//undefined/** 使用立即执行函数* */var cacheConfig =(function(){var obj={};return{setCache:function(k,v){obj[k]=v;},getCache:function(k){return obj[k];}}})();
// 给所有的 li 提供点击事件, 在点击后 弹出目录中的文本内容<body><ul><li>aaa</li><li>bbb</li><li>ccc</li><li>ddd</li><li>eee</li></ul></body><script>var list = document.getElementsByTagName('li');for(var i =0; i < list.length; i++){list[ i ].onclick =function(){//alert(i);//i=5//alert( list[ i ].innerHTML );//报错//alert(this.innerHTML);//一种解决方案//list[4]:eee list[5]:undefined};}</script>
//第二种解决方案:闭包// for ( i = 0; i < list.length; i++ ){var j=i;// list[ i ].onclick = (function (j) {// return function (){// alert(list[j].innerHTML);// }// }(i));// }
//callerfunction fn1(){console.log(fn1.caller);//打印的是fn1函数体}function fn2(){fn1();}fn2();console.log(fn1.caller);//null caller方法只能在函数调用的时候获取
//问题理解的关键: 内层函数保存的j,当执行的时候从哪里取的??//每次绑定都会执行一个外层函数,都会产生一个新的作用域,内层函数会保存这个新作用域中的变量var list = document.getElementsByTagName("li");for(var i=0;i<list.length;i++){list[i].onclick=(function(){var j=i;//每次绑定都会执行外层函数,都会保存一个J的变量在内层函数中returnfunction(){console.log(list[j].innerHTML);}})();}
//结论://闭包保存一个数据,让外层函数执行一次//闭包保存多个数据,让外层函数执行多次var list = document.getElementsByTagName('li');for(var i =0; i < list.length; i++){//希望每一次绑定的时候,就保存一下当前的索引// 多次绑定就保存多个索引——>利用闭包,保存多个数据:让外层函数执行多次list[i].onclick =(function(){var j=i;returnfunction(){//内层函数由浏览器帮助我们调用的alert(list[j].innerHTML);}}());//调用了5次,外层函数调用了五次,就会产生5个独立的作用域,这几个作用域中的j变量的值都不一样}