首先切记一句话:函数也是对象
创建函数的几种方式:
1.函数声明:
// function 函数名(){// }例:function fn(){}
2.函数表达式:
// var 函数名 = function(){// }例:var fn = function(){}
3.构造函数创建:
// var 函数名 = new function(){// }例:var fn = new function(){}
函数声明和函数表达式的区别:
1.函数声明调用时可以在任意可见位置调用,不管是在函数声明前或函数声明后,函数声明还可以预解析(预加载),还会有函数提升的作用。
2.函数表达式必须先定义,后调用。
上面提到了预解析,就说一下预解析:
所谓预解析,字面意思就是提前解析代码。就是在当前作用域中,JavaScript代码执行之前,浏览器首先会默认的把所有带var和function声明的变量进行提前的声明或者定义。
举个例子:
var a = 10
这行代码告诉我们浏览器在全局作用域中有一个a的变量了,如果一个变量只是声明了,但是没有赋值,默认就是undefined, a=10,意思就是给变量进行赋值,a的值是10。
var声明的变量和function声明的函数在预解析的时候有区别,var声明的变量在预解析的时候只是提前的声明,function声明的函数在预解析的时候回提前声明并且会同时定义,也就是说var声明的变量和function声明的函数的区别是在声明的同时没有同时进行定义。
预解析只发生在当前的作用域下,程序最开始的时候,只对window渲的变量和函数进行预解析,只有函数执行的时候才会对函数中的变量和函数进行预解析。
看代码:
console.log(a)var a = 10;console.log(a)fn(10,20)function fn(m,n){var total = m+nconsole.log(total)}
然后看输出结果:
第一次输出a的时候,由于预解析的原因,只声明了还没有定义,所以会输出undefined;第二次输出a的时候,已经定义了,所以输出10。
由于函数的声明和定义是同时进行的,所以fn()虽然是在fn函数定义声明处之前调用的,但是依然可以正常的调用,会正常输出30。
提到预解析,我有想起了作用域: 作用域分 全局作用域 局部作用域 和 块级作用域
全局作用域:
var a = 5;function fn() {b = 10;console.log(a);}console.log(a);console.log(b);
1.定义在全局(window)下的变量拥有全局作用域;
2.所有末定义直接赋值的变量自动声明,并拥有全局作用域;
3.所有window对象上的属性,具有全局作用域。
局部作用域:
function fn() {var b = 10;console.log(b);}console.log(b);
局部作用域一般只在固定的代码片段内可访问到,最常见的例如函数内部;
块级作用域其实也可以算一种局部作用域。
块级作用域:
所声明的变量在指定块的作用域外无法被访问;
在一个函数内部;
在一个代码块(由一对花括号包裹)内部。
let arr = [1, 2, 3, 4, 5];for(let i in arr) {console.log(i); // 1 2 3 4 5}console.log(i); // i is not defined// 对于for循环,圆括号内与花括号内是两个作用域。for(let j in arr) {let j = \'1\';console.log(j); // 1* 5}
说到作用域我又想起来作用域链 什么是作用域链?
当我们在局部/块级作用域中,调用一个外部的变量时,就会产生作用域链。
当我们在一个函数内部访问当前作用域内不存在的变量时,就会逐层向外查找,如果一直没有找到就会报错。
对于函数而言,函数在哪里创建,它就会从哪里开始向上查找变量;而不是函数在哪里调用。
最常见的就是闭包,通过闭包定义的函数来访问函数的内部变量。