Handling common JavaScript problems

2018-05-15 17:26 更新
先决条件: 熟悉核心 HTML CSS JavaScript 语言; 有关跨浏览器测试的高级原则的概念。
目的: 能够诊断常见的JavaScript跨浏览器问题,并使用适当的工具和技术来修复它们。

JavaScript的麻烦

历史上,JavaScript遇到跨浏览器兼容性问题 - 在20世纪90年代,当时的主要浏览器选择(Internet Explorer和Netscape)使用不同语言实现的脚本(Netscape有JavaScript,IE有JScript,还提供VBScript作为 选项),而且至少JavaScript和JScript在某种程度上兼容(基于 > ECMAScript 规范),事情经常以冲突,不兼容的方式实现,导致开发者遭遇许多恶梦。

这种不兼容性问题在21世纪初仍然存在,因为旧的浏览器仍在使用中,仍然需要支持。 这是为什么像 jQuery 这样的库存在的主要原因之一 - 抽象出浏览器实现中的差异(例如, 如何发出HTTP请求)中的代码段,因此开发人员 只需要写一个简单的代码(见 jQuery.ajax() / code>)。 jQuery(或你使用的任何库)将处理在后台的差异,所以你不必。

事情从那时起好多了; 现代浏览器在支持"经典JavaScript功能"方面做得很好,并且使用这样的代码的要求已经减少,因为支持旧的浏览器的需求已经减少了(尽管记住它们没有完全消失)。

这些天,大多数跨浏览器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.

我们将探索所有这些问题,以下更多。

修复一般的JavaScript问题

正如我们在HTML / CSS的上一篇文章中所说的,您应该确保您的代码正常工作,然后再专注于跨平台 浏览器问题。 如果您还不熟悉 JavaScript JavaScript疑难解答的基本知识,您应该先学习该文章,然后再继续。 有一些常见的JavaScript问题,你会想要注意,例如:

  • 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个 错误JavaScript开发人员Make 有一些很好的讨论这些常见的错误和更多。

短绒

HTML和CSS 一样,您可以使用linter来确保更好的质量,更不易出错的JavaScript代码,它指出错误并可以标记 关于不良做法的警告等,并且在其错误/警告报告中被定制为更严格或更轻松。 JavaScript / ECMAScript是我们推荐的 JSHint class ="external"> ESLint ; 这些可以以各种方式使用,其中一些我们将在下面详细描述。

Online

JSHint首页提供了一个在线linter,它允许您在左侧输入JavaScript代码,并在右侧提供输出 ,包括指标,警告和错误。

alt ="">

Code editor plugins

将代码复制并粘贴到网页上以检查其有效性是不太方便的。 你真正想要的是一个linter,将适合你的标准工作流程,最少的麻烦。 许多代码编辑器都有linter插件,例如Github的 Atom 代码编辑器提供了一个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)将这些工具安装为命令行实用程序(通过CLI - 命令行界面) - 您必须安装 .org / en /"class ="external"> NodeJS 首先)。 例如,以下命令安装JSHint:

npm install -g jshint

然后,您可以将这些工具指向您要lint的JavaScript文件,例如:

alt ="">您还可以使用这些工具与任务运行程序/构建工具(如 Gulp "https://webpack.github.io/"class ="external"> Webpack 在开发过程中自动删除JavaScript。 (请参见后面的文章中的使用任务运行器自动化测试工具 。)有关ESLint选项的信息,请参见 ESLint集成; JSHint由Grunt支持开箱即用,并且还具有其他可用的集成,例如, 用于Webpack的JSHint加载器

注意:ESLint比JSHint需要更多的设置和配置,但它也更强大。

浏览器开发工具

浏览器开发者工具有许多有用的功能来帮助调试JavaScript。 首先,JavaScript控制台会报告代码中的错误。

制作我们的 external"> broken-ajax.html example(see the /broken-ajax.html"class ="external">源代码)。 如果你看看控制台,你会看到以下输出:

alt ="">

错误消息读为"TypeError:jsonObj is null",并且行号表示为37.如果我们查看源代码,相关代码段为:

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

  ...

因此,一旦我们尝试访问 jsonObj (您可能会期望,它应该是一个 JSON对象 >)。 这应该是使用以下XMLHttpRequest调用从外部 .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

您可能已经知道此代码有什么问题,但让我们进一步探索,以展示如何调查此代码。 首先,有一个控制台 API,允许JavaScript代码与浏览器的 JavaScript控制台。 它有许多功能可用,但你将经常使用的主要是 > console.log() ,它将自定义消息输出到控制台。

尝试在第31行下面插入以下行(以粗体显示):

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

在浏览器中刷新页面,您将在控制台中得到一个输出,如下所示:

alt ="">

console.log()输出显示 superHeroes 对象似乎不包含任何内容,虽然注意到错误消息现在已更改为"TypeError:heroes is 未定义"。 这表明错误是间歇性的,进一步证明这是某种异步错误。 让我们修复当前错误,并移除 console.log()行,并更新此代码块:

var superHeroes = request.response;
populateHeader(superHeroes);
showHeroes(superHeroes);

到以下:

request.onload = function() {
  var superHeroes = request.response;
  populateHeader(superHeroes);
  showHeroes(superHeroes);
}

这解决了异步问题,通过确保函数不运行并传递 superHeroes 对象,直到响应完成加载并可用。

所以总结一下,任何时候有些东西不起作用,并且一个值似乎不是你的代码中的某个点,你可以使用 console.log()打印出来, 看到发生了什么。

Using the JavaScript debugger

我们已经解决了一个问题,但我们仍然坚持的错误消息"TypeError:heroes is undefined",报告在第51行。让我们现在调查一下,使用浏览器开发工具的更复杂的功能: ://developer.mozilla.org/zh-CN/docs/Tools/Debugger"> JavaScript调试器,因为它在Firefox中调用。

注意:其他浏览器中也提供类似工具; Chrome中的"来源"标签,Safari中的调试器(请参阅 ="https://developer.apple.com/safari/tools/"class ="external"> Safari Web开发工具)等。

在Firefox中,"调试程序"选项卡如下所示:

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.

这些工具的主要特点是能够向代码添加断点 - 这些是代码的执行停止的点,在这一点上,你可以检查当前状态的环境,看看发生了什么。

让我们上班吧。 首先,我们知道错误是在第51行抛出。单击中心面板中的行号51,为它添加一个断点(你会看到一个蓝色的箭头出现在它的顶部)。 现在刷新页面(Cmd / Ctrl + R) - 浏览器将暂停执行代码在第51行。此时,右侧将更新显示一些非常有用的信息。

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.

注意:我们希望您自己尝试修复此问题。 为了给您一个线索,您可以明确告诉XMLHttpRequest对象返回JSON格式 a>或在响应到达后将返回的文本转换为JSON 如果您遇到困难,请参阅我们的 "external"> fixed-ajax.html example。

注意:调试器标签还有许多其他有用的功能,我们在这里没有讨论,例如条件断点和监视表达式。 有关详细信息,请参见调试器页面。

性能问题

由于您的应用程式变得越来越复杂,而且您开始使用更多的JavaScript,您可能会开始遇到性能问题,特别是在较慢的设备上查看应用程序时。 性能是一个大主题,我们没有时间在这里详细讨论。 一些快速提示如下:

  • 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 包含大量细节和一些提高JavaScript性能的优秀提示。 >

跨浏览器JavaScript问题

在本节中,我们将讨论一些或更常见的跨浏览器JavaScript问题。 我们会将此分解为:

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

使用现代JavaScript / API功能

上一篇文章中,我们介绍了由于语言的性质,可以处理HTML和CSS错误和无法识别的功能的一些方法。 JavaScript不像HTML和CSS那么容许,但是如果JavaScript引擎遇到错误或无法识别的语法,更多的时候它会抛出错误。

在最新版本的规范中定义了一些现代JavaScript语言特性( ECMAScript 6 / a> / ECMAScript下一页),它在旧版浏览器中无法使用 其中一些是语法糖(基本上是一个更容易,更好的方式写你已经可以使用现有的功能),一些提供有趣的新的可能性。

例如:

  • 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.

在最近的浏览器中也出现了许多新的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)

有几个策略用于处理与功能支持相关的浏览器之间的不兼容性; 让我们探索最常见的。

