又來到了週末,小匹夫也終於有了喘口氣寫寫博客的時間和精力。話說週五的下午,小匹夫偶然間晃了一眼遊戲蠻牛Unity3D的QQ羣,又看到了一個Unity3D開發中老生長談的問題,「個人開發語言到底是選擇JavaScript呢?仍是C#呢?」。對這個問題,小匹夫也以爲的確該認真的梳理一下了。那麼爲什麼說JavaScript和C#的爭論根本就不存在呢?首先,咱們要知道Unity3D中的Js腳本到底是什麼?最準確的學名,我想應該叫作UnityScript (由於Unity-Technologies在github上託管的代碼就叫這個名字。。。)。那爲什麼UnityScript不是JavaScript呢?咱們就繼續分析一下UnityScript vs JavaScript好了。既然小匹夫說UnityScript不是JavaScript,但Unity3D的確有好幾種腳本語言啊(C#,UnityScript, Boo),那和C#相比,咱們應該如何抉擇呢?因此最後小匹夫會分析一下C# vs UnityScript。javascript
小匹夫的上一篇文章《Mono爲什麼能跨平臺?聊聊CIL(MSIL)》中介紹了一下Unity3D跨平臺的基礎。那就是藉助了CIL,首先將源代碼(例如C#)編譯成CIL(實際上是CIL assembly),以後再經過JIT或者是FULL-AOT將CIL編譯成目標平臺的原生代碼,進而實現了一套代碼,多個平臺使用的跨平臺功能。因此能夠想見,做爲Unity3D中的「JS"(以後我將使用UnityScript來稱呼)一樣也會先被編譯成CIL代碼,以後再編譯成對應平臺的原生代碼。換言之,Unity3D實現了一套在.NET平臺上和C#處於」相同層面「的語言UnityScript。開發了一套本身的語言,這聽上去簡直太瘋狂了是嗎?其實一點也不。剛纔說了,源代碼都要先編譯成CIL,因此只要能有一套編譯器可以把UnityScript的語法分析識別並編譯成CIL,那問題不就解決了嗎?固然,若是有現成的,能符合如下三條的語言做爲參考,那UnityScirpt的開發者應該是感激涕零了吧。哪三點呢?html
是.Net層面的語言。java
像腳本語言git
有編譯器能把它編譯成CILgithub
沒錯,聰明的看官你必定想到了,那位一直隱藏的很深的幕後人物,Unity3D腳本三巨頭之一的Boo了吧。都說Boo是Unity3D得腳本語言之一,但是爲啥就沒見有什麼人用過呢?緣由之一可能就是Unity3D最初之因此要引入Boo,純粹是爲了UnityScript服務的。Boo做爲一個.NET平臺的第三方語言,寫起來也很像腳本語言,而且有對應的編譯器可以實現從Boo到CIL的過程。Boo寫起來是像這個樣子的:(更多關於Boo的內容請參見概覽CLI之上的新語言——Boo)c#
import System import System.Net import System.Threading url, local = argv client = WebClient() call = client.DownloadFile.BeginInvoke(url, local) while not call.IsCompleted: Console.Write(".") Thread.Sleep(50ms) Console.WriteLine()
簡而言之,UnityScript是脫胎於Boo的。雖然看上去UnityScirpt和Boo長的不像,但要明白語法不是問題,語義纔是。只要能把UnityScript的語法解析成Boo的編譯器能認識的,那麼UnityScript的編譯器的大部分工做就能夠交給Boo的編譯器來實現了。因此,基於現成的Boo語言,開發UnityScript編譯器的工做就只剩下語法解析而已了,事實上他們也的確是這樣作的。app
而當咱們來到UnityScript在github的託管頁面,發現居然是Boo的開發者在維護,而UnityScript的編譯處理邏輯所在的文件是一個boo文件--UnityScriptCompiler.boo。this
雖然不懂boo語言,可是仍是能夠看到會引入boo的編譯器:url
import Boo.Lang.Compiler
也會有針對UnityScript的語法進行解析以供Boo的編譯器識別的過程:spa
pipeline.Insert(0, UnityScript.Steps.PreProcess()) pipeline.Replace(Boo.Lang.Compiler.Steps.Parsing, UnityScript.Steps.Parse()) pipeline.Replace(Boo.Lang.Compiler.Steps.IntroduceGlobalNamespaces, UnityScript.Steps.IntroduceUnityGlobalNamespaces()) pipeline.InsertAfter(Boo.Lang.Compiler.Steps.PreErrorChecking, UnityScript.Steps.ApplySemantics()) pipeline.InsertAfter(UnityScript.Steps.ApplySemantics, UnityScript.Steps.ApplyDefaultVisibility()) pipeline.InsertBefore(Boo.Lang.Compiler.Steps.ExpandDuckTypedExpressions, UnityScript.Steps.ProcessAssignmentToDuckMembers()) pipeline.Replace(Boo.Lang.Compiler.Steps.ProcessMethodBodiesWithDuckTyping, UnityScript.Steps.ProcessUnityScriptMethods())
好吧,我纔不會告訴你在同一個github頁面關於UnityScript的介紹是:
A JavaScript implementation based on the Boo programming language.
那麼UnityScript的編譯器到底放在哪裏呢?(從UnityScript到CIL)請看下圖:
Mac版的路徑:/Applications/Unity/Unity.app/Contents/Frameworks/Mono/lib/mono/unity/us.exe
Win版的路徑:U3D路徑/Editor/Data/Mono/lib/mono/unity/us.exe
說了這麼多,仍是須要爲咱們這篇文章的主角UnityScript正個名:是靜態語言且須要編譯,脫胎於Boo語言,和JavaScript除了後綴以外沒有關係。因此與其糾結「U3D的javascript到底和javascript有多形似」,倒不如考慮下UnityScript和Boo有多麼神似。
不少提出小匹夫在前言中提到的那個老生常談問題的盆油,每每認爲UnityScript和咱們平時所說的JavaScript是同樣的,即便有人意識到有所區別,可是認爲本質上也仍是和JavaScript屬於同一範疇。期初小匹夫也是這樣認爲的,由於曾經使用過cocos2d-js開發遊戲的緣故,因此當據說Unity3D他喵的居然支持JS腳本,那叫一個開心啊。但是最初的開心每每也暗示着最後的失望。
由於最後小匹夫意識到了UnityScript和JavaScript是兩種差異很大的語言。
其實所謂的JavaScript是一個很泛泛的名字,能夠用來指任何實現了ECMAScript標準的語言(求JS大神輕噴。。。),而UnityScript主觀上根本就沒有試圖去實現甚至是接近該標準。因此不少JavaScript的庫若是隻是單純的拷貝粘貼,在Unity3D中並不會運行的特別」順利「(之因此加引號是由於不少都運行不起來吧)。
那麼二者究竟有多大的差異呢?最主要和本質的區別前文小匹夫已經說過了,它倆除了長相差很少,壓根就沒啥聯繫。可是仍是須要有個更直觀的認識。那麼小匹夫就簡單總結一下,JavaScript和UnityScrip的差異吧。
JavaScript沒有類的概念。由於它是一種基於原型的語言,繼承發生在對象和對象之間(更靈活,換言之,更動態),而非類和類之間。
舉個例子:
function Person() { this.name = ["chenjiadong"]; } var c = new Person(); console.log(typeof c.introduce); Person.prototype.introduce = function() { console.log("I am "+this.name+"."); }; console.log(typeof c.introduce); c.introduce();
如你所見,在匹夫有限的js知識中,一樣存在着對象和原型的概念。經過關鍵字new,咱們能夠用方法Person建立出一個對象c,此時的小c仍是很單純的,不會introduce本身。可是若是Person通過進化,學會了介紹本身introduce,那麼以前建立的小c也一樣學會了介紹本身introduce。
可是在UnityScript中可不能這樣,由於它有類的概念。你一旦定義了你的類,那麼在程序運行時這個類也就不能改動了。
不知道各位盆油有木有發現,一個新建的UnityScript文件中好像並無聲明一個類。相反,看上去很像通常的javascript的寫法。好比這樣Test.js:
#pragma strict function Start () { } function Update () { }
其實緣由就在於文件名了。U3D中大多數.js文件都表明一個類,因此天然而然的,文件的名字就被用來稱呼這個類了。(固然說大多數文件都是這樣的含義就是,你也能夠在一個文件中定義多個類,好比不繼承自MonoBehav的類)。
因此上面的代碼就等效於這樣的C#代碼:
using UnityEngine; public class Test : MonoBehaviour { void Start () { } // Update is called once per frame void Update () { } }
反觀JavaScript,則僅僅是執行文件中的代碼,而和文件名無關。
除了這兩條比較「宏觀」的差異以外,語法上是否還有什麼不一樣呢?有啊~看官,您繼續瞧好兒咯~
在javascript中,常常能夠這麼幹:
var x = 1, y = 2;
但在UnityScript中這樣寫,U3D一般會這麼說
之前記得在微博上看到過一個大學教授吐槽過一個javascript的「bug」,如圖:(小匹夫居然憑藉記憶力找到那條去年的微博...求贊)
因此,讓大V掉坑裏的,javascript的特性之一,就是程序在執行時會默認在行尾加「;」,換句話說,做爲人類或者猿類的咱們能夠不在行尾寫分號。
而UnityScript爲了不這種極可能形成bug的寫法,對分號作出了要求。接下來不用我多說了吧。
反正你不寫分號,U3D大概會這麼說:
實話實說,小匹夫也不知道怎麼想的這麼一繞口的小標題。可是小匹夫記得在javascript中能夠這麼寫:
var x = 3; // x is 3 var y = (x=x+2); // x is 5, y is 5
因此這裏,x=x+2 這個賦值表達式做爲一個表達式給y賦值。
可是在UnityScript中一樣是不支持這種寫法的。此次U3D會和你嘰歪不少話。
聊javascript終究繞不過去的一個話題,那就是聲明變量時的var。假設你聲明變量時,不帶var,則該變量會默認成爲全局變量。可是聲明變量不帶個var,在UnityScript中可行不通。
好吧,那妥協之,在UnityScript中我帶var還不行嘛?那是否是就能夠像JavaScript同樣,在類型上無拘無束任我飛了?
好比下面的代碼,我在javascript上和unityscript上都運行的很完美啊。
//UnityScript 和 JavaScript都能運行哎~ var x; x = 3; x = new Array();
這樣看起來,貌似UnityScript是動態語言啊!簡直是視類型如無物啊。好多盆油到這裏就迷糊了,接着就產生了那樣的錯覺。
其實做爲靜態語言,在編譯器進行編譯時,變量究竟是什麼類型它可得門清,因此在UnityScript中,上面代碼裏的x究竟是什麼類型但是肯定好的。 以爲奇怪是嗎?那咱們把等效的代碼寫一下各位看官就明白了。
//在unityscript中等效於下面 Object x; x = 3; x = new Array();
對,原來x是Object類型。全部的類型都派生自它,換言之,全部的類型都能轉化成它。因此3能轉化爲Object,Array也能轉化成Object。這也就是UnityScript看起來是動態類型的緣由。(頻繁裝箱,確定會影響效率)
因此你若是調用一個Object沒有的方法,可能會有這樣的提示:
固然,關於var的話題還有不少,好比C#也有,可是和這裏並不是徹底相同。一樣,javascript和unityscript能夠聊的話題也還有不少,可是受限於篇幅,這裏點到即止。
終於來到我們常常爭論的火藥桶話題了,到底C#和Unity3D裏的JS誰好呢?
最多見的問題無非是:_用js寫的u3d遊戲和用c#寫的u3d遊戲,到底誰的運行效率高啊?_
最多見的回答爲非是:_確定是C#啊,由於js是動態的。確定不如編譯的語言好。_
第二常見的問題無非是:_用js開發和用c#開發,哪一個更快更適合我啊?_
第二常見的回答無非是:_js適合我的開發,敏捷快速。c#適合公司開發,規範嚴謹。_
我們仍是用和剛纔討論與javascript的區別時同樣的思路來整理C#和UnityScript的不一樣,也就是按照先本質,再表現的順序。同時兼顧回答一下上面的兩個問題。
開篇就說了,UnityScript是和C#同一個層面的語言,也須要經歷從源代碼到CIL中間語言過渡,最終到編譯成原生語言的過程。因此本質上,最終運行的都是從CIL編譯而來的原生機器語言。但的確會有C#比較快的現象,那麼問題出在哪呢?
一個可能但不是惟一的答案就是UnityScript和C#生成CIL中間語言不一樣。
這一點想一想也很簡單,就像上文提到的var的問題,若是使用Object來處理var的問題,則不可避免的是頻繁的裝箱拆箱的操做,這對效率的影響是很大的。
因此的確,C#的速度更快,但緣由是UnityScript會涉及到頻繁的裝箱拆箱操做,進而生成的CIL代碼與C#有差別,而並不是UnityScript是動態語言且沒有通過編譯。
開發究竟是使用C#仍是UnityScript呢?若是不考慮運行的效率,僅僅考慮開發時候的感覺,小匹夫就談談本身的見解好啦——那就是珍惜時間,遠離UnityScript。
首先有幾個事實咱們要清楚:
UnityScript是脫胎於.NET平臺的第三方語言Boo的。所謂的第三方語言和C#的區別,就跟本身究竟是不是親生的,爹究竟是不是隔壁老王是同樣的。差距多是全方位,立體式的。社區支持,代碼維護,甚至是編譯出來的CIL代碼質量均可能有很大的差距。選擇UnityScript以前,問問本身以前據說過Boo嗎?別忘了UnityScript和Boo的淵源。
UnityScript和JavaScript除了長得像以外,根本就沒有什麼關係。你在JavaScript裏如魚得水,在UnityScript中若是不當心就可能埋下隱患,而一些隱患可能藏得很深。並且UnityScript也是靜態語言,也須要編譯,因此看不出來選擇它做爲開發語言爲何會有人以爲快。
插件的支持。貌似大多數都是C#寫的吧。
好吧,若是上面的3點都不能說動你,那就看看官方的態度好了。
U3D官方團隊基於數據分析作出的一個語言被使用的百分比圖。
因爲Boo語言的使用量基本能夠忽略,因此從Unity5.0版本開始就會中止對Boo的文檔支持。同時消失的還有從菜單建立Boo腳本的選項「Create Boo Script」。從U3D團隊對Boo的態度,也能夠窺見和Boo聯繫密切的UnityScript將來的走勢。
同時U3D團隊也會把支持的重心轉移到C#,也就是說文檔和示例以及社區支持的重心都在C#,C#的文檔會是最完善的,C#的代碼實例會是最詳細的,社區內用C#討論的人數會是最多的。
因此到底如何選擇,各位盆友也都應該有了本身的認識咯。
天色已晚,匹夫睡覺去也~