定义一个函数的方法主要有三种 函数声明、函数表达式、new Function
构造函数,函数声明与函数表达式定义的函数较为常用,构造函数的方式可以将字符串定义为函数
函数声明会将声明与赋值都提前,也就是整个函数体都会被提升到作用域顶部
s(); // 1
function s(){
console.log(1);
}
也就是说,在某个作用域中定义的函数可以在这个作用域的任意位置调用,整个函数体都将被提前,当然也有不一样的情况:
console.log(s); // undefined // 函数的声明提升但是并未赋值函数体
console.log(ss); // Uncaught ReferenceError: ss is not defined // 打印未定义的ss是为了对比说明函数的声明提升
s(); // Uncaught TypeError: s is not a function
if(1){
function s(){
console.log(1);
}
}
此处可以看到函数的声明被提升,但是函数体并未被提升,JS
只有函数作用域与全局作用域以及ES6
引入的let
和const
块级作用域,此处if
不存在作用域的问题,为同一作用域,但是由于解释器在预处理期无法确定if
里面的内容,所以只对函数声明的变量进行了提升,而并未将函数体赋值。
函数表达式只会提升变量的声明,本质上是变量提升并将一个匿名函数对象赋值给变量,
console.log(s); // undefined
var s = function s(){
console.log(1);
}
console.log(s); // f s(){console.log(1);}
在JS引擎的执行的优先级是 变量声明 函数声明 变量赋值
console.log(s); // ƒ s(){console.log(1);}
var s = function(){
console.log(0);
}
function s(){
console.log(1);
}
s(); // 0
new Function
的方式可以将字符串解析为函数
var s = new Function("a","b","console.log(a,b);");
s(1,2); // 1,2
console.log(s.__proto__ === Function.prototype); //true
function ss(){} // 声明的函数都是Function的一个实例
console.log(ss.constructor === Function); // true
console.log(ss.__proto__ === Function.prototype); // true
new Function
与eval
也有所不同,其对解析内容的运行环境的判定有所不同
// eval中的代码执行时的作用域为当前作用域,它可以访问到函数中的局部变量,并沿着作用域链向上查找。
// new Function中的代码执行时的作用域为全局作用域,无法访问函数内的局部变量。
var a = 'Global Scope';
(function b(){
var a = 'Local Scope';
eval("console.log(a)"); //Local Scope
(new Function("console.log(a)"))() //Global Scope
})();