注意:这些策略不存在于单独的孤岛中 - 您可以根据需要组合它们。 例如,您可以使用功能检测来确定是否支持功能; 如果不是,你可以运行代码来加载polyfill或库来处理缺乏支持。

Feature detection

特征检测背后的想法是,您可以运行测试以确定当前浏览器是否支持JavaScript功能,然后有条件地运行代码,以在浏览器中提供可接受的体验,并且不支持该功能。 作为一个快速示例,地理位置API (会公开 Web浏览器正在运行的设备)具有使用的主要入口点 - 全球地理位置属性 docs / Web / API / Navigator"> Navigator 对象。 因此,您可以通过使用类似以下内容来检测浏览器是否支持地理位置:

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是特征检测测试的行业标准。

最后一点,不要将特征检测与浏览器嗅探(检测特定浏览器访问网站)混淆,这是一个可怕的做法,应该不惜一切代价。 有关详情,请参见后面的使用错误的浏览器嗅探代码

注意:已知某些功能无法检测 - 请参阅Modernizr的 Undetectables / a>。

注意:功能检测将在后面的模块中自己的专用文章中详细介绍。

Libraries

JavaScript库基本上是第三方代码单元,您可以附加到您的页面,为您提供了大量的现成功能,可以立即使用,为您节省了大量的时间。 很多JavaScript库可能已经存在,因为他们的开发人员正在编写一组常见的实用函数来节省他们编写未来项目的时间,并决定将其释放到野外,因为其他人可能会发现它们也有用。

JavaScript库往往有几个主要的品种(有些库将提供多种用途):

  • 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

在选择要使用的库时,请确保它可以在您想要支持的浏览器集中工作,并彻底测试您的实现。 还要确保图书馆是受欢迎和良好支持,并且不太可能只是在下周过时。 与其他开发人员谈谈他们推荐什么,看看有多少活动和多少贡献者的图书馆在Github(或其他任何地方存储)等。

注意: JavaScripting.com 可让您了解有多少JavaScript库 是可用的,并且可用于为特定目的查找库。

基本的库使用往往包括下载库的文件(JavaScript,可能是一些CSS或其他依赖项)并将它们附加到您的页面(例如通过 zh-CN / docs / Web / HTML / Element / script"> < script> 元素),但是通常还有许多其他的使用选项, a href ="https://bower.io/"class ="external"> Bower 组件,或通过 ="external"> Webpack 模块bundler。 您将必须阅读库的各个安装页面以获取更多信息。

注意:您在网络上的旅行中也会遇到JavaScript框架,例如 Ember Angular 虽然图书馆通常可用于解决单个问题并放入现有网站,但框架往往更倾向于开发复杂的Web应用程序的完整解决方案。

Polyfills

Polyfills还包括第三方JavaScript文件,您可以放入您的项目,但它们不同于库 - 而库往往增强现有功能,使事情更容易,polyfills提供的功能根本不存在。 Polyfills使用JavaScript或其他技术完全支持浏览器不支持的功能。 例如,您可以使用 es6-promise 这样的polyfill,使promise在浏览器中正常工作 不支持本机。

Modernizr的 HTML5 Cross Browser Polyfills 列表是一个很有用的地方 polyfills为不同的目的。 同样,你应该在使用它们之前研究它们 - 确保它们工作和维护。

让我们通过一个练习 - 在这个例子中,我们将使用一个Fetch polyfill来为旧的浏览器中的Fetch API提供支持; 但是我们还需要使用es6-promise polyfill,因为Fetch大量使用promise,并且不支持它们的浏览器仍然会遇到麻烦

  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,请参阅每个polyfill的各个文档。

你可能会想的一件事是"为什么我们总是加载polyfill代码,即使我们不需要它吗? 这是一个好点 - 由于您的网站越来越复杂,并开始使用更多的库,polyfills等,您可以开始加载大量额外的代码,这可能开始影响性能,特别是在功能不强大的设备上。 仅根据需要加载文件是有意义的。

这需要在JavaScript中进行一些额外的设置。 您需要某种特性检测测试来检测浏览器是否支持我们正在尝试使用的功能:

if (browserSupportsAllFeatures()) {
  main();
} else {
  loadScript('polyfills.js', main);
}

function main(err) {
  // actual app code goes in here
}

所以首先我们运行一个条件,检查函数 browserSupportsAllFeatures()是否返回true。 如果是,我们运行 main()函数,它将包含我们所有的应用程序的代码。 browserSupportsAllFeatures()如下所示:

function browserSupportsAllFeatures() {
  return window.Promise && window.fetch;
}

在这里,我们将测试 承诺 对象和 fetch() 函数存在于 浏览器。 如果两个都做,函数返回true。 如果函数返回 false ,那么我们在条件的第二部分运行代码 - 这将运行一个名为loadScript()的函数,它将polyfill加载到页面中,然后运行 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);
}

