前端技术的发展变化太快了,各种各样的框架。再变也离不开js。所以,在此把js的高级语法总结一遍。 js解析和执行包括:全局和函数
一:全局预处理
<script type="text/javascript"> var a=1; function xx(){ alert(a); } </script>
这段代码会正常输出a=1;
<script type="text/javascript"> var a=1; function xx(){ alert(a); var a=5; } </script>
输出 undefined 为什么呢? 不报错 输出undefined 。 这就是js的解析执行过程
js执行过程分为:预处理的阶段和执行阶段
预处理阶段:
一:创建一个词法环境 LexicalEnvironment==window
二:扫描js代码:
1.用声明的方法创建的函数
2.用var 定义的变量
预处理js处理命名冲突:
先扫描函数声明后扫描变量(var声明)
处理函数声明有冲突会覆盖
处理变量声明有冲突,会忽略
下面为例子:
<script type="text/javascript"> alert(a); //undefined alert(b); //报错 alert(f); //字符串的表达式 alert(g); //undefined var a=5; b=6; alert(b); function f(){ console.log('f'); } //表达式创建的函数 var g=function(){ console.log('g'); } alert(g); </script> js分为两个阶段: 一:预处理阶段 1.var 定义的变量 2.声明的方式创建的函数 window { f:指向函数 a:undefined g:undefined (表达式创建的函数可以当成变量) } 二:执行时 没有定义var 就一次性赋值 window { f:输出函数的值 a:5 变量的定义 b: 6 函数赋值 g:指向函数 }
作用域:
<script type="text/javascript"> alert(a); //undefined alert(b); //undefined alert(c); //报错 alert(d); //报错 var a=1; if (false) { var b=2; }else{ c=3; } function f(){ var d=4; } for (var i = 0; i <3; i++) { } alert(i); //输出3 说明:js是没有块作用域的 js是不垮作用域的
函数作用域:
function f(){ var x function g(){ //函数 g()是不能访问 var x的 } }
动态作用域 : 只有在用行时确定
function f(){ alert(x); } function f1(){ var x=5; f(); } function f2(){ var x=6 ; f(); } f1(); //报错 (等于window 没有全局变量) 说明js 没有动态作用域
静态作用域 别称:词法作用域 (lexical) 闭包
词法作用域的特点:
一:在js解析器读取创建函数的时候
1.给函数 f 添加一个成员 【【scopel】】 ==创建 函数 f 时的词法环境(lexicalEnv)==window
2.调用 f(); 真正开始执行的时候 创建自己的词法环境 (lexical)==函数本身 f.[[scope]]==window
二:在词法解析解析阶段(在声明的阶段) 确定了相关的作用域
var x=100; function f(){ alert(x); } function f1(){ var x=5; f(); } function f2(){ var x=6 ; f(); } f1(); //输出100
作用域链条:
function f(){ // 解析 f的 scope==window f 调用时创建自己的词法 (lexical {x=100})-->f.[[scope]] var x=100; function g(){ //创建 g时 g.[[scope]]==f.(lexical) // g 运行时创建自己的(lexical)-->指向了 g[[score]] } g(); }
形成的链条:
g.lexical-->g。[[scope]] -->f.lexical --->f.[[scope]] --->window
js创建函数的方式:
1.声明的方式 function f(){ } 2.匿名函数 var f=function(){ } 3.匿名函数 var f=function x(Aargument){ } 4. var f=new Function(“参数”,“alert(xx)函数体”) function f(){ //给f scope==window 预处理阶段 把x和g 加进去 var x=100; var g=function(){ //g.score==f.lexical alert(x); } g(); } f(); //输出100 function f(){ var x=100; //g.[[scope]]==window var g= new Function(“”,“alert(x)”); g(); //报错 x没有定义 } f(); </script>
作用域的本质:
当在函数中找到一个变量的时候,首先,会在这个函数本身的词法环境中去找,找到结束--->找不到,去父函数的词法环境中去找--->window 中途找到退出
具体的用法:(作用域就是一个信息隐藏)
全局变量越来越多时,会导致冲突。
匿名的立即调用函数的写法: (function(){ var a=5; var b=6; function f(){ alert(a); } window.f=f; //技巧:在外面能访问到 定义window })(); f(); //输出5 这样就只对外面提供了一个f函数,减少了命名的冲突
闭包:
定义:是词法闭包的简称,引用了自由变量的函数,这个被引用的自由变量将和这个函数一同存在。即使离开了他的创建环境也不例外。(闭包就是一个对象,里面包含的一个函数,函数中有被他补货的变量)
本质:js支持作用链和函数的嵌套函数
自由变量指父函数的自由变量(a,b)
闭包是什么时候创建的呢:
1.给f2创建一个[[scope]],===父函数f1的词法环境
2.扫描f2的代码,保留f1的词法环境
闭包函数的写法:
如何查看(在浏览器中):
代码:
<script type="text/javascript"> function f1(){ //父函数 var a=10; var b=20; function f2(){ //子函数 coonsole.log(a); } f2(); } f1(); //即使离开了他的创建环境也不例外。 function f1(){ var a=10; var b=20; return function f2(){ coonsole.log(a); } } var result=f1(); result(); </script>
不调用父元素的属性是不会产生闭包的。
代码:
function f1(){ var m=10; function f2(){ coonsole.log("fss"); } f2(); } f1();
不会看到任何结果:
调用父的父元素:(是可以产生闭包的)
function f1(){ var m=10; function f2(){ var n=20; function f3(){ coonsole.log(m); } f3(); } f2(); }
效果:
使用闭包的优势:
1.减少全局变量
function add(){ var a=0; return function(){ a++; alert(a); } } var result=f(); resul (); resul ();
2.减少传递给函数的参数数量
//减少参数 function add(base,max){ } //构建一个计算工厂的函数 function calFactory(base){ return function(max){ var total=0; for (var i = 1; i <=max; i++) { total+=i; } return total+base; } } var adder=calFactory(2); alert(adder(3)); //输出8; alert(4); //输出12 var adder2=calFactory(1); alert (adder2(3)); //输出7 function calFactory(base){ return function (max){ var total=0; for (var i = 1 ; i <=max; i++) { total+=i; } return total+base; } }
3.封装
//立即调用的函数 (function(){ var m=0; function getM(){ return m; } funciton setM(){ m=val; } window.g=getM; window.f3()=setM; })(); f(12); alert(g()); //输出12
闭包的注意点:
一:对捕获的变量只是个引用,不是复制
function f(){ var num=1; function g(){ alert(num); } num++; g(); } f(); //输出2
//分析:
1.f调用生成一个f 的词法环境 le==g的[[scope]] 产生闭包 g函数没有真正的运行 num++;
2.f的词法环境是一个对象 (num就是一个成员) num的值就是f函数的词法环境
3.g的[[scope]]==f 的 le==num
所以就是对象的引用
二:父函数每调用一次,会产生不同的闭包
function f(){ var num=1; return function(){ num++; alert(num); } } var result1=f(); result1();//2 result1();//3 var result2=f(); result2();//2 result2(); //3 分析: 1.调用两次不同的 f() 就创建了不同的词法环境 所以:父函数没调用一次就会产生不同的闭包
三:循环中的问题
<div id="1">1</div> <div id="2">2</div> <div id="3">3</div> <script type="text/javascript"> for (var i = 1;i <=3; i++) { var ele=document.getElementById(i); ele.onclick=function(){ alert(i); } } //点击一直输出4 //解决方案 闭包 用匿名函数 for (var i = 1;i <=3; i++) { var ele=document.getElementById(i); ele.onclick= (function(id){ return function(){ alert(id); } })(i); }
对象的种类:
1. undefined
2.null
3.string
4.number
5.boolean
对象的类型:
1.js内置的
2.宿主环境(window)
3.自己创建
对象的创建:
1.对象自变量的创建
<script type="text/javascript"> var p={ name:“sunliyuan”, work:function(){ console.log("working..."); }, _age:18, get age(){ return this._aga; }, set age(val){ if(val<0||val>150){ throw new Error("invalid value"); }else{ this._aga=val; } }, address: }; console.log(p.name); </script>
又一访问种方式:
括号的访问:
级联访问实例:
通过Object对象:
自己给自己加上属性:
另一种加法:
还是100(不可修改)
//get,set,writable,enuerable,configurable,value Object.definePropertis(p,{ salary:{ value:1000, writable:false }, gender:{ value:true }, height:{ get:function(){ return 180 }, set:function(val){ console.log(val); } } });
输出的内容: