先決條件: | 熟悉核心 HTML , CSS 和 JavaScript 語(yǔ)言; 有關(guān)跨瀏覽器測(cè)試的高級(jí)原則的概念。 |
---|---|
目的: | 能夠診斷常見(jiàn)的JavaScript跨瀏覽器問(wèn)題,并使用適當(dāng)?shù)墓ぞ吆图夹g(shù)來(lái)修復(fù)它們。 |
歷史上,JavaScript遇到跨瀏覽器兼容性問(wèn)題 - 在20世紀(jì)90年代,當(dāng)時(shí)的主要瀏覽器選擇(Internet Explorer和Netscape)使用不同語(yǔ)言實(shí)現(xiàn)的腳本(Netscape有JavaScript,IE有JScript,還提供VBScript作為 選項(xiàng)),而且至少JavaScript和JScript在某種程度上兼容(基于 > ECMAScript 規(guī)范),事情經(jīng)常以沖突,不兼容的方式實(shí)現(xiàn),導(dǎo)致開發(fā)者遭遇許多惡夢(mèng)。
這種不兼容性問(wèn)題在21世紀(jì)初仍然存在,因?yàn)榕f的瀏覽器仍在使用中,仍然需要支持。 這是為什么像 jQuery 這樣的庫(kù)存在的主要原因之一 - 抽象出瀏覽器實(shí)現(xiàn)中的差異(例如, 如何發(fā)出HTTP請(qǐng)求)中的代碼段,因此開發(fā)人員 只需要寫一個(gè)簡(jiǎn)單的代碼(見(jiàn) jQuery.ajax() / code>)。 jQuery(或你使用的任何庫(kù))將處理在后臺(tái)的差異,所以你不必。
事情從那時(shí)起好多了; 現(xiàn)代瀏覽器在支持"經(jīng)典JavaScript功能"方面做得很好,并且使用這樣的代碼的要求已經(jīng)減少,因?yàn)橹С峙f的瀏覽器的需求已經(jīng)減少了(盡管記住它們沒(méi)有完全消失)。
這些天,大多數(shù)跨瀏覽器JavaScript問(wèn)題被看到:
我們將探索所有這些問(wèn)題,以下更多。
正如我們?cè)贖TML / CSS的上一篇文章中所說(shuō)的,您應(yīng)該確保您的代碼正常工作,然后再專注于跨平臺(tái) 瀏覽器問(wèn)題。 如果您還不熟悉 JavaScript JavaScript疑難解答的基本知識(shí),您應(yīng)該先學(xué)習(xí)該文章,然后再繼續(xù)。 有一些常見(jiàn)的JavaScript問(wèn)題,你會(huì)想要注意,例如:
this
scope to a separate variable, then using that variable in nested functions so you can be sure you are applying functionality to the correct this
scope.i
?at the time it was created), how each one reports i
?as 11, because for loops do all their iterating before nested functions are invoked. If you want this to work correctly, you need to define a function to add the handler separately, calling it on each iteration and passing it the current value of para
and i
each time (or something similar). See good-for-loop.html for a version that works.注意: Buggy JavaScript代碼:最常見(jiàn)的10個(gè) 錯(cuò)誤JavaScript開發(fā)人員Make 有一些很好的討論這些常見(jiàn)的錯(cuò)誤和更多。
與 HTML和CSS 一樣,您可以使用linter來(lái)確保更好的質(zhì)量,更不易出錯(cuò)的JavaScript代碼,它指出錯(cuò)誤并可以標(biāo)記 關(guān)于不良做法的警告等,并且在其錯(cuò)誤/警告報(bào)告中被定制為更嚴(yán)格或更輕松。 JavaScript / ECMAScript是我們推薦的 JSHint 和 class ="external"> ESLint ; 這些可以以各種方式使用,其中一些我們將在下面詳細(xì)描述。
JSHint首頁(yè)提供了一個(gè)在線linter,它允許您在左側(cè)輸入JavaScript代碼,并在右側(cè)提供輸出 ,包括指標(biāo),警告和錯(cuò)誤。
alt ="">
將代碼復(fù)制并粘貼到網(wǎng)頁(yè)上以檢查其有效性是不太方便的。 你真正想要的是一個(gè)linter,將適合你的標(biāo)準(zhǔn)工作流程,最少的麻煩。 許多代碼編輯器都有l(wèi)inter插件,例如Github的 Atom 代碼編輯器提供了一個(gè)JSHint插件。
安裝:
alt ="">其他流行的編輯有類似的linting包可用。 例如,請(qǐng)參閱 JSHint安裝頁(yè)面的"文本編輯器和IDE的插件"部分。
還有其他方法來(lái)使用這種短絨; 您可以在 JSHint 和 用戶指南/開始"class ="external"> ESLint 安裝頁(yè)面。
值得一提的是命令行使用 - 您可以使用npm(Node Package Manager)將這些工具安裝為命令行實(shí)用程序(通過(guò)CLI - 命令行界面) - 您必須安裝 .org / en /"class ="external"> NodeJS 首先)。 例如,以下命令安裝JSHint:
npm install -g jshint
然后,您可以將這些工具指向您要lint的JavaScript文件,例如:
alt ="">您還可以使用這些工具與任務(wù)運(yùn)行程序/構(gòu)建工具(如 Gulp 或 "https://webpack.github.io/"class ="external"> Webpack 在開發(fā)過(guò)程中自動(dòng)刪除JavaScript。 (請(qǐng)參見(jiàn)后面的文章中的使用任務(wù)運(yùn)行器自動(dòng)化測(cè)試工具 。)有關(guān)ESLint選項(xiàng)的信息,請(qǐng)參見(jiàn) ESLint集成; JSHint由Grunt支持開箱即用,并且還具有其他可用的集成,例如, 用于Webpack的JSHint加載器。
注意:ESLint比JSHint需要更多的設(shè)置和配置,但它也更強(qiáng)大。
瀏覽器開發(fā)者工具有許多有用的功能來(lái)幫助調(diào)試JavaScript。 首先,JavaScript控制臺(tái)會(huì)報(bào)告代碼中的錯(cuò)誤。
制作我們的 external"> broken-ajax.html example(see the /broken-ajax.html"class ="external">源代碼)。 如果你看看控制臺(tái),你會(huì)看到以下輸出:
alt ="">
錯(cuò)誤消息讀為"TypeError:jsonObj is null",并且行號(hào)表示為37.如果我們查看源代碼,相關(guān)代碼段為:
function populateHeader(jsonObj) { var myH1 = document.createElement('h1'); myH1.textContent = jsonObj['squadName']; header.appendChild(myH1); ...
因此,一旦我們嘗試訪問(wèn) jsonObj
(您可能會(huì)期望,它應(yīng)該是一個(gè) JSON對(duì)象 >)。 這應(yīng)該是使用以下XMLHttpRequest調(diào)用從外部 .json
文件中獲取:
var requestURL = 'https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json'; var request = new XMLHttpRequest(); request.open('GET', requestURL); request.send(); var superHeroes = request.response; populateHeader(superHeroes); showHeroes(superHeroes);
但這不行。
您可能已經(jīng)知道此代碼有什么問(wèn)題,但讓我們進(jìn)一步探索,以展示如何調(diào)查此代碼。 首先,有一個(gè)控制臺(tái) API,允許JavaScript代碼與瀏覽器的 JavaScript控制臺(tái)。 它有許多功能可用,但你將經(jīng)常使用的主要是 > console.log()
,它將自定義消息輸出到控制臺(tái)。
嘗試在第31行下面插入以下行(以粗體顯示):
console.log('Response value: ' + superHeroes);
在瀏覽器中刷新頁(yè)面,您將在控制臺(tái)中得到一個(gè)輸出,如下所示:
alt ="">
console.log()
輸出顯示 superHeroes
對(duì)象似乎不包含任何內(nèi)容,雖然注意到錯(cuò)誤消息現(xiàn)在已更改為"TypeError:heroes is 未定義"。 這表明錯(cuò)誤是間歇性的,進(jìn)一步證明這是某種異步錯(cuò)誤。 讓我們修復(fù)當(dāng)前錯(cuò)誤,并移除 console.log()
行,并更新此代碼塊:
var superHeroes = request.response; populateHeader(superHeroes); showHeroes(superHeroes);
到以下:
request.onload = function() { var superHeroes = request.response; populateHeader(superHeroes); showHeroes(superHeroes); }
這解決了異步問(wèn)題,通過(guò)確保函數(shù)不運(yùn)行并傳遞 superHeroes
對(duì)象,直到響應(yīng)完成加載并可用。
所以總結(jié)一下,任何時(shí)候有些東西不起作用,并且一個(gè)值似乎不是你的代碼中的某個(gè)點(diǎn),你可以使用 console.log()
打印出來(lái), 看到發(fā)生了什么。
我們已經(jīng)解決了一個(gè)問(wèn)題,但我們?nèi)匀粓?jiān)持的錯(cuò)誤消息"TypeError:heroes is undefined",報(bào)告在第51行。讓我們現(xiàn)在調(diào)查一下,使用瀏覽器開發(fā)工具的更復(fù)雜的功能: ://developer.mozilla.org/zh-CN/docs/Tools/Debugger"> JavaScript調(diào)試器,因?yàn)樗贔irefox中調(diào)用。
注意:其他瀏覽器中也提供類似工具; Chrome中的"來(lái)源"標(biāo)簽,Safari中的調(diào)試器(請(qǐng)參閱 ="https://developer.apple.com/safari/tools/"class ="external"> Safari Web開發(fā)工具)等。
在Firefox中,"調(diào)試程序"選項(xiàng)卡如下所示:
alt ="">
這些工具的主要特點(diǎn)是能夠向代碼添加斷點(diǎn) - 這些是代碼的執(zhí)行停止的點(diǎn),在這一點(diǎn)上,你可以檢查當(dāng)前狀態(tài)的環(huán)境,看看發(fā)生了什么。
讓我們上班吧。 首先,我們知道錯(cuò)誤是在第51行拋出。單擊中心面板中的行號(hào)51,為它添加一個(gè)斷點(diǎn)(你會(huì)看到一個(gè)藍(lán)色的箭頭出現(xiàn)在它的頂部)。 現(xiàn)在刷新頁(yè)面(Cmd / Ctrl + R) - 瀏覽器將暫停執(zhí)行代碼在第51行。此時(shí),右側(cè)將更新顯示一些非常有用的信息。
alt ="">
showHeroes()
?the function we are currently in, and below we have request.onload
, which stores the event handler function containing the call to showHeroes()
.showHeroes
, and Window
(the global scope). Each scope can be expanded to show the values of variables inside the scope at the point that execution of the code was stopped.我們可以在這里找到一些非常有用的信息。
showHeroes
scope — you can see from this that the heroes variable is undefined, indicating that accessing the members
property of jsonObj
(first line of the function) didn't work.jsonObj
variable is storing a text string, not a JSON object.request.onload
in the Call Stack section. The view will update to show the request.onload
function in the center panel, and its scopes in the Scopes section.request.onload
scope, you'll see that the superHeroes
variable is a text string too, not an object. This settles it — our XMLHttpRequest
call is returning the JSON as text, not JSON.注意:我們希望您自己嘗試修復(fù)此問(wèn)題。 為了給您一個(gè)線索,您可以明確告訴XMLHttpRequest對(duì)象返回JSON格式 a>或在響應(yīng)到達(dá)后將返回的文本轉(zhuǎn)換為JSON 。 如果您遇到困難,請(qǐng)參閱我們的 "external"> fixed-ajax.html example。
注意:調(diào)試器標(biāo)簽還有許多其他有用的功能,我們?cè)谶@里沒(méi)有討論,例如條件斷點(diǎn)和監(jiān)視表達(dá)式。 有關(guān)詳細(xì)信息,請(qǐng)參見(jiàn)調(diào)試器頁(yè)面。
由于您的應(yīng)用程式變得越來(lái)越復(fù)雜,而且您開始使用更多的JavaScript,您可能會(huì)開始遇到性能問(wèn)題,特別是在較慢的設(shè)備上查看應(yīng)用程序時(shí)。 性能是一個(gè)大主題,我們沒(méi)有時(shí)間在這里詳細(xì)討論。 一些快速提示如下:
注意:Addy Osmani的 "> 寫快速,高效的JavaScript 包含大量細(xì)節(jié)和一些提高JavaScript性能的優(yōu)秀提示。 >
在本節(jié)中,我們將討論一些或更常見(jiàn)的跨瀏覽器JavaScript問(wèn)題。 我們會(huì)將此分解為:
在上一篇文章中,我們介紹了由于語(yǔ)言的性質(zhì),可以處理HTML和CSS錯(cuò)誤和無(wú)法識(shí)別的功能的一些方法。 JavaScript不像HTML和CSS那么容許,但是如果JavaScript引擎遇到錯(cuò)誤或無(wú)法識(shí)別的語(yǔ)法,更多的時(shí)候它會(huì)拋出錯(cuò)誤。
在最新版本的規(guī)范中定義了一些現(xiàn)代JavaScript語(yǔ)言特性( ECMAScript 6 / a> / ECMAScript下一頁(yè)),它在舊版瀏覽器中無(wú)法使用 。 其中一些是語(yǔ)法糖(基本上是一個(gè)更容易,更好的方式寫你已經(jīng)可以使用現(xiàn)有的功能),一些提供有趣的新的可能性。
例如:
<img>
element). They are not supported in IE at all?but are supported across all modern browsers.在最近的瀏覽器中也出現(xiàn)了許多新的API,它們?cè)谂f版瀏覽器中不起作用,例如:
有幾個(gè)策略用于處理與功能支持相關(guān)的瀏覽器之間的不兼容性; 讓我們探索最常見(jiàn)的。
注意:這些策略不存在于單獨(dú)的孤島中 - 您可以根據(jù)需要組合它們。 例如,您可以使用功能檢測(cè)來(lái)確定是否支持功能; 如果不是,你可以運(yùn)行代碼來(lái)加載polyfill或庫(kù)來(lái)處理缺乏支持。
特征檢測(cè)背后的想法是,您可以運(yùn)行測(cè)試以確定當(dāng)前瀏覽器是否支持JavaScript功能,然后有條件地運(yùn)行代碼,以在瀏覽器中提供可接受的體驗(yàn),并且不支持該功能。 作為一個(gè)快速示例,地理位置API (會(huì)公開 Web瀏覽器正在運(yùn)行的設(shè)備)具有使用的主要入口點(diǎn) - 全球地理位置屬性 docs / Web / API / Navigator"> Navigator 對(duì)象。 因此,您可以通過(guò)使用類似以下內(nèi)容來(lái)檢測(cè)瀏覽器是否支持地理位置:
if("geolocation" in navigator) { navigator.geolocation.getCurrentPosition(function(position) { // show the location on a map, perhaps using the Google Maps API }); } else { // Give the user a choice of static maps instead perhaps }
您還可以為CSS功能編寫此類測(cè)試,例如通過(guò)測(cè)試 style"> element.style.property (例如 paragraph.style.transform!== undefined
)。 但對(duì)于CSS和JavaScript,最好使用已建立的特性檢測(cè)庫(kù),而不是一直編寫自己的特性。 Modernizr是特征檢測(cè)測(cè)試的行業(yè)標(biāo)準(zhǔn)。
最后一點(diǎn),不要將特征檢測(cè)與瀏覽器嗅探(檢測(cè)特定瀏覽器訪問(wèn)網(wǎng)站)混淆,這是一個(gè)可怕的做法,應(yīng)該不惜一切代價(jià)。 有關(guān)詳情,請(qǐng)參見(jiàn)后面的使用錯(cuò)誤的瀏覽器嗅探代碼。
注意:已知某些功能無(wú)法檢測(cè) - 請(qǐng)參閱Modernizr的 Undetectables / a>。
注意:功能檢測(cè)將在后面的模塊中自己的專用文章中詳細(xì)介紹。
JavaScript庫(kù)基本上是第三方代碼單元,您可以附加到您的頁(yè)面,為您提供了大量的現(xiàn)成功能,可以立即使用,為您節(jié)省了大量的時(shí)間。 很多JavaScript庫(kù)可能已經(jīng)存在,因?yàn)樗麄兊拈_發(fā)人員正在編寫一組常見(jiàn)的實(shí)用函數(shù)來(lái)節(jié)省他們編寫未來(lái)項(xiàng)目的時(shí)間,并決定將其釋放到野外,因?yàn)槠渌丝赡軙?huì)發(fā)現(xiàn)它們也有用。
JavaScript庫(kù)往往有幾個(gè)主要的品種(有些庫(kù)將提供多種用途):
Document.querySelector()
/Document.querySelectorAll()
/Node
methods available across browsers, but it can still be useful when older browsers need supporting.在選擇要使用的庫(kù)時(shí),請(qǐng)確保它可以在您想要支持的瀏覽器集中工作,并徹底測(cè)試您的實(shí)現(xiàn)。 還要確保圖書館是受歡迎和良好支持,并且不太可能只是在下周過(guò)時(shí)。 與其他開發(fā)人員談?wù)勊麄兺扑]什么,看看有多少活動(dòng)和多少貢獻(xiàn)者的圖書館在Github(或其他任何地方存儲(chǔ))等。
注意: JavaScripting.com 可讓您了解有多少JavaScript庫(kù) 是可用的,并且可用于為特定目的查找?guī)臁?/span>
基本的庫(kù)使用往往包括下載庫(kù)的文件(JavaScript,可能是一些CSS或其他依賴項(xiàng))并將它們附加到您的頁(yè)面(例如通過(guò) zh-CN / docs / Web / HTML / Element / script"> < script>
元素),但是通常還有許多其他的使用選項(xiàng), a class ="external"> Bower 組件,或通過(guò) ="external"> Webpack 模塊bundler。 您將必須閱讀庫(kù)的各個(gè)安裝頁(yè)面以獲取更多信息。
注意:您在網(wǎng)絡(luò)上的旅行中也會(huì)遇到JavaScript框架,例如 Ember 和 Angular 。 雖然圖書館通??捎糜诮鉀Q單個(gè)問(wèn)題并放入現(xiàn)有網(wǎng)站,但框架往往更傾向于開發(fā)復(fù)雜的Web應(yīng)用程序的完整解決方案。
Polyfills還包括第三方JavaScript文件,您可以放入您的項(xiàng)目,但它們不同于庫(kù) - 而庫(kù)往往增強(qiáng)現(xiàn)有功能,使事情更容易,polyfills提供的功能根本不存在。 Polyfills使用JavaScript或其他技術(shù)完全支持瀏覽器不支持的功能。 例如,您可以使用 es6-promise 這樣的polyfill,使promise在瀏覽器中正常工作 不支持本機(jī)。
Modernizr的 HTML5 Cross Browser Polyfills 列表是一個(gè)很有用的地方 polyfills為不同的目的。 同樣,你應(yīng)該在使用它們之前研究它們 - 確保它們工作和維護(hù)。
讓我們通過(guò)一個(gè)練習(xí) - 在這個(gè)例子中,我們將使用一個(gè)Fetch polyfill來(lái)為舊的瀏覽器中的Fetch API提供支持; 但是我們還需要使用es6-promise polyfill,因?yàn)镕etch大量使用promise,并且不支持它們的瀏覽器仍然會(huì)遇到麻煩
<script>
element so they will be available on the page already when we start trying to use Fetch: <script src="es6-promise.js"></script> <script src="fetch.js"></script>
<script>
, add the following code:var myImage = document.querySelector('.my-image'); fetch('flowers.jpg').then(function(response) { response.blob().then(function(myBlob) { var objectURL = URL.createObjectURL(myBlob); myImage.src = objectURL; }); });
注意:您可以在 -finished.html"class ="external"> fetch-polyfill-finished.html (另請(qǐng)參閱 測(cè)試/跨瀏覽器測(cè)試/ javascript / fetch-polyfill-finished.html"class ="external">源代碼)。
注意:同樣,有很多不同的方法可以使用您將遇到的不同polyfill,請(qǐng)參閱每個(gè)polyfill的各個(gè)文檔。
你可能會(huì)想的一件事是"為什么我們總是加載polyfill代碼,即使我們不需要它嗎? 這是一個(gè)好點(diǎn) - 由于您的網(wǎng)站越來(lái)越復(fù)雜,并開始使用更多的庫(kù),polyfills等,您可以開始加載大量額外的代碼,這可能開始影響性能,特別是在功能不強(qiáng)大的設(shè)備上。 僅根據(jù)需要加載文件是有意義的。
這需要在JavaScript中進(jìn)行一些額外的設(shè)置。 您需要某種特性檢測(cè)測(cè)試來(lái)檢測(cè)瀏覽器是否支持我們正在嘗試使用的功能:
if (browserSupportsAllFeatures()) {
main();
} else {
loadScript('polyfills.js', main);
}
function main(err) {
// actual app code goes in here
}
所以首先我們運(yùn)行一個(gè)條件,檢查函數(shù) browserSupportsAllFeatures()
是否返回true。 如果是,我們運(yùn)行 main()
函數(shù),它將包含我們所有的應(yīng)用程序的代碼。 browserSupportsAllFeatures()
如下所示:
function browserSupportsAllFeatures() {
return window.Promise && window.fetch;
}
在這里,我們將測(cè)試 承諾
對(duì)象和 fetch()
函數(shù)存在于 瀏覽器。 如果兩個(gè)都做,函數(shù)返回true。 如果函數(shù)返回 false
,那么我們?cè)跅l件的第二部分運(yùn)行代碼 - 這將運(yùn)行一個(gè)名為loadScript()的函數(shù),它將polyfill加載到頁(yè)面中,然后運(yùn)行 main
)。 loadScript()
如下所示:
function loadScript(src, done) {
var js = document.createElement('script');
js.src = src;
js.onload = function() {
done();
};
js.onerror = function() {
done(new Error('Failed to load script ' + src));
};
document.head.appendChild(js);
}
此函數(shù)創(chuàng)建一個(gè)新的< script>
元素,然后將其 src
屬性設(shè)置為我們指定為第一個(gè)參數(shù)的路徑(\'polyfills.js\'
代碼>當(dāng)我們?cè)谏厦娴拇a中調(diào)用它)。 當(dāng)它加載時(shí),我們運(yùn)行我們指定的函數(shù)作為第二個(gè)參數(shù)( main()
)。 如果在加載腳本時(shí)出現(xiàn)錯(cuò)誤,我們?nèi)匀徽{(diào)用該函數(shù),但是有一個(gè)自定義錯(cuò)誤,我們可以檢索它來(lái)幫助調(diào)試問(wèn)題,如果它發(fā)生。
注意polyfills.js基本上是我們使用的兩個(gè)polyfill放在一起成一個(gè)文件。 我們手動(dòng)執(zhí)行此操作,但有一些更聰明的解決方案會(huì)自動(dòng)為您生成捆綁軟件 - 請(qǐng)參見(jiàn) Browserify (請(qǐng)參閱 "https://www.sitepoint.com/getting-started-browserify/"class ="external"> Browserify 了解基本教程)。 將JS文件合并成一個(gè)是個(gè)好主意 - 減少您需要的HTTP請(qǐng)求的數(shù)量,提高您的網(wǎng)站的性能。
您可以在 fetch-polyfill-only-when-needed.html (請(qǐng)參閱 測(cè)試/跨瀏覽器測(cè)試/ javascript / fetch-polyfill-only-when-needed.html"class ="external">源代碼)。 我們想表明,我們不能信任這個(gè)代碼 - 它最初是由菲利普·沃爾頓寫的。 請(qǐng)查看他的文章僅在需要時(shí)加載Polyfills 的原始代碼, 加上圍繞更廣泛的主題的很多有用的解釋)。
注意:有一些第三方選項(xiàng)可供考慮,例如 Polyfill.io > - 這是一個(gè)meta-polyfill庫(kù),它將查看每個(gè)瀏覽器的功能,并根據(jù)需要應(yīng)用polyfills,具體取決于您在代碼中使用的API和JS功能。
對(duì)于想要使用現(xiàn)代JavaScript功能的人來(lái)說(shuō),越來(lái)越受歡迎的另一個(gè)選擇是將使用ECMAScript 6 / ECMAScript 2015功能的代碼轉(zhuǎn)換為將在舊版瀏覽器中使用的版本。
注意:這被稱為"transpiling" - 你不是將代碼編譯到較低級(jí)別,以便在計(jì)算機(jī)上運(yùn)行(就像你用C代碼說(shuō)的)。 相反,你將它改變?yōu)橐粋€(gè)類似的抽象級(jí)別的語(yǔ)法,所以它可以以相同的方式使用,但在稍微不同的情況下(在這種情況下,將一種風(fēng)格的JavaScript轉(zhuǎn)換為另一種)。
例如,我們討論了箭頭函數(shù)(參見(jiàn) "external"> arrow-function.html live,并查看 /javascript/arrow-function.html"class ="external">源代碼),只能在最新的瀏覽器中使用:
() => { ... }
我們可以把這傳遞給一個(gè)傳統(tǒng)的老式匿名函數(shù),所以它可以在舊的瀏覽器中工作:
function() { ... }
推薦的JavaScript翻譯工具目前為 Babel 。 這提供了適于換膚的語(yǔ)言特征的換碼能力。 對(duì)于不能輕易地轉(zhuǎn)換成較舊版本的功能,Babel還提供了polyfills來(lái)提供支持。
嘗試使用Babel的最簡(jiǎn)單方法是使用在線版本,您可以在其中輸入源代碼 左邊,并在右邊輸出一個(gè)transpiled版本。
注意:有許多方法可以使用Babel(任務(wù)運(yùn)行器,自動(dòng)化工具等),您可以在 /"class ="external">設(shè)置頁(yè)面。
所有瀏覽器都有 user-agent 字符串,用于標(biāo)識(shí)瀏覽器是什么(版本,名稱,操作系統(tǒng)等)。在幾乎每天都使用Netscape或Internet Explorer的糟糕時(shí)期, 使用所謂的瀏覽器嗅探代碼來(lái)檢測(cè)用戶正在使用的瀏覽器,并為他們提供適當(dāng)?shù)拇a以在該瀏覽器上工作。
代碼用于看起來(lái)像這樣(雖然這是一個(gè)簡(jiǎn)化的例子):
var ua = navigator.userAgent; if(ua.indexOf('Firefox') !== -1) { // run Firefox-specific code } else if(ua.indexOf('Chrome') !== -1) { // run Chrome-specific code }
這個(gè)想法是相當(dāng)不錯(cuò)的 - 檢測(cè)什么瀏覽器正在查看網(wǎng)站,并運(yùn)行代碼,以確保瀏覽器將能夠使用您的網(wǎng)站OK。
請(qǐng)注意:嘗試現(xiàn)在打開JavaScript控制臺(tái),然后運(yùn)行navigator.userAgent,以查看返回的內(nèi)容。
然而,隨著時(shí)間的推移,開發(fā)人員開始看到這種方法的主要問(wèn)題。 一開始,代碼是容易出錯(cuò)的。 如果你知道一個(gè)功能不能工作在Firefox 10及以下,并實(shí)現(xiàn)代碼來(lái)檢測(cè)這一點(diǎn),然后Firefox 11出來(lái) - 這是支持這個(gè)功能? Firefox 11 proabbly將不被支持,因?yàn)樗皇荈irefox 10。你必須定期更改所有嗅探代碼。
許多開發(fā)人員實(shí)現(xiàn)了不良的瀏覽器嗅探代碼,并沒(méi)有維護(hù)它,瀏覽器開始被鎖定使用包含他們自從實(shí)現(xiàn)的功能的網(wǎng)站。 這變得非常普遍,瀏覽器開始說(shuō)謊他們?cè)谒麄兊挠脩舸碜址?或聲稱他們是所有瀏覽器)的瀏覽器,繞開嗅探代碼。 瀏覽器還實(shí)現(xiàn)了允許用戶更改瀏覽器在使用JavaScript查詢時(shí)報(bào)告的用戶代理字符串的功能。 這使所有瀏覽器嗅探更容易出錯(cuò),最終毫無(wú)意義。
注意:您應(yīng)該閱讀瀏覽器用戶代理字符串的歷史記錄 Aaron Andersen對(duì)這種情況的有用和有趣的采取。
這里學(xué)到的教訓(xùn)是 - 從不使用瀏覽器嗅探。 瀏覽器嗅探代碼在現(xiàn)代唯一真正的用例是,如果你正在實(shí)施一個(gè)bug的修復(fù)在一個(gè)特定的版本的特定瀏覽器。 但即使如此,大多數(shù)錯(cuò)誤在瀏覽器供應(yīng)商快速發(fā)布周期中很快得到修復(fù)。 它不會(huì)經(jīng)常出現(xiàn)。 功能檢測(cè)幾乎總是更好的選項(xiàng) - 如果您檢測(cè)某項(xiàng)功能是否受支持,則當(dāng)新的瀏覽器版本發(fā)布時(shí),您不需要更改代碼, 更可靠。
如果你在加入現(xiàn)有項(xiàng)目時(shí)遇到瀏覽器嗅探,看看它是否可以被更明智的東西取代。 瀏覽器嗅探會(huì)導(dǎo)致各種有趣的錯(cuò)誤,例如錯(cuò)誤1308462 。
在上一篇文章中,我們包含了很多關(guān)于處理CSS前綴的討論。 嗯,新的JavaScript實(shí)現(xiàn)有時(shí)也使用前綴,雖然JavaScript使用駝峰案而不是連字符像CSS。 例如,如果在名為 Object
的新shint API對(duì)象上使用了前綴:
mozObject
webkitObject
msObject
以下是一個(gè)示例,來(lái)自我們的暴力模仿演示(參見(jiàn) //github.com/mdn/violent-theremin"class ="external">源代碼),它使用 / docs / Web / API / Canvas_API"> Canvas API 和 Web Audio API / a>創(chuàng)建一個(gè)有趣(和嘈雜)的繪圖工具:
var AudioContext = window.AudioContext || window.webkitAudioContext; var audioCtx = new AudioContext();
在Web Audio API的情況下,Chrome / Opera通過(guò) webkit
前綴版本支持使用API的關(guān)鍵入口點(diǎn)(它們現(xiàn)在支持非前綴版本)。 解決這種情況的簡(jiǎn)單方法是創(chuàng)建在某些瀏覽器中以前綴為前綴的對(duì)象的新版本,并使其等于非前綴版本或前綴版本(或任何其他需要考慮的前綴版本) - 將使用當(dāng)前正在查看站點(diǎn)的瀏覽器支持的任何一個(gè)。
然后我們使用該對(duì)象來(lái)操縱API,而不是原始的。 在這種情況下,我們正在創(chuàng)建修改的 AudioContext 構(gòu)造函數(shù),然后創(chuàng)建一個(gè)新的音頻上下文實(shí)例 用于我們的Web音頻編碼。
此模式可應(yīng)用于任何前綴JavaScript功能。 JavaScript庫(kù)/ polyfills也使用這種代碼,盡可能地從開發(fā)人員抽象瀏覽器差異。
同樣,前綴功能從來(lái)不應(yīng)該用于生產(chǎn)網(wǎng)站 - 它們可能會(huì)更改或刪除沒(méi)有警告,并導(dǎo)致跨瀏覽器問(wèn)題。 如果您堅(jiān)持使用前綴功能,請(qǐng)確保使用正確的功能。 您可以在MDN參考頁(yè)以及 caniuse.com 等網(wǎng)站上查找哪些瀏覽器需要使用不同JavaScript / API功能的前綴。 如果你不確定,你也可以通過(guò)直接在瀏覽器中進(jìn)行一些測(cè)試來(lái)發(fā)現(xiàn)。
例如,嘗試進(jìn)入瀏覽器的開發(fā)者控制臺(tái)并開始輸入
window.AudioContext
如果您的瀏覽器支持此功能,它將自動(dòng)完成。
還有很多其他問(wèn)題你會(huì)遇到JavaScript; 最重要的事情,知道真正是如何在網(wǎng)上找到答案。 有關(guān)我們的最佳建議,請(qǐng)參閱HTML和CSS文章的查找?guī)椭糠?/a>。
這就是JavaScript。 簡(jiǎn)單嗎? 也許不是那么簡(jiǎn)單,但這篇文章應(yīng)該至少給你一個(gè)開始,和一些想法如何解決與JavaScript相關(guān)的問(wèn)題,你會(huì)遇到。
更多建議: