隐藏内部实现
对函数的传统认知就是先声明一个函数,在向里面添加代码。但反过来想也可以带来一些启示,从所写的代码中挑出来一个任意的片段,然后用函数声明对他进行包装,实际上就是把这些代码隐藏起来了。
在软件设计中应该最小限度的暴露内容,一些模块和对象API的设计也都应该遵循此原则。
function doSomething(a){
function otherthing(a){
return a+1;
}
var b=a+otherthing(a*2);
console.log(b*2);
}
doSomething(2); //14
这样b和otherthing方法在doSomething内部隐藏了,外部无法访问它们,只能被doSomething所控制,设计上讲具体内容私有化了。
立即执行函数
像上面的代码内部函数otherthing这个标识符有时会污染所在的作用域(以上为doSomething作用域)而且还得再所在的作用域显示的通过函数名调用它,我们来看如下代码
var a=3;
(function foo(){
var a=2;
console.log(a); //2
})();
console.log(a); //3
由于函数被包含在了一对()中,因此就成了一个表达式,通过在末尾在加一个()就可以立即执行它,就好像这样
function foo(){
//...
}
foo();
//简写为这样
(function foo(){
//...
})()
在javascript中这种形式应该很常见了,也有这种形式的(function(){//...}())
值得一提的是上面这个形式也省略了函数名,也看起来更简单了,所以也值得鼓励这样写,(function(){//...})()
这种和上面两种形式在功能上是一致的,你可以随意按爱好选择。有时也可以把window穿进去,这样我们就可以访问全局作用域的变量和函数了。
块作用域
javascript与其他语言在块作用域也有很大的不同,而javascript根本没有块作用域。
for(var i=1;i<10;i++){
a=1;
console.log(i);
}
console.log(a);//1
可以看到最后的打印a也正常打印了1,也就是说{//…}没有起作用,外界可以访问到内部的变量a(当然也可以访问i了),这就是javascript没有块作用域的体现。不过在js中也有些东西可以生成块作用域的,例如with和try/catch。
javascript中这种奇怪的行为在ES6改变了,有一个新的关键词let
,另一种变量声明的方式,这种方式可以把变量绑定在所在的{//。。。},而外界无法访问它。
for(let i;i<10;i++){
console.log(i) //正常输出
}
console.log(i); //ReferenceError
{//…}外面的console语句访问不到里面的i了。
另外还有ES6引入了const,同样可以用来创建块作用域变量,但其值都是固定的,任何想修改这个变量的值都会报出错误。
声明提升
来看如下的代码:
a=2;
var a;
console.log(a);
console.log(..)会输出什么呢?答案是2。在看下面地代码:
console.log(a);
var a=2;
这次又会输出什么呢。答案是undefined。可能跟你想的很有差别吧,这里就涉及到了变量声明提升,在我的一篇文章里有提到过(作用域与js编译原理)编译器在javascript中工作的时候先是找出所有的声明,然后在关联他们提交给引擎执行代码,话句话说在js中包括变量和函数在内的所有声明都会在任何代码被执行前首先被处理。在看上面的第一段代码执行的时候其实是这样:
var a;
a=2;
console.log(a) //自然是2了
在看第二段代码:
var a;
console.log(a);
a=2;
先声明,所以console语句在对a进行RHS查询的时候,会找到a,但没有值,所以就输出undefined。
函数声明foo();function foo(){//...}
也会发生声明提升,但是函数表达式不会提升,像这样var foo=function(){//...};
不会发生声明提升。
在编译器工作的时候函数声明优先被提到最高处。
在ES6中的新关键词let和const声明方式不会有声明提升,也就是使得程序猿能够规范的编程~