文本標(biāo)記語言(HTML)是用于在 Internet 上顯示 Web 頁面的主要標(biāo)記語言。換句話說,網(wǎng)頁由 HTML 組成,用于通過 Web 瀏覽器顯示文本,圖像或其他資源。
使用標(biāo)記來描述文檔結(jié)構(gòu)和表現(xiàn)形式并通過瀏覽器進(jìn)行解析,然后把結(jié)果顯示在網(wǎng)頁上. 它是網(wǎng)頁構(gòu)成的基礎(chǔ),你見到的所有網(wǎng)頁都離不開HTML,所以學(xué)習(xí)HTML是基礎(chǔ)中的基礎(chǔ)。
什么是HTML?
HTML是用來制作網(wǎng)頁的標(biāo)記語言
HTML是Hypertext MarkupLanguage的英文縮寫, 即超文本標(biāo)記語言
HTML語言是一種標(biāo)記語言,不需要編譯,直接由瀏 HTML語言是一種標(biāo)記語言,不需要編譯,直接由瀏覽器執(zhí)行
HTML文件是一個(gè)文本文件,包含了一些HTML元素, HTML文件是一個(gè)文本文件,包含了一些HTML元素,標(biāo)簽等.
HTML文件必須使用html或htm為文件名后綴
HTML是大小寫不敏感的,HTML與html是一樣的
Html和CSS的關(guān)系
學(xué)習(xí)web前端開發(fā)基礎(chǔ)技術(shù)需要掌握:HTML、CSS、JavaScript語言。下面我們就來了解下這三門技術(shù)都是用來實(shí)現(xiàn)什么的:
1. HTML是網(wǎng)頁內(nèi)容的載體。內(nèi)容就是網(wǎng)頁制作者放在頁面上想要讓用戶瀏覽的信息,可以包含文字、圖片、視頻等。
2. CSS樣式是表現(xiàn)(外觀控制)。就像網(wǎng)頁的外衣。比如,標(biāo)題字體、顏色變化,或?yàn)闃?biāo)題加入背景圖片、邊框等。所有這些用來改變內(nèi)容外觀的東西稱之為表現(xiàn)。
3. JavaScript是用來實(shí)現(xiàn)網(wǎng)頁上的特效效果。如:鼠標(biāo)滑過彈出下拉菜單。或鼠標(biāo)滑過表格的背景顏色改變。還有焦點(diǎn)新聞(新聞圖片)的輪換。可以這么理解,有動(dòng)畫的,有交互的一般都是用JavaScript來實(shí)現(xiàn)的。
HTML形象案例理解
為了簡(jiǎn)化對(duì)復(fù)雜Internet的理解,暫時(shí)把復(fù)雜的Internet抽象成:向女生宿舍發(fā)送信號(hào)。
為了能夠在斷網(wǎng)后繼續(xù)聯(lián)絡(luò)女神,我們買來了電池和開關(guān),然后拉了普通電線線到女神的寢室。女神把小燈泡接在了電線的兩端。
這時(shí)只需要開閉開關(guān),就可以控制女神屋里的燈泡了。
發(fā)送信號(hào)
關(guān)閉著小燈泡1秒鐘,代表發(fā)送一個(gè)0。
打開了小燈泡1秒鐘,代表發(fā)送一個(gè)1。
此時(shí)可以向女神發(fā)送任意二進(jìn)制內(nèi)容了。
發(fā)送文本
我們只希望發(fā)送一個(gè)“Good Night”。所以必須想辦法對(duì)“Good Night”進(jìn)行編碼。
'G','o','o','d'的ASCII編碼是:071,111,111,100,轉(zhuǎn)換成二進(jìn)制就是:0100 0001 0110 1111 0110 1111 0110 0100
使用發(fā)送二進(jìn)制信號(hào)的方法發(fā)過去,女神需要自己解碼來閱讀出“Good”。
發(fā)送富文本
有一天,我們希望發(fā)送“I miss you”。
為了表達(dá)心意,你希望加粗“you”,并使用紅色的字體發(fā)送“miss”,而這一根簡(jiǎn)陋的電線似乎很難理解“加粗”和“紅色”
于是機(jī)智的我們發(fā)送了:I <red>miss</red> <b>you</b>
同樣機(jī)智的女神理解了這件事情。
你發(fā)明了一種語言
我們很喜歡這種編碼格式,這是一種標(biāo)記語言,于是我們稱其為:TGML (To Girl Markup Language)
平時(shí)你使用TGML來和你的女神通信,TGML可以非常好的解決富文本的傳輸問題。
表白
終于決定表白了,我們繪制了很美麗的心形圖片,還有兩個(gè)按鈕:接受、拒絕。
兩個(gè)按鈕分別會(huì)鏈接到 "接受.tgml"和"拒絕.tgml"
為了描述圖片,我們使用了<img src="pic">
為了描述按鈕,我們使用了<a href="接受.tgml">
現(xiàn)在發(fā)送給女神的內(nèi)容,已經(jīng)不只是帶格式的富文本,而是帶鏈接的富文本。
于是在這個(gè)世界上,擁有了一種能夠描述超文本的語言,叫做TGML
超文本
描述超文本的方式有很多,例如:HTML,TGML,還有markdown。HTML的誕生是為了描述超文本。
超文本的用途也很多,例如:描述一個(gè)網(wǎng)頁,或者描述一個(gè)Word文檔。HTML文件是超文本文件。但是超文本未必是HTML。
Microsoft Word
Microsoft Word是Office的一個(gè)組件。它同樣代表一個(gè)超文本資源。
如果你解壓一個(gè)docx文件并仔細(xì)翻一翻,你能找到你剛才寫的作業(yè),是以HTML方式記載著。
當(dāng)然,描述超文本還有markdown。所以你可以使用一些工具,比如pandoc,把markdown轉(zhuǎn)換成word。
故事的結(jié)局
女神的名字叫瀏覽器,我們的名字叫Web服務(wù)器,女神和我們中間的那根線叫HTTP。
今年國(guó)慶假期終于可以憋在家里了不用出門了,不用出去看后腦了,真的是一種享受。這么好的光陰怎么浪費(fèi),睡覺、吃飯、打豆豆這怎么可能(耍多了也煩),完全不符合我們程序員的作風(fēng),趕緊起來把文章寫完。
這篇文章比較基礎(chǔ),在國(guó)慶期間的業(yè)余時(shí)間寫的,這幾天又完善了下,力求把更多的前端所涉及到的關(guān)于文件上傳的各種場(chǎng)景和應(yīng)用都涵蓋了,若有疏漏和問題還請(qǐng)留言斧正和補(bǔ)充。
以下是本文所涉及到的知識(shí)點(diǎn),break or continue ?
原理很簡(jiǎn)單,就是根據(jù) http 協(xié)議的規(guī)范和定義,完成請(qǐng)求消息體的封裝和消息體的解析,然后將二進(jìn)制內(nèi)容保存到文件。
我們都知道如果要上傳一個(gè)文件,需要把 form 標(biāo)簽的enctype設(shè)置為multipart/form-data,同時(shí)method必須為post方法。
那么multipart/form-data表示什么呢?
multipart互聯(lián)網(wǎng)上的混合資源,就是資源由多種元素組成,form-data表示可以使用HTML Forms 和 POST 方法上傳文件,具體的定義可以參考RFC 7578。
multipart/form-data 結(jié)構(gòu)
看下 http 請(qǐng)求的消息體
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryDCntfiXcSkPhS4PN 表示本次請(qǐng)求要上傳文件,其中boundary表示分隔符,如果要上傳多個(gè)表單項(xiàng),就要使用boundary分割,每個(gè)表單項(xiàng)由———XXX開始,以———XXX結(jié)尾。
每一個(gè)表單項(xiàng)又由Content-Type和Content-Disposition組成。
Content-Disposition: form-data 為固定值,表示一個(gè)表單元素,name 表示表單元素的 名稱,回車換行后面就是name的值,如果是上傳文件就是文件的二進(jìn)制內(nèi)容。
Content-Type:表示當(dāng)前的內(nèi)容的 MIME 類型,是圖片還是文本還是二進(jìn)制數(shù)據(jù)。
解析
客戶端發(fā)送請(qǐng)求到服務(wù)器后,服務(wù)器會(huì)收到請(qǐng)求的消息體,然后對(duì)消息體進(jìn)行解析,解析出哪是普通表單哪些是附件。
可能大家馬上能想到通過正則或者字符串處理分割出內(nèi)容,不過這樣是行不通的,二進(jìn)制buffer轉(zhuǎn)化為string,對(duì)字符串進(jìn)行截取后,其索引和字符串是不一致的,所以結(jié)果就不會(huì)正確,除非上傳的就是字符串。
不過一般情況下不需要自行解析,目前已經(jīng)有很成熟的三方庫可以使用。
至于如何解析,這個(gè)也會(huì)占用很大篇幅,后面的文章在詳細(xì)說。
使用 form 表單上傳文件
在 ie時(shí)代,如果實(shí)現(xiàn)一個(gè)無刷新的文件上傳那可是費(fèi)老勁了,大部分都是用 iframe 來實(shí)現(xiàn)局部刷新或者使用 flash 插件來搞定,在那個(gè)時(shí)代 ie 就是最好用的瀏覽器(別無選擇)。
DEMO
這種方式上傳文件,不需要 js ,而且沒有兼容問題,所有瀏覽器都支持,就是體驗(yàn)很差,導(dǎo)致頁面刷新,頁面其他數(shù)據(jù)丟失。
HTML
<form method="post" action="http://localhost:8100" enctype="multipart/form-data">
選擇文件:
<input type="file" name="f1"/> input 必須設(shè)置 name 屬性,否則數(shù)據(jù)無法發(fā)送<br/>
<br/>
標(biāo)題:<input type="text" name="title"/><br/><br/><br/>
<button type="submit" id="btn-0">上 傳</button>
</form>
復(fù)制代碼
服務(wù)端文件的保存基于現(xiàn)有的庫koa-body結(jié)合 koa2實(shí)現(xiàn)服務(wù)端文件的保存和數(shù)據(jù)的返回。
在項(xiàng)目開發(fā)中,文件上傳本身和業(yè)務(wù)無關(guān),代碼基本上都可通用。
在這里我們使用koa-body庫來實(shí)現(xiàn)解析和文件的保存。
koa-body 會(huì)自動(dòng)保存文件到系統(tǒng)臨時(shí)目錄下,也可以指定保存的文件路徑。
然后在后續(xù)中間件內(nèi)得到已保存的文件的信息,再做二次處理。
NODE
/**
* 服務(wù)入口
*/
var http = require('http');
var koaStatic = require('koa-static');
var path = require('path');
var koaBody = require('koa-body');//文件保存庫
var fs = require('fs');
var Koa = require('koa2');
var app = new Koa();
var port = process.env.PORT || '8100';
var uploadHost= `http://localhost:${port}/uploads/`;
app.use(koaBody({
formidable: {
//設(shè)置文件的默認(rèn)保存目錄,不設(shè)置則保存在系統(tǒng)臨時(shí)目錄下 os
uploadDir: path.resolve(__dirname, '../static/uploads')
},
multipart: true // 開啟文件上傳,默認(rèn)是關(guān)閉
}));
//開啟靜態(tài)文件訪問
app.use(koaStatic(
path.resolve(__dirname, '../static')
));
//文件二次處理,修改名稱
app.use((ctx) => {
var file = ctx.request.files.f1;//得道文件對(duì)象
var path = file.path;
var fname = file.name;//原文件名稱
var nextPath = path+fname;
if(file.size>0 && path){
//得到擴(kuò)展名
var extArr = fname.split('.');
var ext = extArr[extArr.length-1];
var nextPath = path+'.'+ext;
//重命名文件
fs.renameSync(path, nextPath);
}
//以 json 形式輸出上傳文件地址
ctx.body = `{
"fileUrl":"${uploadHost}${nextPath.slice(nextPath.lastIndexOf('/')+1)}"
}`;
});
/**
* http server
*/
var server = http.createServer(app.callback());
server.listen(port);
console.log('demo1 server start ...... ');
復(fù)制代碼
CODE
https://github.com/Bigerfe/fe-learn-code/
年來,前端技術(shù)日新月異,前端已經(jīng)不僅僅是網(wǎng)頁,更多的開始由狹義向廣義發(fā)展。
先后涌現(xiàn)出了具備后端能力的node,具備移動(dòng)開發(fā)能力的react native,具備游戲渲染能力的cocos2d-js,以及iOS上的熱修復(fù)技術(shù)JSPatch等等新技術(shù)。
咋一看,幾乎各個(gè)端都被JavaScript攻陷,大有一統(tǒng)江湖之勢(shì)。
究竟,JavaScript如何做到上天入地?zé)o所不能?JavaScript真的能一統(tǒng)江湖嗎?
亂世出英雄:JavaScript的誕生故事要從JavaScript的由來說起。
高能瞎扯淡版,正經(jīng)臉的同學(xué)可以忽略
有人的地方就有江湖,有江湖的地方就有紛爭(zhēng)。
故事要從當(dāng)年的瀏覽器之戰(zhàn)說起。
時(shí)間回到1994年,
(→ 那時(shí)候我還是個(gè)寶寶~ #天真臉#)
景兄弟橫空出世,并自帶神器網(wǎng)景導(dǎo)航,戰(zhàn)斗力爆表,勢(shì)如劈竹,瞬時(shí)間威震天下。
一出世就武裝到牙齒,武力值這么高還自帶兵器,這個(gè)科學(xué)嗎?
港真,我也覺得不科學(xué),也許跟熊孩子哪吒、女漢子雅典娜是一個(gè)品種吧?
這一切北方的老前輩微軟大濕,都看在眼里,不甘天下盡歸景兄弟這個(gè)初出茅廬的毛孩子,大濕積淀多年,潛心修煉一年,終于帶著大殺器IE 1.0出關(guān)了,誓于景兄弟爭(zhēng)個(gè)高低。
自此景兄弟的網(wǎng)景導(dǎo)航 VS 微軟大濕的IE 的軍備戰(zhàn)爭(zhēng)開始。
景兄弟仔細(xì)掂量,微軟大濕財(cái)大氣粗,內(nèi)功深厚,臣妾實(shí)在是辦不到啊啊啊啊啊啊。
景兄弟緊急召集門人商議對(duì)策,有一門人曰:”以我們微薄之力硬磕,是萬萬使不得的。如今我們,一是宜施行合縱之策,抱大腿,組成聯(lián)盟!二是避其鋒芒,出奇招致勝。“
于是景兄弟依照此策略,一方面找到了當(dāng)時(shí)德高為重的另一位前輩SUN,組成了開發(fā)者聯(lián)盟。
(微軟大濕:握草,聯(lián)盟都粗來了,那我是不是得搞個(gè)部落?)
另一方面,景兄弟找到了鍛造大師布蘭登,請(qǐng)布大師幫忙升級(jí)兵器網(wǎng)景導(dǎo)航,大師就是大師,不費(fèi)吹灰之力就完成了強(qiáng)化升級(jí),然而布大師突發(fā)奇想,本來這是近距離攻擊兵器,要是有多一個(gè)遠(yuǎn)距離攻擊的能力那豈不是更好?Just do it. 想罷大師就加了一個(gè)遠(yuǎn)距離攻擊的feature。于是有了自帶遠(yuǎn)距離攻擊能力的網(wǎng)景導(dǎo)航2.0。景兄弟一看這么流弊心里甚是歡喜,不過遠(yuǎn)距離攻擊的技能叫做LiveScript,感覺不是特別Fashion。特然想到這不是跟SUN前輩聯(lián)盟嘛,SUN家的Java正是獨(dú)霸武林之時(shí)。不如把名字改成跟Java有關(guān),蹭一把東風(fēng),蹭點(diǎn)光環(huán)。一拍腦袋,JavaScript!!!眾門人一聽:”好好好,JavaScript 流弊炫酷吊炸天!“
果然第一節(jié)下半場(chǎng),景兄弟攜強(qiáng)化過的網(wǎng)景導(dǎo)航2.0 戰(zhàn)個(gè)痛快,那是杠杠的!人家一問,你咋還能遠(yuǎn)程攻擊,你這個(gè)遠(yuǎn)程攻擊用的是啥?答曰:JavaScript。“JavaScript,一定是跟SUN家Java是一個(gè)系列產(chǎn)品,一定很流弊!”#光環(huán)加成,各種膜拜臉#
微軟大濕虧了一場(chǎng),痛定思痛,也要搞遠(yuǎn)程攻擊功能,果然不久,就祭出了同樣帶有遠(yuǎn)程攻擊能力的IE 3.0,鑒于景兄弟的遠(yuǎn)程攻擊叫做JavaScript,J開頭的感覺應(yīng)該比較流弊,所以微軟大濕的叫做JScript。
然后戰(zhàn)爭(zhēng)就從地面貼身肉搏戰(zhàn),開始逐步升級(jí)到了遠(yuǎn)距離核戰(zhàn)爭(zhēng)。
正所謂,城門失火,殃及池魚。這么打下去苦逼的是搬磚的頁面仔,就是我這種,到處都是雷區(qū),無處下腳。
最后到了1997年,“聯(lián)合國(guó)安理會(huì)秘書長(zhǎng)”艾瑪(ECMA)出來調(diào)停,多方簽署了“核不擴(kuò)散條約”,約束各種遠(yuǎn)程攻擊武器的使用,這才走上了正軌。
1995年SUN開發(fā)了Java技術(shù),這是第一個(gè)通用軟件平臺(tái)。Java擁有跨平臺(tái)、面向?qū)ο蟆⒎盒途幊痰奶匦裕瑥V泛應(yīng)用于企業(yè)級(jí)Web應(yīng)用開發(fā)和移動(dòng)應(yīng)用開發(fā)。Java也伴隨著互聯(lián)網(wǎng)的迅猛發(fā)展而發(fā)展,逐漸成為重要的網(wǎng)絡(luò)編程語言。名噪一時(shí)。
1994年Netscape公司成立,并推出了自己的瀏覽器的免費(fèi)版本 Netscape Navigator,很快就占有了瀏覽器市場(chǎng)。到了 1995 年,微軟公司開始加入,并很快發(fā)布了自己的 Internet Explorer 1.0。
1995年,當(dāng)時(shí)在Netscape就職的Brendan Eich(布蘭登·艾克),正為Netscape Navigator 2.0瀏覽器開發(fā)的一門名為L(zhǎng)iveScript的腳本語言,后來Netscape與Sun Microsystems組成的開發(fā)聯(lián)盟,為了讓這門語言搭上Java這個(gè)編程語言“熱詞”,將其臨時(shí)改名為“JavaScript”,日后這成為大眾對(duì)這門語言有諸多誤解的原因之一。
JavaScript最初受Java啟發(fā)而開始設(shè)計(jì)的,目的之一就是“看上去像Java”,因此語法上有類似之處,一些名稱和命名規(guī)范也借自Java。但JavaScript的主要設(shè)計(jì)原則源自Self和Scheme。JavaScript與Java名稱上的近似,是當(dāng)時(shí)Netscape為了營(yíng)銷考慮與SUN達(dá)成協(xié)議的結(jié)果。
==> 所以,JavaScript和Java其實(shí)沒有半毛錢關(guān)系。
JavaScript推出后在瀏覽器上大獲成功,微軟在不久后就為Internet Explorer 3.0瀏覽器推出了JScript,以與處于市場(chǎng)領(lǐng)導(dǎo)地位的Netscape產(chǎn)品同臺(tái)競(jìng)爭(zhēng)。JScript也是一種JavaScript實(shí)現(xiàn),這兩個(gè)
JavaScript語言版本在瀏覽器端共存意味著語言標(biāo)準(zhǔn)化的缺失,對(duì)這門語言進(jìn)行標(biāo)準(zhǔn)化被提上了日程,在1997年,由Netscape、SUN、微軟、寶藍(lán)等公司組織及個(gè)人組成的技術(shù)委員會(huì)在ECMA(歐洲計(jì)算機(jī)制造商協(xié)會(huì))確定定義了一種名叫ECMAScript的新腳本語言標(biāo)準(zhǔn),規(guī)范名為ECMA-262。JavaScript成為了ECMAScript的實(shí)現(xiàn)之一。ECMA-262 第五版,即是ES5。
==> ECMA-262,包括ES5, ES6等是一個(gè)標(biāo)準(zhǔn),JavaScript是ECMAScript的一個(gè)實(shí)現(xiàn)。
完整的JavaScript實(shí)現(xiàn)應(yīng)該包含三個(gè)部分:
在網(wǎng)景導(dǎo)航2.0和IE 3.0出現(xiàn)之后的幾年間,網(wǎng)景和微軟公司不停的發(fā)布新版本的瀏覽器,支持更多的新功能。自此拉開了瀏覽器之戰(zhàn)的序幕。這場(chǎng)瀏覽器之戰(zhàn)到現(xiàn)在還在繼續(xù),以下一張圖看清楚過程。
從瀏覽器之戰(zhàn)可以看出,各家瀏覽器比拼的大致兩個(gè)方面視覺體驗(yàn)(渲染排版)和速度(腳本運(yùn)行)。
==> 所以一個(gè)完整的瀏覽器組成,至少包含兩個(gè)部分:
補(bǔ)充一個(gè)市面常見瀏覽器的內(nèi)核和JavaScript引擎搭配:
其他JavaScript引擎,Rhino,由Mozilla基金會(huì)管理,開放源代碼,完全以Java編寫,可以看做SpiderMonkey的Java版。
注意:webkit不單單只是一個(gè)排版引擎,webkit = 排版引擎 + JavaScript引擎。
==> 所以,JavaScript是動(dòng)態(tài)語言,它的運(yùn)行都是基于JavaScript引擎,引擎大都是由靜態(tài)語言實(shí)現(xiàn)C++、Java、and so on。JavaScript的能力也是由引擎賦予。不管是瀏覽器環(huán)境中是window,亦或是node環(huán)境中的process,均是由引擎提供。
(番外:Mozilla的人不知道為啥特別喜歡猴子,經(jīng)常以猴子命名技術(shù),所以看到帶Monkey的,十有八九估計(jì)是他們搞的。)
在瀏覽器環(huán)境中,DOM、BOM、window對(duì)象、setTimeout/setInterval,alert,console等方法均不是JavaScript自身具備的能力,而是瀏覽器native實(shí)現(xiàn),然后通過JavaScript引擎注入到JS運(yùn)行的全局上下文中,供JS使用。
鑒別方式,在調(diào)試器console中打出來,帶有[native code]的即是:
講道理:
JavaScript運(yùn)行 → 依賴于JavaScript引擎 ← 瀏覽器集成了JavaScript引擎,同時(shí)通過JavaScript引擎注入native代碼工JS腳本使用
發(fā)散一下思維,只要有JavaScript引擎,就能運(yùn)行JS腳本,不管有沒有瀏覽器!只是缺少瀏覽器提供的alert,window等方法。
既然瀏覽器可以往JavaScript引擎中注入代碼,賦予JS腳本在網(wǎng)頁中特殊的能力,同理我們可以自己集成JavaScript引擎,自己定義自己的方法往JavaScript引擎中注入,賦予JS更多更強(qiáng)的自定義能力!
注入的關(guān)鍵是:值類型相互對(duì)應(yīng),Obj映射class的一個(gè)實(shí)例,function映射一個(gè)句柄或者引用
JavaScript內(nèi)部,所有數(shù)字都是以64位浮點(diǎn)數(shù)形式儲(chǔ)存,即使整數(shù)也是如此
這就是說,在JavaScript語言的底層,根本沒有整數(shù),所有數(shù)字都是小數(shù)(64位浮點(diǎn)數(shù))。容易造成混淆的是,某些運(yùn)算只有整數(shù)才能完成,此時(shí)JavaScript會(huì)自動(dòng)把64位浮點(diǎn)數(shù),轉(zhuǎn)成32位整數(shù),然后再進(jìn)行運(yùn)算。由于浮點(diǎn)數(shù)不是精確的值,所以涉及小數(shù)的比較和運(yùn)算要特別小心。盡量避免使用JavaScript做精準(zhǔn)計(jì)算和密集計(jì)算。
根據(jù)國(guó)際標(biāo)準(zhǔn)IEEE 754,JavaScript浮點(diǎn)數(shù)的64個(gè)二進(jìn)制位,從最左邊開始,是這樣組成的。
第1位:符號(hào)位,0表示正數(shù),1表示負(fù)數(shù)
第2位到第12位:儲(chǔ)存指數(shù)部分
第13位到第64位:儲(chǔ)存小數(shù)部分(即有效數(shù)字)
符號(hào)位決定了一個(gè)數(shù)的正負(fù),指數(shù)部分決定了數(shù)值的大小,小數(shù)部分決定了數(shù)值的精度。
IEEE 754規(guī)定,有效數(shù)字第一位默認(rèn)總是1,不保存在64位浮點(diǎn)數(shù)之中。也就是說,有效數(shù)字總是1.xx…xx的形式,其中xx..xx的部分保存在64位浮點(diǎn)數(shù)之中,最長(zhǎng)可能為52位。因此,JavaScript提供的有效數(shù)字最長(zhǎng)為53個(gè)二進(jìn)制位(64位浮點(diǎn)的后52位+有效數(shù)字第一位的1)。
內(nèi)部表現(xiàn)公式:(-1)^符號(hào)位 1.xx…xx 2^指數(shù)位
精度最多只能到53個(gè)二進(jìn)制位,這意味著,絕對(duì)值小于2的53次方的整數(shù),即-(253-1)到253-1,都可以精確表示。
而大部分的后端語言,C++、Java、Python等的long型都是可以支持到64位,因此long型數(shù)據(jù)從后端語言傳給JavaScript會(huì)發(fā)生低位截?cái)?/strong>。遇到這種情況一般使用String處理,如需要在JavaScript中做long型計(jì)算,需要自行實(shí)現(xiàn)計(jì)算器。
有了自行往JavaScript引擎中注入的想法,接下來就是分析可行性。
大部分是JavaScript引擎是使用C++編寫,如果自己的程序使用的是C++可以很方便的進(jìn)行注入,如果是OC,可以使用OC和C++混編的形式。
其他語言怎么破?
要在一門靜態(tài)語言上與動(dòng)態(tài)語言JavaScript相互調(diào)用,最便捷的方式是找到一個(gè)這門語言實(shí)現(xiàn)的JavaScript引擎(開源),直接進(jìn)行集成,注入。如果沒有,則需要使用多一層橋接,把這門語言的接口暴露給C++,再由C++實(shí)現(xiàn)的JavaScript引擎將接口注入供JavaScript使用。
服務(wù)端集成思路&實(shí)踐:
我們都知道nodeJS,但是nodeJS的運(yùn)行依賴于Google的V8 引擎,V8是C++實(shí)現(xiàn),底層使用C++實(shí)現(xiàn)底層功能,比如網(wǎng)絡(luò),數(shù)據(jù)庫IO,對(duì)外暴露一個(gè)構(gòu)造器接口注入到上下文中,注意此處暴露的只是一個(gè)構(gòu)造器接口而不是一個(gè)創(chuàng)建完的實(shí)例。然后實(shí)現(xiàn)了一個(gè)require的hook函數(shù)。當(dāng)使用require加載一個(gè)JS模塊時(shí),跟網(wǎng)頁中使用AMD 的require并無異樣,當(dāng)使用require加載系統(tǒng)庫,既是C++的模塊時(shí),會(huì)調(diào)用暴露出來的構(gòu)造器接口,得到一個(gè)實(shí)例對(duì)象。不管是裝載JS模塊還是裝載C++模塊,得到的都可以看做是一個(gè)Module Object,node會(huì)將裝載完的模塊緩存到binding_cache中,下次在別處的代碼中使用require裝載模塊時(shí),就會(huì)先去binding_cache中查找,如果找到了則返回該module object,如果沒找到再執(zhí)行上面的裝載流程。
這就是node的基本原理:C++封裝底層操作,通過V8注入,使得JS腳本有網(wǎng)絡(luò)和IO能力
以上說到的幾個(gè)都是C++層面的應(yīng)用,那么經(jīng)典的Java怎么玩?是不是Java就必須是靜態(tài)語言的玩法,沒有辦法像C++之類的,可以使用JS的動(dòng)態(tài)特性?
當(dāng)然不是。這個(gè)時(shí)候,我們需要說起前面介紹過的一個(gè)JS引擎 Rhino,Rhino是完全由Java編寫,可想而知,Rhino幾乎就是為Java應(yīng)用而生的。
用法是這樣:
首先在我們的Java應(yīng)用中集成Rhino;
所有的IO操作,網(wǎng)絡(luò)操作等,都封裝成service,并提供增刪改查,setter && getter等多種方法
通過spring,把這些service bean注入到Rhino中;
把業(yè)務(wù)邏輯寫到JS代碼中,JS代碼調(diào)用多個(gè)已注入的Java service處理業(yè)務(wù)邏輯,拼裝數(shù)據(jù)返回!
好處:修改業(yè)務(wù)邏輯不需要修改Java代碼,也就是不需要重新編譯和部署,只需要刷新下跑在Rhino中的JS代碼即可。以往Java應(yīng)用的一個(gè)痛點(diǎn)是部署,需要重新編譯,打包,部署重啟服務(wù)器,現(xiàn)在以這種形式開發(fā),可以達(dá)到服務(wù)端的熱更新和熱部署。既可以享有Java服務(wù)的穩(wěn)定性和可靠性,又可以享有JS的靈活性。
這種技術(shù)和用法在差不多十年前就有過,前EMC的工程師基于EMC著名的商業(yè)產(chǎn)品Documentum,設(shè)計(jì)了一套Java開源的中小企業(yè)CMS系統(tǒng)Alfresco,在該系統(tǒng)中實(shí)現(xiàn)了這種技術(shù),這種技術(shù)基于spring,叫做spring-surf,做了一個(gè)膠水層。可以看做小十年前的node吧。
Demo,使用spring-surf框架的系統(tǒng)中一個(gè)webscript模塊
categorynode.get.xml定義URL攔截器和權(quán)限控制;
.get指明是處理GET請(qǐng)求,RESTful;
在categorynode.get.js中調(diào)用已注入的Java Bean處理業(yè)務(wù)邏輯;
若為網(wǎng)頁請(qǐng)求返回.html.ftl,若為Ajax,返回.json.ftl;
(此處配套使用的是FreeMarker模板引擎)
==> categorynode.get.desc.xml
==> categorynode.get.js
==> categorynode.get.html.ftl
==> categorynode.get.json.ftl
React Native目前也是異常火爆,RN程序的運(yùn)行依賴于Facebook的RN框架。在iOS、Android的模擬器或是真機(jī)上,React Native使用的是JavaScriptCore引擎,也就是Safari所使用的JavaScript引擎。但是在iOS上JavaScriptCore并沒有使用即時(shí)編譯技術(shù)(JIT),因?yàn)樵趇OS中應(yīng)用無權(quán)擁有可寫可執(zhí)行的內(nèi)存頁(因而無法動(dòng)態(tài)生成代碼),在安卓上,理論上是可以使用的。JavaScriptCore引擎也是使用C++編寫,在iOS和安卓中,JavaScriptCore都做了一層封裝,可以無須關(guān)心引擎和系統(tǒng)橋接的那一層。iOS/Android系統(tǒng)通過JavaScriptCore引擎將定制好的各種原生組件注入,如:listview,text等。
cocos2dx是游戲開發(fā)中非常常用的游戲渲染引擎,有一系列的產(chǎn)品,如:cocos2dx(C++),cocos2d-lua(lua), cocos2d-js(JavaScript)等多個(gè)產(chǎn)品。其中最新退出的是cocos2dx的JS版本的cocos2d-js,編寫游戲渲染特效代碼相比于C++和lua非常方便。對(duì)于做需要經(jīng)常更新的渲染場(chǎng)景,C++是靜態(tài)語言,每次修改都需要重新編譯才能運(yùn)行,顯然是不合適的。自然也就想到了腳本語言,lua和js,兩者有些類似,都是動(dòng)態(tài)語言,只需要集成一個(gè)運(yùn)行引擎,提供一個(gè)運(yùn)行的容器即可運(yùn)行,同時(shí)通過引擎注入底層方法供腳本調(diào)用即可。lua好處是精簡(jiǎn),語法精簡(jiǎn),引擎頁很小很精簡(jiǎn),所以不可避免的代碼量會(huì)比js多,同時(shí)學(xué)習(xí)成本比較高。js的好處是有ECMAScrtpt的核心,語法比較豐富,同時(shí)有支持一些高級(jí)屬性。在cocos2d-js中,cocos2dx(C++)集成了SpiderMonkey(C++)作為JS運(yùn)行引擎,中間做了一個(gè)膠水層既是JS Binding,通過引擎注入了一個(gè)cc的全局對(duì)象,映射的是底層C++的一個(gè)單例C++實(shí)例。表面上寫的是JS代碼,實(shí)際上操作的是底層的C++。cocos2d-js是代碼可以運(yùn)行在多種環(huán)境中,當(dāng)運(yùn)行的網(wǎng)頁環(huán)境中時(shí),使用的是cocos2d-html5引擎,底層操作的是canvas;當(dāng)運(yùn)行在客戶端上時(shí),使用的是cocos2dx引擎,底層操作的是C++,再由C++去操控openGL做繪制和渲染。提供相同的API,對(duì)開發(fā)者幾乎是透明無差異的,開發(fā)者只需要關(guān)注實(shí)現(xiàn)效果即可。達(dá)到一套代碼,多端運(yùn)行(網(wǎng)頁端,客戶端)。
JSPatch是目前比較流行的iOS上的熱修復(fù)技術(shù),JSPatch 能做到通過 JS 調(diào)用和改寫 OC 方法最根本的原因是 Objective-C 是動(dòng)態(tài)語言,OC 上所有方法的調(diào)用/類的生成都通過 Objective-C Runtime 在運(yùn)行時(shí)進(jìn)行,我們可以通過類名/方法名反射得到相應(yīng)的類和方法。JSPatch 的基本原理就是:JS 傳遞字符串給 OC,OC 通過 Runtime 接口調(diào)用和替換 OC 方法。
關(guān)鍵技術(shù)之一是 JS 和 OC 之間的消息互傳。JSPatch里包含了,一個(gè)JS引擎JavaScriptCore(Safari,React Native用的同款)。用到了 JavaScriptCore 的接口,OC 端在啟動(dòng) JSPatch 引擎時(shí)會(huì)創(chuàng)建一個(gè) JSContext 實(shí)例,JSContext 是 JS 代碼的執(zhí)行環(huán)境,可以給 JSContext 添加方法,JS 就可以直接調(diào)用這個(gè)方法。本質(zhì)上就是通過JavaScriptCore引擎注入,暴露OC的方法供JS調(diào)用來實(shí)現(xiàn)動(dòng)態(tài)修改OC的反射。
Demo,iOS熱更新,熱修復(fù):
集成JavaScriptCore引擎;
通過引擎,橋接JS和OC;
通過JS修改OC反射。
詳細(xì)的JSPatch技術(shù)介紹請(qǐng)移步:https://github.com/bang590/JSPatch/wiki
關(guān)于JavaScript引擎:
在iOS 或 android 上能夠運(yùn)行的JavaScript 引擎有4個(gè):JavaScriptCore,SpiderMonkey,V8,Rhino。下面這個(gè)表格展示各個(gè)引擎在iOS 和 Android 的兼容性。
因?yàn)閕OS平臺(tái)不支持JIT即時(shí)編譯,而V8只有JIT模式,所以V8無法在iOS平臺(tái)使用(越獄設(shè)備除外,想體驗(yàn)iOS JIT的同學(xué)可以自行越獄)。
所以,目前可以做到橫跨iOS和Android雙平臺(tái)的JS引擎,只有兩款,即是SpiderMonkey和JavaScriptCore。
JavaScript引擎會(huì)受很多東西影響,比如交叉編譯器的版本、引擎的版本和操作系統(tǒng)的種類等。
至于如何選擇,可以參考:《Part I: How to Choose a JavaScript Engine for iOS and Android Development》
至此,JavaScript從立足于前端,到征戰(zhàn)全端的逆襲之路,可以總結(jié)為“攜引擎以令天下”。
不足之處,還請(qǐng)各位看官輕拍~
bang590/JSPatch中問參考文檔
Cocos2d-JS | Cocos2d-x官方參考文檔
Alfresco官方參考文檔
《Browser Wars: The End or Just the Beginning?》
《Part I: How to Choose a JavaScript Engine for iOS and Android Development》
《React Native 從入門到源碼》
*請(qǐng)認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。