Handling common JavaScript problems

2018-05-15 17:26 更新
先決條件: 熟悉核心 HTML , CSS JavaScript 語言; 有關(guān)跨瀏覽器測試的高級原則的概念。
目的: 能夠診斷常見的JavaScript跨瀏覽器問題,并使用適當(dāng)?shù)墓ぞ吆图夹g(shù)來修復(fù)它們。

JavaScript的麻煩

歷史上,JavaScript遇到跨瀏覽器兼容性問題 - 在20世紀(jì)90年代,當(dāng)時(shí)的主要瀏覽器選擇(Internet Explorer和Netscape)使用不同語言實(shí)現(xiàn)的腳本(Netscape有JavaScript,IE有JScript,還提供VBScript作為 選項(xiàng)),而且至少JavaScript和JScript在某種程度上兼容(基于 > ECMAScript 規(guī)范),事情經(jīng)常以沖突,不兼容的方式實(shí)現(xiàn),導(dǎo)致開發(fā)者遭遇許多惡夢。

這種不兼容性問題在21世紀(jì)初仍然存在,因?yàn)榕f的瀏覽器仍在使用中,仍然需要支持。 這是為什么像 jQuery 這樣的庫存在的主要原因之一 - 抽象出瀏覽器實(shí)現(xiàn)中的差異(例如, 如何發(fā)出HTTP請求)中的代碼段,因此開發(fā)人員 只需要寫一個(gè)簡單的代碼(見 jQuery.ajax() / code>)。 jQuery(或你使用的任何庫)將處理在后臺(tái)的差異,所以你不必。

事情從那時(shí)起好多了; 現(xiàn)代瀏覽器在支持"經(jīng)典JavaScript功能"方面做得很好,并且使用這樣的代碼的要求已經(jīng)減少,因?yàn)橹С峙f的瀏覽器的需求已經(jīng)減少了(盡管記住它們沒有完全消失)。

這些天,大多數(shù)跨瀏覽器JavaScript問題被看到:

  • When bad quality browser sniffing code, feature detection code, and vendor prefix usage blocks browsers from running code they could otherwise use just fine.
  • When developers make use of new/nascent JavaScript features (for example ECMAScript 6 / ECMAScript Next features, modern Web APIs...) in their code, and find that such features don't work in older browsers.

我們將探索所有這些問題,以下更多。

修復(fù)一般的JavaScript問題