此函数创建一个新的< script> 元素,然后将其 src 属性设置为我们指定为第一个参数的路径(\'polyfills.js\' 代码>当我们在上面的代码中调用它)。 当它加载时,我们运行我们指定的函数作为第二个参数( main())。 如果在加载脚本时出现错误,我们仍然调用该函数,但是有一个自定义错误,我们可以检索它来帮助调试问题,如果它发生。

注意polyfills.js基本上是我们使用的两个polyfill放在一起成一个文件。 我们手动执行此操作,但有一些更聪明的解决方案会自动为您生成捆绑软件 - 请参见 Browserify (请参阅 "https://www.sitepoint.com/getting-started-browserify/"class ="external"> Browserify 了解基本教程)。 将JS文件合并成一个是个好主意 - 减少您需要的HTTP请求的数量,提高您的网站的性能。

您可以在 fetch-polyfill-only-when-needed.html (请参阅 测试/跨浏览器测试/ javascript / fetch-polyfill-only-when-needed.html"class ="external">源代码)。 我们想表明,我们不能信任这个代码 - 它最初是由菲利普·沃尔顿写的。 请查看他的文章仅在需要时加载Polyfills 的原始代码, 加上围绕更广泛的主题的很多有用的解释)。

注意:有一些第三方选项可供考虑,例如 Polyfill.io > - 这是一个meta-polyfill库,它将查看每个浏览器的功能,并根据需要应用polyfills,具体取决于您在代码中使用的API和JS功能。

JavaScript transpiling

对于想要使用现代JavaScript功能的人来说,越来越受欢迎的另一个选择是将使用ECMAScript 6 / ECMAScript 2015功能的代码转换为将在旧版浏览器中使用的版本。

注意:这被称为"transpiling" - 你不是将代码编译到较低级别,以便在计算机上运行(就像你用C代码说的)。 相反,你将它改变为一个类似的抽象级别的语法,所以它可以以相同的方式使用,但在稍微不同的情况下(在这种情况下,将一种风格的JavaScript转换为另一种)。

例如,我们讨论了箭头函数(参见 "external"> arrow-function.html live,并查看 /javascript/arrow-function.html"class ="external">源代码),只能在最新的浏览器中使用:

() => { ... }

我们可以把这传递给一个传统的老式匿名函数,所以它可以在旧的浏览器中工作:

function() { ... }

推荐的JavaScript翻译工具目前为 Babel 这提供了适于换肤的语言特征的换码能力。 对于不能轻易地转换成较旧版本的功能,Babel还提供了polyfills来提供支持。

尝试使用Babel的最简单方法是使用在线版本,您可以在其中输入源代码 左边,并在右边输出一个transpiled版本。

注意:有许多方法可以使用Babel(任务运行器,自动化工具等),您可以在 /"class ="external">设置页面

使用不良的浏览器嗅探代码

所有浏览器都有 user-agent 字符串,用于标识浏览器是什么(版本,名称,操作系统等)。在几乎每天都使用Netscape或Internet Explorer的糟糕时期, 使用所谓的浏览器嗅探代码来检测用户正在使用的浏览器,并为他们提供适当的代码以在该浏览器上工作。

代码用于看起来像这样(虽然这是一个简化的例子):

var ua = navigator.userAgent;

if(ua.indexOf('Firefox') !== -1) {
  // run Firefox-specific code
} else if(ua.indexOf('Chrome') !== -1) {
  // run Chrome-specific code
}

这个想法是相当不错的 - 检测什么浏览器正在查看网站,并运行代码,以确保浏览器将能够使用您的网站OK。

请注意:尝试现在打开JavaScript控制台,然后运行navigator.userAgent,以查看返回的内容。

然而,随着时间的推移,开发人员开始看到这种方法的主要问题。 一开始,代码是容易出错的。 如果你知道一个功能不能工作在Firefox 10及以下,并实现代码来检测这一点,然后Firefox 11出来 - 这是支持这个功能? Firefox 11 proabbly将不被支持,因为它不是Firefox 10。你必须定期更改所有嗅探代码。

许多开发人员实现了不良的浏览器嗅探代码,并没有维护它,浏览器开始被锁定使用包含他们自从实现的功能的网站。 这变得非常普遍,浏览器开始说谎他们在他们的用户代理字符串(或声称他们是所有浏览器)的浏览器,绕开嗅探代码。 浏览器还实现了允许用户更改浏览器在使用JavaScript查询时报告的用户代理字符串的功能。 这使所有浏览器嗅探更容易出错,最终毫无意义。

注意:您应该阅读浏览器用户代理字符串的历史记录 Aaron Andersen对这种情况的有用和有趣的采取。

这里学到的教训是 - 从不使用浏览器嗅探。 浏览器嗅探代码在现代唯一真正的用例是,如果你正在实施一个bug的修复在一个特定的版本的特定浏览器。 但即使如此,大多数错误在浏览器供应商快速发布周期中很快得到修复。 它不会经常出现。 功能检测几乎总是更好的选项 - 如果您检测某项功能是否受支持,则当新的浏览器版本发布时,您不需要更改代码, 更可靠。

如果你在加入现有项目时遇到浏览器嗅探,看看它是否可以被更明智的东西取代。 浏览器嗅探会导致各种有趣的错误,例如错误1308462

处理JavaScript前缀

在上一篇文章中,我们包含了很多关于处理CSS前缀的讨论。 嗯,新的JavaScript实现有时也使用前缀,虽然JavaScript使用驼峰案而不是连字符像CSS。 例如,如果在名为 Object 的新shint API对象上使用了前缀:

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

以下是一个示例,来自我们的暴力模仿演示(参见 //github.com/mdn/violent-theremin"class ="external">源代码),它使用 / docs / Web / API / Canvas_API"> Canvas API Web Audio API / a>创建一个有趣(和嘈杂)的绘图工具:

var AudioContext = window.AudioContext || window.webkitAudioContext;
var audioCtx = new AudioContext();

在Web Audio API的情况下,Chrome / Opera通过 webkit 前缀版本支持使用API的关键入口点(它们现在支持非前缀版本)。 解决这种情况的简单方法是创建在某些浏览器中以前缀为前缀的对象的新版本,并使其等于非前缀版本或前缀版本(或任何其他需要考虑的前缀版本) - 将使用当前正在查看站点的浏览器支持的任何一个。

然后我们使用该对象来操纵API,而不是原始的。 在这种情况下,我们正在创建修改的 AudioContext 构造函数,然后创建一个新的音频上下文实例 用于我们的Web音频编码。

此模式可应用于任何前缀JavaScript功能。 JavaScript库/ polyfills也使用这种代码,尽可能地从开发人员抽象浏览器差异。

同样,前缀功能从来不应该用于生产网站 - 它们可能会更改或删除没有警告,并导致跨浏览器问题。 如果您坚持使用前缀功能,请确保使用正确的功能。 您可以在MDN参考页以及 caniuse.com 等网站上查找哪些浏览器需要使用不同JavaScript / API功能的前缀。 如果你不确定,你也可以通过直接在浏览器中进行一些测试来发现。

例如,尝试进入浏览器的开发者控制台并开始输入

window.AudioContext

如果您的浏览器支持此功能,它将自动完成。

寻找帮助

还有很多其他问题你会遇到JavaScript; 最重要的事情,知道真正是如何在网上找到答案。 有关我们的最佳建议,请参阅HTML和CSS文章的查找帮助部分

概要

这就是JavaScript。 简单吗? 也许不是那么简单,但这篇文章应该至少给你一个开始,和一些想法如何解决与JavaScript相关的问题,你会遇到。

以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号