JS Function 原理

53 阅读2分钟

函数首先是对象

函数是Function构造函数的实例,也就是说,如果我们把function当做对象用也是能够出发对象的优化机制的。

  • prototype 指向 原型对象
  • proto 指向 Function.prototype

声明式函数

function outer() {
  let x = 10;
  return function inner() { return x; }; // 闭包保留对x的引用
}
  1. 捕获当前的词法环境,形成闭包
  2. 通过作用域链访问外部元素直到顶部
  3. 函数的生命在执行前被提升到作用域顶部(hoisting)

函数提升

这里的提升其实是因为JS的执行过程分为:

声明 - 执行 - 回收

每一个作用域下都是先声明再回收

所以对于下面这种情况JS的执行顺序就会是:

myFunc(); // 正常执行
function myFunc() {} 
  1. 声明 myFunc() 但是未赋值, 执行当前作用域,进入myFunc
  2. 声明 myFunc 作用域内容,执行 Func 作用域
  • 还有一种说法是函数声明提升了函数定义和函数体,这样理解我认为也没有问题。

对于 var, 也是一样的,在声明阶段被声明,但是没有初始化,所以是 undefined

变量不提升

对于使用 let const 声明的变量,在执行到声明语句之前,都会处于暂时性死区。所以下面的函数声明方式不能提升:

myFunc()
 // ReferenceError cannot access xx before initialization
let myFunc = function(){}

优先级

不同的声明方式之间有优先级:函数声明 > 变量声明

注意这里的优先级是值声明的时机优先级,而不是生效优先级

foo(); 
// TypeError: foo is not a function(表达式未提升函数体)
var foo = function() { console.log(1); };
function foo() { console.log(2); }
foo(); // 1(覆盖函数声明)

所以对于上面这种情况,function 声明的 foo 反而会被 var 声明覆盖,导致无法访问

构造式函数

let x = 10 
const dynamicFunc = new Function('return x') // 不能访问到 10

使用 Function 解析字符串生成的变量只能在全局作用域中执行,但是可以使用 with(this)绑定到当前实例。Vue2就是这么做的

注意,使用 Function 解析字符串的开销是很大的

箭头函数

没有独立的this和arguments,没有prototype,不能作为构造函数使用

笔者才疏学浅,各位读者多多担待,不吝赐教。