正如我們在HTML / CSS的上一篇文章中所說的,您應(yīng)該確保您的代碼正常工作,然后再專注于跨平臺(tái) 瀏覽器問題。 如果您還不熟悉 JavaScript JavaScript疑難解答的基本知識,您應(yīng)該先學(xué)習(xí)該文章,然后再繼續(xù)。 有一些常見的JavaScript問題,你會(huì)想要注意,例如:

  • Basic syntax and logic problems (again, check out Troubleshooting JavaScript).
  • Making sure variables, etc. are defined in the correct scope, and you are not running into conflicts between items declared in different places (see Function scope and conflicts).
  • Confusion about this, in terms of what scope it applies to, and therefore if its value is what you intended. You can read What is "this"? for a light introduction; you should also study examples like this one, which shows a typical pattern of saving a 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.
  • Incorrectly using functions inside loops — for example, in bad-for-loop.html (see source code), we loop through 10 iterations, each time creating a paragraph and adding an onclick event handler to it. When clicked, each one should alert a message containing its number (the value of 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.
  • Making sure asynchronous operations have returned before trying to use the values they return. For example, this Ajax example checks to make sure the request is complete and the response has been returned before trying to use the response for anything. This kind of operation has been made easier to handle by the introduction to Promises to the JavaScript language.

注意: Buggy JavaScript代碼:最常見的10個(gè) 錯(cuò)誤JavaScript開發(fā)人員Make 有一些很好的討論這些常見的錯(cuò)誤和更多。

短絨

HTML和CSS 一樣,您可以使用linter來確保更好的質(zhì)量,更不易出錯(cuò)的JavaScript代碼,它指出錯(cuò)誤并可以標(biāo)記 關(guān)于不良做法的警告等,并且在其錯(cuò)誤/警告報(bào)告中被定制為更嚴(yán)格或更輕松。 JavaScript / ECMAScript是我們推薦的 JSHint class ="external"> ESLint ; 這些可以以各種方式使用,其中一些我們將在下面詳細(xì)描述。

Online

JSHint首頁提供了一個(gè)在線linter,它允許您在左側(cè)輸入JavaScript代碼,并在右側(cè)提供輸出 ,包括指標(biāo),警告和錯(cuò)誤。

alt ="">

Code editor plugins

將代碼復(fù)制并粘貼到網(wǎng)頁上以檢查其有效性是不太方便的。 你真正想要的是一個(gè)linter,將適合你的標(biāo)準(zhǔn)工作流程,最少的麻煩。 許多代碼編輯器都有l(wèi)inter插件,例如Github的 Atom 代碼編輯器提供了一個(gè)JSHint插件。

安裝:

  1. Install Atom (if you haven't got an up-to-date version already installed) — download it from the Atom page linked above.
  2. Go to Atom's Preferences... dialog (e.g. by Choosing Atom > Preferences... on Mac, or File > Preferences... on Windows/Linux) and choose the Install option in the left-hand menu.
  3. In the Search packages text field, type "jslint" and press Enter/Return to search for linting-related packages.
  4. You should see a package called lint at the top of the list. Install this first (using the Install button), as other linters rely on it to work. After that, install the linter-jshint plugin.
  5. After the packages have finished installing, try loading up a JavaScript file: you'll see any issues highlighted with green (for warnings) and red (for errors) circles next to the line numbers, and a separate panel at the bottom provides line numbers, error messages, and sometimes suggested values or other fixes.

alt ="">其他流行的編輯有類似的linting包可用。 例如,請參閱 JSHint安裝頁面的"文本編輯器和IDE的插件"部分。

Other uses

還有其他方法來使用這種短絨; 您可以在 JSHint 用戶指南/開始"class ="external"> ESLint 安裝頁面。

值得一提的是命令行使用 - 您可以使用npm(Node Package Manager)將這些工具安裝為命令行實(shí)用程序(通過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ā)過程中自動(dòng)刪除JavaScript。 (請參見后面的文章中的使用任務(wù)運(yùn)行器自動(dòng)化測試工具 。)有關(guān)ESLint選項(xiàng)的信息,請參見 ESLint集成; JSHint由Grunt支持開箱即用,并且還具有其他可用的集成,例如, 用于Webpack的JSHint加載器

注意:ESLint比JSHint需要更多的設(shè)置和配置,但它也更強(qiáng)大。

瀏覽器開發(fā)工具

瀏覽器開發(fā)者工具有許多有用的功能來幫助調(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",并且行號表示為37.如果我們查看源代碼,相關(guān)代碼段為:

function populateHeader(jsonObj) {
  var myH1 = document.createElement('h1');
  myH1.textContent = jsonObj['squadName'];
  header.appendChild(myH1);

  ...

因此,一旦我們嘗試訪問 jsonObj (您可能會(huì)期望,它應(yīng)該是一個(gè) JSON對象 >)。 這應(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);

但這不行。

The Console API

您可能已經(jīng)知道此代碼有什么問題,但讓我們進(jìn)一步探索,以展示如何調(diào)查此代碼。 首先,有一個(gè)控制臺(tái) API,允許JavaScript代碼與瀏覽器的 JavaScript控制臺(tái)。 它有許多功能可用,但你將經(jīng)常使用的主要是 > console.log() ,它將自定義消息輸出到控制臺(tái)。

嘗試在第31行下面插入以下行(以粗體顯示):

console.log('Response value: ' + superHeroes);

在瀏覽器中刷新頁面,您將在控制臺(tái)中得到一個(gè)輸出,如下所示:

alt ="">

console.log()輸出顯示 superHeroes 對象似乎不包含任何內(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);
}

這解決了異步問題,通過確保函數(shù)不運(yùn)行并傳遞 superHeroes 對象,直到響應(yīng)完成加載并可用。

所以總結(jié)一下,任何時(shí)候有些東西不起作用,并且一個(gè)值似乎不是你的代碼中的某個(gè)點(diǎn),你可以使用 console.log()打印出來, 看到發(fā)生了什么。

Using the JavaScript debugger

我們已經(jīng)解決了一個(gè)問題,但我們?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中的"來源"標(biāo)簽,Safari中的調(diào)試器(請參閱 ="https://developer.apple.com/safari/tools/"class ="external"> Safari Web開發(fā)工具)等。

在Firefox中,"調(diào)試程序"選項(xiàng)卡如下所示:

alt ="">

  • On the left, you can select the script you want to debug (in this case we have only one).
  • The center panel shows the code in the selected script.
  • The right-hand panel shows useful details pertaining to the current environment — Breakpoints, Callstack and currently active Scopes.

這些工具的主要特點(diǎn)是能夠向代碼添加斷點(diǎn) - 這些是代碼的執(zhí)行停止的點(diǎn),在這一點(diǎn)上,你可以檢查當(dāng)前狀態(tài)的環(huán)境,看看發(fā)生了什么。

讓我們上班吧。 首先,我們知道錯(cuò)誤是在第51行拋出。單擊中心面板中的行號51,為它添加一個(gè)斷點(diǎn)(你會(huì)看到一個(gè)藍(lán)色的箭頭出現(xiàn)在它的頂部)。 現(xiàn)在刷新頁面(Cmd / Ctrl + R) - 瀏覽器將暫停執(zhí)行代碼在第51行。此時(shí),右側(cè)將更新顯示一些非常有用的信息。

alt ="">

  • Under Breakpoints, you'll see the details of the break-point you have set.
  • Under Call Stack, you'll see two entries — this is basically a list of the series of functions that were invoked to cause the current function to be invoked. At the top, we have showHeroes()?the function we are currently in, and below we have request.onload, which stores the event handler function containing the call to showHeroes().
  • Under Scopes, you'll see the currently active scope for the function we are looking at. We only have two — 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.

我們可以在這里找到一些非常有用的信息。

  1. Expand the 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.
  2. You can also see that the jsonObj variable is storing a text string, not a JSON object.
  3. Exploring further down the call stack, click 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.
  4. Now if you expand the 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ù)此問題。 為了給您一個(gè)線索,您可以明確告訴XMLHttpRequest對象返回JSON格式 a>或在響應(yīng)到達(dá)后將返回的文本轉(zhuǎn)換為JSON 如果您遇到困難,請參閱我們的 "external"> fixed-ajax.html example。

注意:調(diào)試器標(biāo)簽還有許多其他有用的功能,我們在這里沒有討論,例如條件斷點(diǎn)和監(jiān)視表達(dá)式。 有關(guān)詳細(xì)信息,請參見調(diào)試器頁面。

性能問題

由于您的應(yīng)用程式變得越來越復(fù)雜,而且您開始使用更多的JavaScript,您可能會(huì)開始遇到性能問題,特別是在較慢的設(shè)備上查看應(yīng)用程序時(shí)。 性能是一個(gè)大主題,我們沒有時(shí)間在這里詳細(xì)討論。 一些快速提示如下:

  • To avoid loading more JavaScript than you need, bundle your scripts into a single file using a solution like Browserify. In general, reducing the number of HTTP requests is very good for performance.
  • Make your files even smaller by minifying them before you load them onto your production server. Minifying squashes all the code together onto a huge single line, making it take up far less file size. It is ugly, but you don't need to read it when it is finished! This is best done using a minification tool like Uglify (there's also an online version — see JSCompress.com)
  • When using APIs, make sure you turn off the API features when they are not being used; some API calls can be really expensive on processing power. For example, when showing a video stream, make sure it is turned off when you can't see it. When tracking a device's location using repeated Geolocation calls, make sure you turn it off when the user stops using it.
  • Animations can be really costly for performance. A lot of JavaScript libraries provide animation capabilities programmed by JavaSCript, but it is much more cost effective to do the animations via native browser features like CSS Animations (or the nascent Web Animations API) than JavaScript. Read Brian Birtles' Animating like you just don’t care with Element.animate for some really useful theory on why animation is expensive, tips on how to improve animation performance, and information on the Web Animations API.

注意:Addy Osmani的 "> 寫快速,高效的JavaScript 包含大量細(xì)節(jié)和一些提高JavaScript性能的優(yōu)秀提示。 >

跨瀏覽器JavaScript問題

在本節(jié)中,我們將討論一些或更常見的跨瀏覽器JavaScript問題。 我們會(huì)將此分解為:

  • Using modern core JavaScript features
  • Using modern Web API features
  • Using bad browser sniffing code
  • Performance problems

使用現(xiàn)代JavaScript / API功能

上一篇文章中,我們介紹了由于語言的性質(zhì),可以處理HTML和CSS錯(cuò)誤和無法識別的功能的一些方法。 JavaScript不像HTML和CSS那么容許,但是如果JavaScript引擎遇到錯(cuò)誤或無法識別的語法,更多的時(shí)候它會(huì)拋出錯(cuò)誤。

在最新版本的規(guī)范中定義了一些現(xiàn)代JavaScript語言特性( ECMAScript 6 / a> / ECMAScript下一頁),它在舊版瀏覽器中無法使用 其中一些是語法糖(基本上是一個(gè)更容易,更好的方式寫你已經(jīng)可以使用現(xiàn)有的功能),一些提供有趣的新的可能性。

例如:

  • Promises are a great new feature for performing asynchronous operations and making sure those operations are complete before code that relies on their results is used for something else. As an example, the Fetch API (a modern equivalent to XMLHTTPRequest) uses promises to fetch resources across the network and make sure that the response has been returned before they are used (for example, displaying an image inside an <img> element). They are not supported in IE at all?but are supported across all modern browsers.
  • Arrow functions provide a shorter, more convenient syntax for writing anonymous functions, which also has other advantages (see Arrow functions). For a quick example, see arrow-function.html. Arrow functions are supported across all modern browsers, except for IE and Safari.
  • Declaring strict mode at the top of your JavaScript code causes it to be parsed with a stricter set of rules, meaning that more warnings and errors will be thrown, and some things will be disallowed that would otherwise be acceptable. It is arguably a good idea to use strict mode, as it makes for better, more efficient code, however it has limited/patchy support across browsers (see Strict mode in browsers).
  • Typed arrays allow JavaScript code to access and manipulate raw binary data, which is necessary as browser APIs for example start to manipulate streams of raw video and audio data. These are available in IE10 and above, and all modern browsers.

在最近的瀏覽器中也出現(xiàn)了許多新的API,它們在舊版瀏覽器中不起作用,例如:

  • IndexedDB API, Web Storage API, and others for storing website data on the client-side.
  • Web Workers API for running JavaScript in a separate thread, helping to improve performance.
  • WebGL API for real 3D graphics.
  • Web Audio API for advanced audio manipulation.
  • WebRTC API for multi-person, real-time video/audio connectivity (e.g. video conferencing).
  • WebVR API for engineering virtual reality experiences in the browser (e.g. controlling a 3D view with data input from VR Hardware)

有幾個(gè)策略用于處理與功能支持相關(guān)的瀏覽器之間的不兼容性; 讓我們探索最常見的。

注意:這些策略不存在于單獨(dú)的孤島中 - 您可以根據(jù)需要組合它們。 例如,您可以使用功能檢測來確定是否支持功能; 如果不是,你可以運(yùn)行代碼來加載polyfill或庫來處理缺乏支持。

Feature detection

特征檢測背后的想法是,您可以運(yùn)行測試以確定當(dāng)前瀏覽器是否支持JavaScript功能,然后有條件地運(yùn)行代碼,以在瀏覽器中提供可接受的體驗(yàn),并且不支持該功能。 作為一個(gè)快速示例,地理位置API (會(huì)公開 Web瀏覽器正在運(yùn)行的設(shè)備)具有使用的主要入口點(diǎn) - 全球地理位置屬性 docs / Web / API / Navigator"> Navigator 對象。 因此,您可以通過使用類似以下內(nèi)容來檢測瀏覽器是否支持地理位置:

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功能編寫此類測試,例如通過測試 style"> element.style.property (例如 paragraph.style.transform!== undefined )。 但對于CSS和JavaScript,最好使用已建立的特性檢測庫,而不是一直編寫自己的特性。 Modernizr是特征檢測測試的行業(yè)標(biāo)準(zhǔn)。

最后一點(diǎn),不要將特征檢測與瀏覽器嗅探(檢測特定瀏覽器訪問網(wǎng)站)混淆,這是一個(gè)可怕的做法,應(yīng)該不惜一切代價(jià)。 有關(guān)詳情,請參見后面的使用錯(cuò)誤的瀏覽器嗅探代碼

注意:已知某些功能無法檢測 - 請參閱Modernizr的 Undetectables / a>。

注意:功能檢測將在后面的模塊中自己的專用文章中詳細(xì)介紹。

Libraries

JavaScript庫基本上是第三方代碼單元,您可以附加到您的頁面,為您提供了大量的現(xiàn)成功能,可以立即使用,為您節(jié)省了大量的時(shí)間。 很多JavaScript庫可能已經(jīng)存在,因?yàn)樗麄兊拈_發(fā)人員正在編寫一組常見的實(shí)用函數(shù)來節(jié)省他們編寫未來項(xiàng)目的時(shí)間,并決定將其釋放到野外,因?yàn)槠渌丝赡軙?huì)發(fā)現(xiàn)它們也有用。

