天天看點

圖解 Google V8 # 02:函數即對象:一篇文章徹底搞懂 JavaScript 的函數特點

說明

圖解 Google V8 學習筆記

什麼是 JavaScript 中的對象?

JavaScript 是一門​

​基于對象 (Object-Based) ​

​的語言,可以說 JavaScript 中大部分的内容都是由對象構成的,諸如函數、數組,也可以說 JavaScript 是建立在對象之上的語言。

圖解 Google V8 # 02:函數即對象:一篇文章徹底搞懂 JavaScript 的函數特點

雖然 JavaScript 是基于對象設計的,但是它卻不是一門面向對象的語言 ​

​(Object—Oriented Programming Language)​

​,因為面向對象語言天生支援封裝、繼承、多态,但是 JavaScript 并沒有直接提供多态的支援,是以要在 JavaScript 中使用多态并不是一件容易的事。

JavaScript 中實作繼承的方式卻非常簡單:隻是在對象中添加了一個稱為原型的屬性,把繼承的對象通過原型連結起來,就實作了繼承,我們把這種繼承方式稱為基于原型鍊繼承。

JavaScript 中的對象非常簡單,每個對象就是由一組組屬性和值構成的集合

例子:

var person=new Object();
person.firstname="John";
person.lastname="Doe";
person.age=50;
person.eyecolor="blue";      

上面代碼對象裡面有四個屬性,用圖表示對象的構成:

圖解 Google V8 # 02:函數即對象:一篇文章徹底搞懂 JavaScript 的函數特點

例子2:

var person=new Object()
person.firstname="John"
person.lastname="Doe"
person.info = new Object()
person.info.age=50
person.info.eyecolor="blue"
person.showinfo = function (){
    console.log(/*...*/)
}      

上面代碼的記憶體布局,如圖:

圖解 Google V8 # 02:函數即對象:一篇文章徹底搞懂 JavaScript 的函數特點

對象的屬性值類型

1、原始類型 (primitive)

所謂的原始類的資料,是指值本身無法被改變,JavaScript 中的原始值主要包括 ​

​null、undefined、boolean、number、string、bigint、symbol​

​ 這七種。

2、對象類型 (Object)

對象的屬性值也可以是另外一個對象,比如上圖中的 info 屬性值就是一個對象。

3、函數類型 (Function)

如果對象中的屬性值是函數,那麼我們把這個屬性稱為方法,是以我們又說對象具備屬性和方法,比如上圖中的 showinfo 就是 person 對象的一個方法。

函數的本質

在 JavaScript 中,函數是一種特殊的對象,它和對象一樣可以擁有屬性和值,但是函數和普通對象不同的是,函數可以被調用。

例子:

function foo(){
    var test = 1
}
foo.myName = 1
foo.uName = 2
console.log(foo.myName)      

函數調用:

// 通過函數名稱加小括号來實作函數的調用
function foo(){
    var test = 1
    console.log(test)
}
foo()

// 調用一個匿名函數
(function (){
    var test = 1
    console.log(test)
})()      

V8 内部是怎麼實作函數可調用特性的呢?

在 V8 内部,會為函數對象添加了兩個隐藏屬性,具體屬性如下圖所示:

圖解 Google V8 # 02:函數即對象:一篇文章徹底搞懂 JavaScript 的函數特點

函數除了可以擁有常用類型的屬性值之外,還擁有兩個隐藏屬性,分别是 name 屬性和 code 屬性。

name 屬性

隐藏 name 屬性的值就是函數名稱,如果某個函數沒有設定函數名,該函數對象的預設的 name 屬性值就是 anonymous,表示該函數對象沒有被設定名稱。

code 屬性

隐藏屬性是 code 屬性,其值表示函數代碼,以字元串的形式存儲在記憶體中。

當執行到一個函數調用語句時,V8 便會從函數對象中取出 code 屬性值,也就是函數代碼,然後再解釋執行這段函數代碼。

函數是一等公民

如果某個程式設計語言的函數,可以和這個語言的資料類型做一樣的事情,我們就把這個語言中的函數稱為一等公民。

由于函數的​

​可被調用​

​的特性,使得實作函數的可指派、可傳參和可作為傳回值等特性變得有一點麻煩。為什麼?

為了實作變量的查找,V8 會為其維護一個作用域鍊,如果函數中使用了某個變量,但是在函數内部又沒有定義該變量,那麼函數就會沿着作用域鍊去外部的作用域中查找該變量,具體流程如下圖所示:

圖解 Google V8 # 02:函數即對象:一篇文章徹底搞懂 JavaScript 的函數特點

當函數内部引用了外部的變量時,使用這個函數進行指派、傳參或作為傳回值,你還需要保證這些被引用的外部變量是确定存在的,這就是讓函數作為一等公民麻煩的地方,因為虛拟機還需要處理函數引用的外部變量。

function foo(){
    var number = 1
    function bar(){
        number++
        console.log(number)
    }
    return bar
}
var mybar = foo()
mybar()      

繼續閱讀