第二篇《Google V8 原理》之 函数声明&原型链&作用域

167 阅读2分钟

函数声明和函数表达式

函数声明:结果 正常打印

    foo()
    function foo() {
        console.log('foo')
    }

函数表达式:结果 报错,foo is not a function

foo()
    var foo = function () {
        console.log('foo')
    }

原理解析

image.png

image.png

作用域中包含了变量 x 和 foo,变量 x 的默认值是 undefined,变量 foo 指向了 foo 函数对象,foo 函数对象被 V8 存放在内存中的堆空间了,这些变量都是在编译阶段被装进作用域中的。 在执行之前,这些变量都被提升到作用域中了,所以在执行阶段,V8 当然就能获取到所有的定义变量了。我们把这种在编译阶段,将所有的变量提升到作用域的过程称为变量提升

原型链

什么是继承?

简单地理解,继承就是一个对象可以访问另外一个对象中的属性和方法

原型继承如何实现?

当前对象通过隐藏属性_proto_属性指向了另外一个对象。

image.png

当访问对象C.color属性时,V8会优先查找C对象本身的属性,如果没有color属性会查找当前对象的原型链。 继承的概念:继承就是一个对象可以访问另外一个对象中的属性和方法,在JavaScript 中,我们通过原型和原型链的方式来实现了继承特性。

__proto__隐式原型)与prototype(显式原型)

f.__proto__ === f.prototype //true

作用域链

全局作用域和函数作用域类似,也是存放变量和函数的地方,但是它们还是有点不一样:全局作用域是在 V8 启动过程中就创建了,且一直保存在内存中不会被销毁的,直至 V8 退出。而函数作用域是在执行该函数时创建的,当函数执行结束之后,函数作用域就随之被销毁掉了。

作用域链是怎么工作的?

尝试打印这段代码

var name = 'juejin';
var type = 'global';

function foo () {
    var name = 'foo'
    console.log(name)
    console.log(type)
}

function bar () {
    var name = 'bar';
    var type = 'function'
    foo()
}

编译过程如下图所示 image.png

foo 函数查找变量的路径到底是什么?

沿着 foo 函数作用域–>bar 函数作用域–> 全局作用域 ?

还是,沿着 foo 函数作用域—> 全局作用域?

结论

JavaScript 是基于词法作用域的,词法作用域就是指,查找作用域的顺序是按照函数定义时的位置来决定的。bar 和 foo 函数的外部代码都是全局代码,所以无论你是在 bar函数中查找变量,还是在 foo 函数中查找变量,其查找顺序都是按照当前函数作用域–> 全局作用域这个路径来的