JavaScript庫往往有幾個(gè)主要的品種(有些庫將提供多種用途):

  • Utility libraries: Provide a bunch of functions to make mundane tasks easier and less boring to manage. jQuery for example provides its own fully-featured selectors and DOM manipuation libraries, to allow CSS-selector type selecting of elements in JavaScript and easier DOM building. It is not so important now we have modern features like Document.querySelector()/Document.querySelectorAll()/Node methods available across browsers, but it can still be useful when older browsers need supporting.
  • Convenience libraries: Make difficult things easier to do. For example, the WebGL API is really complex and challenging to use when you write it directly, so the Three.js library (and others) is built on top of WebGL and provides a much easier API for creating common 3D objects, lighting, textures, etc. The Service Worker API is also very complex to use, so code libraries have started appearing to make common Service Worker uses-cases much easier to implement (see the Service Worker Cookbook for several useful code samples).
  • Effects libraries: These libraries are designed to allow you to easily add special effects to your websites. This was more useful back when DHTML was a popular buzzword, and implementing effect involved a lot of complex JavaScript, but these days browsers have a lot of built in CSS3 features and APIs to implementing effects more easily. See JavaScripting.com/animation for a list of libraries.
  • UI libraries: Provide methods for implementing complex UI features that would otherwise be challenging to implement and get working cross browser, for example jQuery UI and Foundation. These tend to be used as the basis of an entire site layout; it is often difficult to drop them in just for one UI feature.
  • Normalization libraries: Give you a simple syntax that allows you to easily complete a task without having to worry about cross browser differences. The library will manipulate appropriate APIs in the background so the functionality will work whatever the browser (in theory). For example, LocalForage is a library for client-side data storage, which provides a simple syntax for storing a retrieving data. In the background, it uses the best API the browser has available for storing the data, whether that is IndexedDB, Web Storage, or even WebSQL (which is now deprecated, but is still supported in some older versions of Safari/IE). As another example, jQuery

