函数提升
用function关键字声明定义的函数,在js代码执行前会被扫描并提升到最顶部,因此这样的函数可以在任何位置使用而不需要考虑书写定义的顺序前后:
ok=myfun("lz","hsb");
console.log(ok);
function myfun(x,y){
return x+y;
}
输出
lzhsb
而如果是用字面量匿名函数赋值的定义方式,不会引起函数提升:
console.log(myfun); //连这个函数都未定义,更别提调用它了
var myfun=function(x,y){
return x+y;
};
输出
undefined
变量提升
注意,如果一个变量根本不存在,那么尝试直接使用它(除了对它赋值)时会直接报错,只有声明过了的变量才能使用,只是不作初始化或赋值时这个变量的值是undefined。
变量提升,是指js扫描代码并把所有变量声明提升到其作用域的最顶部,自然全局变量、局部变量、块级变量的”最顶部”都是不一样的。如:
console.log(x); //因为变量提升而可以使用之,虽然是undefined但确实已经声明了
var x=10; //变量初始化
console.log(x);
myfun(1,3); //因为函数提升而可以把使用写在声明之前
function myfun(a,b){
console.log(c); //因为变量提升而可以使用之,即便变量的声明写在return之后!
return c;
var c=a+b;
}
输出
undefined
10
undefined
上面的代码相当于:
var x; //全局变量x提升到最前面
//全局函数myfun提升到最前面
function myfun(a,b){
var c; //局部变量c提升到函数体最前面
console.log(c);
return c;
c=a+b; //初始化变成了赋值,这部分代码在return后,永远不被执行
}
console.log(x);
x=10; //初始化变成了赋值
console.log(x);
myfun(1,3);
异常抛掷和捕获
在try块中执行可能发生异常的代码,即这块代码执行过程中可能存在throw关键字抛掷异常信息。对于捕获到的异常,在catch块中执行当捕获到异常时进行的行为,在js中异常对象本身即是throw抛掷出的信息。在finally块中执行无论是否发生异常都做处理的代码。
<!DOCTYPE HTML>
<HTML>
<BODY>
</BODY>
<HEAD>
<meta charset="utf-8">
<script>
try{
var a=myfun(3.14,"lzhsb");
}catch(e){
console.log("发生异常:"+e);
}finally{
console.log("a="+a);
}
function myfun(x,y){
if(typeof x!=="number")
throw "x不是一个数字!";
else if(typeof y!=="number")
throw "y不是一个数字!";
console.log("这里执行了吗");
return x+y;
}
</script>
</HEAD>
</HTML>
输出
发生异常:y不是一个数字!
a=undefined
throw发生时,后续的代码都不会继续执行,而是转去执行catch块中的代码。因此这个例子中使用myfun函数发生异常后并没有返回x+y,全局变量a的值是初始值undefined。
ES6的Map
Map是ES6规范下的新数据类型,老版本的浏览器不支持,还是用我的新火狐试一下:
var a=new Map();
undefined
typeof a;
"object"
a instanceof Map;
true
可以看到它是一种新的object子类型,但是同样是键值对系,Map和Object是不同的。
Object通过属性来访问属性值,而Map是通过访问这个Map对象的get方法(方法内可能是去搜索一棵树),在键值对很多时Map的速度会快很多。
Map对象的使用
a.set("lzh","sb");
Map { lzh → "sb" }
a.set("age",20);
Map { lzh → "sb", age → 20 }
a.has("age");
true
a.delete("lzh");
true
a;
Map { age → 20 }
a.clear();
undefined
a;
Map { }
相比Object还有一个优势是,Object定位是一个对象,对象的键都是”属性”即”属性名”,即Object对象的key只能是字符串(因此才可以省略引号),而Map的key可以是任何数据类型:
var a=new Map();
undefined
a.set(undefined,"ok");
Map { undefined → "ok" }
a.set(null,666);
Map { undefined → "ok", null → 666 }
a.set(new Array(1,"lzh"),"sb");
Map { undefined → "ok", null → 666, […] → "sb" }
不过用这样的匿名object对象做key,就访问不到它了:
a.get([1,"lzh"]);
undefined
所以要让object类对象成为key,还是自己手里要留一个引用的:
var b=new Array(1,"eee");
undefined
a.set(b,"sbsb");
Map { undefined → "ok", null → 666, […] → "sb", […] → "sbsb" }
a.get(b);
"sbsb"
反观Object对象
Object的key只能是字符串,就算尝试投入一个变量,也会被加上引号解析成字符串,这点要格外小心:
var a={ok:1};
undefined
a;
Object { ok: 1 }
var ok=66;
undefined
var a={ok:1};
undefined
a;
Object { ok: 1 }
就算尝试投入一个数字,也会被加上引号解析成字符串的:
var a={666:777};
undefined
a;
Object { 666: 777 }
a[666];
777
a['666'];
777
因为对象的key的定位”属性”指的就是”属性名”嘛。
ES6的Set
ES6规范下的Set对象指的就是一个集合,即具有确定性,互异性,无序性:
var a=new Set();
undefined
typeof a;
"object"
a instanceof Set;
true
可以看到Set也属于Object对象,可以理解成是只有不重复的key而没有value值(key天生就不能重复,符合集合的互异性)。
Set对象的使用
var a=new Set();
undefined
a.add(1);
Set [ 1 ]
a.add('1');
Set [ 1, "1" ]
a.add(1);
Set [ 1, "1" ]
a.delete(1);
true
a;
Set [ "1" ]
使用Set去重
机器学习白皮书中提出了python里给一个可迭代对象去重的最快的方式就是转换成Set,在js里也可以这样做,不过似乎不能用Set做强制类型转换,需要传给Set的构造器去构造一个Set出来:
var a=new Array(1,2,3,3,4);
undefined
a;
Array [ 1, 2, 3, 3, 4 ]
b=Set(a);
TypeError: calling a builtin Set constructor without new is forbidden debugger eval code:1:3
b=new Set(a);
Set [ 1, 2, 3, 4 ]