函数声明和函数表达式的区别
//函数声明
function wscat(type){
return type="wscat";
}
//函数表达式
var oaoafly =function (type){
return type="oaoafly";
}
区别
- 函数声明不管在哪里定义,该函数都可以进行调用。而函数表达式的值必须是在函数表达式赋值完成后,该函数才能调用。
- 对于函数声明,js解析器会优先读取,确保在所有 代码执行前声明已经被解析。而函数表达式,如同其他基本类型的变量一样,只在执行到某一句的时候也会对其进行解析。
声明前置
变量声明前置
变量声明前置就是在一个作用域块中,所有的变量都被放在块的开始声明。
var a=1;
function main(){
console.log(a);
var a=2;
}
main()//输出undefined;
为什么会输出undefined呢?是因为脚本在运行的时候会自动将变量声明前置解析如下:
var a=1;
function main(){
var a;
console.log(a);
a=2;
}//所以,我们在写js的时候应该尽量将变量声明放在作用域的开始地方。
函数声明前置
使用function关键字就可以声明一个函数,它的特征是函数声明提升,执行代码前会先读取函数声明,即函数声明不必放在调用的前面。
var a=3;
console.log(a);
sayHello();
function sayHello (){
console.log("hello");
}
解析为
var a;
function sayHello(){}
然后:conslole.log(a);
a=3;
sayHello();
对于函数表达式,和var一个变量没什么区别。
arguments
可以在函数体内读取所有的参数,这就是argunments对象的由来。
arguments对象的使用
- 通过方括号语法访问每一个元素
var fun=function(one){
console.log(arguments[0]);
console.log(arguments[1]);
console.log(arguments[2]);
}
fun(1,2,3)
//1
//2
//3
- 通过length属性,查看到底要几个参数
function fun(){
return argunments.length;
}
fun(1,2,3,)
- 参数赋值(严格模式下不容许)
var fun=function (a,b){
argunments[1]=2;
return a+b;
}
fun(1,1)//3
重载
js没有重载,但是可以在js中实现重载的功能,即传递不同的实参,可以自动调用匹配。
function print(name,age,sex){
if(name){
console.log(name);
}
if(age){
console.log(age);
}
if(sex){
console.log(sex);
}
}
print("byyu",26)
立即执行函数
最常用的两种写法:
(function (){/*code*/}());
(function (){/*code*/})();
为什么第一种执行而不报错呢?因为在js里,括号内部不能包含语句,当解析器对代码进行解析的时候,先碰到了(),然后碰到function关键字就会自动将()里面的代码识别为函数表达式而不是函数声明。
作用
模拟块作用域
js只有函数作用域,在同时调用多个库的情况下,很容易造成对象或者变量的覆盖。
liba.js
var num=1;
libb.js
var num=2;
如果在页面上同时引用liba,js和liba,js两个库。必然导致num的覆盖,为此:
liba.js
(function(){
var num=1;
})();
libb,js
(function(){
var num=2;
})();
经过改造之后,两个库的代码就完全独立,互不影响了。
用递归实现 n!
function factor(n){
if (n==1){
return 1
}
return n*factor(n-1);
}
factor(100)
代码输出
function getInfo(name, age, sex){
console.log('name:',name);
console.log('age:', age);
console.log('sex:', sex);
console.log(arguments);
arguments[0] = 'valley';
console.log('name', name);
}
getInfo('饥人谷', 2, '男');//name:饥人谷,age:2,sex:男,['饥人谷',2,'男'],name:valley
getInfo('小谷', 3);//name:小谷,age:3,undefined,['小谷,'3'],name:valley;
getInfo('男');name:男,undefined,undefined,['男'],name:valley;
写一个函数,返回参数的平方和
function sumOfSquares(){
var result=0;
for(i=0;i<arguments.length;i++){
result=result+argument[i]+argument[i];
}
return result;
}
var demo=sumOfSquares(1,3,4);
console.log(demo);
代码输出(变量提升)
console.log(a);
var a = 1;
console.log(b);
console.log(a)
输出undefined;因为a变量提升,相当于
var a;
console.log(a);
只声明了没有赋值,为undefined。
console.log(b)会报错,因为既没有声明也没有赋值。
代码输出(函数声明前置)
sayName('world');
sayAge(10);
function sayName(name){
console.log('hello ', name);
}
var sayAge = function(age){
console.log(age);
};
sayName函数会输出'hello''world',sayAge会报错
原因:第一个是函数声明,会函数声明前置,所以函数声明不必放在调用的前面。而第二个是函数表达式,相当于普通变量,所以函数调用必须放在函数声明的后面,否则会报错。
作用域链(查找过程伪代码)
var x = 10
bar()
function foo() {
console.log(x)
}
function bar(){
var x = 30
foo()
}//输出10
原因:
globalcontext={
Ao{ x:10;
foo:function;
bar:function;}
scope:null;
}}
foo.[[scope]]=globalcontext.Ao;
bar.[[scope]]=globalcontext.Ao;
barcontext={
Ao{
x:30;}
scope:globalcontext.Ao;
}
foocontext={
Ao{};
scope:globalcontext.Ao;
}//调用foo时候会先从Ao里面找,Ao里面为空,会向globalcontext.Ao里面找,找到x=10,所以会打印出10。
作用域链(查找过程伪代码)
var x = 10;
bar()
function bar(){
var x = 30;
function foo(){
console.log(x)
}
foo();
}
过程:
1. globalcontext={
Ao:{
x:10;
bar: function;
}
scope:null;
}
bar.[[scope]]=globalcontext.Ao;
2. barcontext={
Ao:{
x:30;
foo:function;
}
scope:bar.[[scope]]=globalcontext.Ao;
}
foo.[[scope]]=barcontext.Ao;
3. foocontext={
Ao:{};
scope:foo.[[scope]]=barcontext.Ao;
}//输出30.调用foo()的时候,会在foo的Ao中查找,可见里面没有。所以会在scope里面找,barcontext.Ao里面有x=30;所以输出的结果为30。
查找过程的伪代码(立即执行函数)
var x = 10;
bar()
function bar(){
var x = 30;
(function (){
console.log(x)
})()//等价于function foo(){console.log(x);}
}
过程:
1. globalcontext={
Ao:{
x:10;
bar:function;
}
scope:null;
}
bar.[[scope]]=globalcontext.Ao;
2. barcontext={
Ao:{
x:30;
foo:function;
}
scope:globalcontext.Ao;
}
foocontext.[[scope]]=barcontext.Ao;
3. foocontext={
Ao:{};
scope:barcontext.Ao;
}//调用foo()函数时,Ao中没有值。所以在scope:barcontext.Ao查找;其中中x=30,所以输出30
作用域链(查找过程伪代码)
var a = 1;
function fn(){
console.log(a)//输出undefined;因为变量声明前置var a
var a = 5
console.log(a)输出5,因为a=5
a++
var a
fn3()
fn2()
console.log(a)//输出6,因为a++,a为6
function fn2(){
console.log(a)//输出20,因为fn2.[[scope]]=fncontext.Ao;并且a=20,将
fncontext.Ao中的a=6改为20;
a = 20
}
}
function fn3(){
console.log(a)//输出200,因为fn3中的AO为空,所以会在
globalcontext.Ao;中找,又因为a=200,将a=1改为200,所以a=200
a = 200
}
fn()
console.log(a)
过程:
globalcontext={
Ao:{
a:1;
fn3:function;
fn:function;
}
scope:null;
}
fn3.[[scope]]=globalcontext.Ao;
fn.[[scope]]=globalcontext.Ao;
fncontext={
Ao:{
a:6;
fn2:function;
scope:globalcontext.Ao;
}
fn2.[[scope]]=fncontext.Ao;
}
fn3context={
Ao:{};
scope:globalcontext.Ao;
}
fn2context={
Ao:{};
scope:fncontext.Ao;
}