在選擇要使用的庫時(shí),請確保它可以在您想要支持的瀏覽器集中工作,并徹底測試您的實(shí)現(xiàn)。 還要確保圖書館是受歡迎和良好支持,并且不太可能只是在下周過時(shí)。 與其他開發(fā)人員談?wù)勊麄兺扑]什么,看看有多少活動(dòng)和多少貢獻(xiàn)者的圖書館在Github(或其他任何地方存儲(chǔ))等。

注意: JavaScripting.com 可讓您了解有多少JavaScript庫 是可用的,并且可用于為特定目的查找?guī)臁?/span>

基本的庫使用往往包括下載庫的文件(JavaScript,可能是一些CSS或其他依賴項(xiàng))并將它們附加到您的頁面(例如通過 zh-CN / docs / Web / HTML / Element / script"> < script> 元素),但是通常還有許多其他的使用選項(xiàng), a class ="external"> Bower 組件,或通過 ="external"> Webpack 模塊bundler。 您將必須閱讀庫的各個(gè)安裝頁面以獲取更多信息。

注意:您在網(wǎng)絡(luò)上的旅行中也會(huì)遇到JavaScript框架,例如 Ember Angular 。 雖然圖書館通??捎糜诮鉀Q單個(gè)問題并放入現(xiàn)有網(wǎng)站,但框架往往更傾向于開發(fā)復(fù)雜的Web應(yīng)用程序的完整解決方案。

Polyfills

Polyfills還包括第三方JavaScript文件,您可以放入您的項(xiàng)目,但它們不同于庫 - 而庫往往增強(qiáng)現(xiàn)有功能,使事情更容易,polyfills提供的功能根本不存在。 Polyfills使用JavaScript或其他技術(shù)完全支持瀏覽器不支持的功能。 例如,您可以使用 es6-promise 這樣的polyfill,使promise在瀏覽器中正常工作 不支持本機(jī)。

Modernizr的 HTML5 Cross Browser Polyfills 列表是一個(gè)很有用的地方 polyfills為不同的目的。 同樣,你應(yīng)該在使用它們之前研究它們 - 確保它們工作和維護(hù)。

讓我們通過一個(gè)練習(xí) - 在這個(gè)例子中,我們將使用一個(gè)Fetch polyfill來為舊的瀏覽器中的Fetch API提供支持; 但是我們還需要使用es6-promise polyfill,因?yàn)镕etch大量使用promise,并且不支持它們的瀏覽器仍然會(huì)遇到麻煩

  1. To get started, make a local copy of our fetch-polyfill.html example and our nice image of some flowers in a new directory. We are going to write code to fetch the flowers image and display it in the page.
  2. Next, save copies of the Fetch polyfill and the es6-promises polyfill in the same directory as the HTML.
  3. Apply the polyfill scripts to the page using the following code — place these above the existing <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>
  4. Inside the original <script>, add the following code:
  5. 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;
      });
    });
  6. Now if you load it in a browser that doesn't support Fetch (Safari and IE are obvious candidates), you should still see the flower image appear — cool!?

注意:同樣,有很多不同的方法可以使用您將遇到的不同polyfill,請參閱每個(gè)polyfill的各個(gè)文檔。

你可能會(huì)想的一件事是"為什么我們總是加載polyfill代碼,即使我們不需要它嗎? 這是一個(gè)好點(diǎn) - 由于您的網(wǎng)站越來越復(fù)雜,并開始使用更多的庫,polyfills等,您可以開始加載大量額外的代碼,這可能開始影響性能,特別是在功能不強(qiáng)大的設(shè)備上。 僅根據(jù)需要加載文件是有意義的。

這需要在JavaScript中進(jìn)行一些額外的設(shè)置。 您需要某種特性檢測測試來檢測瀏覽器是否支持我們正在嘗試使用的功能:

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;
}

在這里,我們將測試 承諾 對象和 fetch() 函數(shù)存在于 瀏覽器。 如果兩個(gè)都做,函數(shù)返回true。 如果函數(shù)返回 false ,那么我們在條件的第二部分運(yùn)行代碼 - 這將運(yùn)行一個(gè)名為loadScript()的函數(shù),它將polyfill加載到頁面中,然后運(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)我們在上面的代碼中調(diào)用它)。 當(dāng)它加載時(shí),我們運(yùn)行我們指定的函數(shù)作為第二個(gè)參數(shù)( main())。 如果在加載腳本時(shí)出現(xiàn)錯(cuò)誤,我們?nèi)匀徽{(diào)用該函數(shù),但是有一個(gè)自定義錯(cuò)誤,我們可以檢索它來幫助調(diào)試問題,如果它發(fā)生。

注意polyfills.js基本上是我們使用的兩個(gè)polyfill放在一起成一個(gè)文件。 我們手動(dòng)執(zhí)行此操作,但有一些更聰明的解決方案會(huì)自動(dòng)為您生成捆綁軟件 - 請參見 Browserify (請參閱 "https://www.sitepoint.com/getting-started-browserify/"class ="external"> Browserify 了解基本教程)。 將JS文件合并成一個(gè)是個(gè)好主意 - 減少您需要的HTTP請求的數(shù)量,提高您的網(wǎng)站的性能。

您可以在 fetch-polyfill-only-when-needed.html (請參閱 測試/跨瀏覽器測試/ javascript / fetch-polyfill-only-when-needed.html"class ="external">源代碼)。 我們想表明,我們不能信任這個(gè)代碼 - 它最初是由菲利普·沃爾頓寫的。 請查看他的文章僅在需要時(shí)加載Polyfills 的原始代碼, 加上圍繞更廣泛的主題的很多有用的解釋)。

注意:有一些第三方選項(xiàng)可供考慮,例如 Polyfill.io > - 這是一個(gè)meta-polyfill庫,它將查看每個(gè)瀏覽器的功能,并根據(jù)需要應(yīng)用polyfills,具體取決于您在代碼中使用的API和JS功能。

JavaScript transpiling

對于想要使用現(xiàn)代JavaScript功能的人來說,越來越受歡迎的另一個(gè)選擇是將使用ECMAScript 6 / ECMAScript 2015功能的代碼轉(zhuǎn)換為將在舊版瀏覽器中使用的版本。

注意:這被稱為"transpiling" - 你不是將代碼編譯到較低級別,以便在計(jì)算機(jī)上運(yùn)行(就像你用C代碼說的)。 相反,你將它改變?yōu)橐粋€(gè)類似的抽象級別的語法,所以它可以以相同的方式使用,但在稍微不同的情況下(在這種情況下,將一種風(fēng)格的JavaScript轉(zhuǎn)換為另一種)。

例如,我們討論了箭頭函數(shù)(參見 "external"> arrow-function.html live,并查看 /javascript/arrow-function.html"class ="external">源代碼),只能在最新的瀏覽器中使用:

() => { ... }

我們可以把這傳遞給一個(gè)傳統(tǒng)的老式匿名函數(shù),所以它可以在舊的瀏覽器中工作:

function() { ... }

推薦的JavaScript翻譯工具目前為 Babel 這提供了適于換膚的語言特征的換碼能力。 對于不能輕易地轉(zhuǎn)換成較舊版本的功能,Babel還提供了polyfills來提供支持。

嘗試使用Babel的最簡單方法是使用在線版本,您可以在其中輸入源代碼 左邊,并在右邊輸出一個(gè)transpiled版本。

注意:有許多方法可以使用Babel(任務(wù)運(yùn)行器,自動(dòng)化工具等),您可以在 /"class ="external">設(shè)置頁面。

使用不良的瀏覽器嗅探代碼

所有瀏覽器都有 user-agent 字符串,用于標(biāo)識瀏覽器是什么(版本,名稱,操作系統(tǒng)等)。在幾乎每天都使用Netscape或Internet Explorer的糟糕時(shí)期, 使用所謂的瀏覽器嗅探代碼來檢測用戶正在使用的瀏覽器,并為他們提供適當(dāng)?shù)拇a以在該瀏覽器上工作。

代碼用于看起來像這樣(雖然這是一個(gè)簡化的例子):

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ò)的 - 檢測什么瀏覽器正在查看網(wǎng)站,并運(yùn)行代碼,以確保瀏覽器將能夠使用您的網(wǎng)站OK。

請注意:嘗試現(xiàn)在打開JavaScript控制臺(tái),然后運(yùn)行navigator.userAgent,以查看返回的內(nèi)容。

然而,隨著時(shí)間的推移,開發(fā)人員開始看到這種方法的主要問題。 一開始,代碼是容易出錯(cuò)的。 如果你知道一個(gè)功能不能工作在Firefox 10及以下,并實(shí)現(xiàn)代碼來檢測這一點(diǎn),然后Firefox 11出來 - 這是支持這個(gè)功能? Firefox 11 proabbly將不被支持,因?yàn)樗皇荈irefox 10。你必須定期更改所有嗅探代碼。

許多開發(fā)人員實(shí)現(xiàn)了不良的瀏覽器嗅探代碼,并沒有維護(hù)它,瀏覽器開始被鎖定使用包含他們自從實(shí)現(xiàn)的功能的網(wǎng)站。 這變得非常普遍,瀏覽器開始說謊他們在他們的用戶代理字符串(或聲稱他們是所有瀏覽器)的瀏覽器,繞開嗅探代碼。 瀏覽器還實(shí)現(xiàn)了允許用戶更改瀏覽器在使用JavaScript查詢時(shí)報(bào)告的用戶代理字符串的功能。 這使所有瀏覽器嗅探更容易出錯(cuò),最終毫無意義。

注意:您應(yīng)該閱讀瀏覽器用戶代理字符串的歷史記錄 Aaron Andersen對這種情況的有用和有趣的采取。

這里學(xué)到的教訓(xùn)是 - 從不使用瀏覽器嗅探。 瀏覽器嗅探代碼在現(xiàn)代唯一真正的用例是,如果你正在實(shí)施一個(gè)bug的修復(fù)在一個(gè)特定的版本的特定瀏覽器。 但即使如此,大多數(shù)錯(cuò)誤在瀏覽器供應(yīng)商快速發(fā)布周期中很快得到修復(fù)。 它不會(huì)經(jīng)常出現(xiàn)。 功能檢測幾乎總是更好的選項(xiàng) - 如果您檢測某項(xiàng)功能是否受支持,則當(dāng)新的瀏覽器版本發(fā)布時(shí),您不需要更改代碼, 更可靠。

如果你在加入現(xiàn)有項(xiàng)目時(shí)遇到瀏覽器嗅探,看看它是否可以被更明智的東西取代。 瀏覽器嗅探會(huì)導(dǎo)致各種有趣的錯(cuò)誤,例如錯(cuò)誤1308462 。

處理JavaScript前綴

在上一篇文章中,我們包含了很多關(guān)于處理CSS前綴的討論。 嗯,新的JavaScript實(shí)現(xiàn)有時(shí)也使用前綴,雖然JavaScript使用駝峰案而不是連字符像CSS。 例如,如果在名為 Object 的新shint API對象上使用了前綴:

  • Mozilla would use mozObject
  • Chrome/Opera/Safari would use webkitObject
  • Microsoft would use msObject

以下是一個(gè)示例,來自我們的暴力模仿演示(參見 //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通過 webkit 前綴版本支持使用API的關(guān)鍵入口點(diǎn)(它們現(xiàn)在支持非前綴版本)。 解決這種情況的簡單方法是創(chuàng)建在某些瀏覽器中以前綴為前綴的對象的新版本,并使其等于非前綴版本或前綴版本(或任何其他需要考慮的前綴版本) - 將使用當(dāng)前正在查看站點(diǎn)的瀏覽器支持的任何一個(gè)。

然后我們使用該對象來操縱API,而不是原始的。 在這種情況下,我們正在創(chuàng)建修改的 AudioContext 構(gòu)造函數(shù),然后創(chuàng)建一個(gè)新的音頻上下文實(shí)例 用于我們的Web音頻編碼。

此模式可應(yīng)用于任何前綴JavaScript功能。 JavaScript庫/ polyfills也使用這種代碼,盡可能地從開發(fā)人員抽象瀏覽器差異。

同樣,前綴功能從來不應(yīng)該用于生產(chǎn)網(wǎng)站 - 它們可能會(huì)更改或刪除沒有警告,并導(dǎo)致跨瀏覽器問題。 如果您堅(jiān)持使用前綴功能,請確保使用正確的功能。 您可以在MDN參考頁以及 caniuse.com 等網(wǎng)站上查找哪些瀏覽器需要使用不同JavaScript / API功能的前綴。 如果你不確定,你也可以通過直接在瀏覽器中進(jìn)行一些測試來發(fā)現(xiàn)。

例如,嘗試進(jìn)入瀏覽器的開發(fā)者控制臺(tái)并開始輸入

window.AudioContext

如果您的瀏覽器支持此功能,它將自動(dòng)完成。

尋找?guī)椭?/span>

還有很多其他問題你會(huì)遇到JavaScript; 最重要的事情,知道真正是如何在網(wǎng)上找到答案。 有關(guān)我們的最佳建議,請參閱HTML和CSS文章的查找?guī)椭糠?/a>。

概要

這就是JavaScript。 簡單嗎? 也許不是那么簡單,但這篇文章應(yīng)該至少給你一個(gè)開始,和一些想法如何解決與JavaScript相關(guān)的問題,你會(huì)遇到。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號