前端技术的发展变化太快了,各种各样的框架。再变也离不开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.οnclick=function(){
alert(i);
}
}
//点击一直输出4
//解决方案 闭包 用匿名函数
for (var i = 1;i <=3; i++) {
var ele=document.getElementById(i);
ele.οnclick= (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);
}
}
});
输出的内容: