JavaScript/最佳实践
本章将介绍当前 JavaScript 社区标准和每个程序员都应该了解的最佳实践。
此方法已弃用。请使用 innerHTML
或 DOM 操作方法代替。
在 XHTML 中,document.write
无法正常工作,但可以使用 DOM 操作方法实现相同的效果 [1]。
尽量避免仅用于执行 JavaScript 代码的链接。
<a href="javascript:resumeFancyVersion()">See my résumé!</a>
相反,请考虑
<a href="resumePlainVersion.html" onclick="return !resumeFancyVersion()">See my résumé!</a>
启用 JavaScript 的用户将获得包含 JavaScript 增强内容的版本(可能使用 DHTML),而没有 JavaScript 的用户将被重定向到静态提供该内容的 XHTML 页面。这比拥有缺少正常 href 属性值的 <a> 元素更易于访问。首先,它使用 正确的语言语义。其次,它为没有 JavaScript 的用户提供了访问您内容的途径。第三,它检测函数执行是否成功,并在出现故障的情况下将启用 JS 的读者也重定向。
onclick 表达式计算为一个布尔值。如果该函数成功执行了所需的效果并返回 true,则 onclick 将返回失败,并且超链接不会执行。当该函数因任何原因而失败时,false、null 或 undefined 值将计算为 true,并且不会阻止链接执行。或者,如果您不想提供静态等效项,可以在语义要求较低的元素中嵌入 onclick 属性
<strong onclick="resumeFancyVersion()">See my résumé!</strong>
因此,任何用户代理在阅读减少的 <a> 元素时都不会感到困惑。
许多人使用 JavaScript 函数来立即捕获表单条目中最常见的错误类型。例如,一些网页表单要求人们两次输入相同的内容。其他网页表单要求人们输入电子邮件地址。然后他们快速检查输入的内容是否至少看起来像电子邮件地址
function isValidEmail(string) {
// These comments use the following terms from RFC2822:
// local-part, domain, domain-literal and dot-atom.
// Does the address contain a local-part followed an @ followed by a domain?
// Note the use of lastIndexOf to find the last @ in the address
// since a valid email address may have a quoted @ in the local-part.
// Does the domain name have at least two parts, i.e. at least one dot,
// after the @? If not, is it a domain-literal?
// This will accept some invalid email addresses
// BUT it doesn't reject valid ones.
var atSym = string.lastIndexOf("@");
if (atSym < 1) { return false; } // no local-part
if (atSym == string.length - 1) { return false; } // no domain
if (atSym > 64) { return false; } // there may only be 64 octets in the local-part
if (string.length - atSym > 255) { return false; } // there may only be 255 octets in the domain
// Is the domain plausible?
var lastDot = string.lastIndexOf(".");
// Check if it is a dot-atom such as example.com
if (lastDot > atSym + 1 && lastDot < string.length - 1) { return true; }
// Check if could be a domain-literal.
if (string.charAt(atSym + 1) == '[' && string.charAt(string.length - 1) == ']') { return true; }
return false;
}
不幸的是,一些其他的“电子邮件验证”JavaScript 函数拒绝了完全有效的电子邮件地址。例如,一些 错误地拒绝了 包含“+”号的有效地址。
原始的电子邮件地址语法 (RFC 821) 确实允许“+”号。 RFC 822 于同月(1982 年 8 月)发布,也允许它们。当前版本的语法在 RFC2821/RFC2822 中给出。 RFC3696 的第 3 节提供了关于非典型有效电子邮件地址的有用示例。
验证后,许多 JavaScript 程序员使用 encodeURIComponent()
对电子邮件地址进行编码,以解决某些无法正确处理加号的客户端语言。 [1][2]
用于电子邮件地址的引用规则的复杂性使得对地址的本地部分或域字面量进行完整测试变得不切实际。鉴于不一定存在与有效本地部分相对应的真实邮箱,那么在复杂的验证脚本上花费多少额外的下载时间呢?
[email protected]
[email protected]
[email protected]
[email protected]
me.example@com
"spaces must be quoted"@example.com
!#$%&'*+-/=.?^_`{|}~@[1.0.0.127]
!#$%&'*+-/=.?^_`{|}~@[IPv6:0123:4567:89AB:CDEF:0123:4567:89AB:CDEF]
me(this is a comment)@example.com
– 注释不受鼓励,但 RFC2822 未禁止。
me@
@example.com
[email protected]
[email protected]
[email protected]
me\@example.com
spaces\ must\ be\ within\ quotes\ even\ when\ [email protected]
a\@[email protected]
注释:此代码的设计错误,会导致拒绝某些实际上有效的电子邮件。如果对有效和无效电子邮件的更改被接受,则应同时审查以下代码。
可以使用以下测试页面测试电子邮件验证函数。将三个文件保存在同一个目录中,然后在 Web 浏览器中打开 validEmail.htm。
validEmail.js
function isValidEmail(string) {
// These comments use the following terms from RFC2822:
// local-part, domain, domain-literal and dot-atom.
// Does the address contain a local-part followed an @ followed by a domain?
// Note the use of lastIndexOf to find the last @ in the address
// since a valid email address may have a quoted @ in the local-part.
// Does the domain name have at least two parts, i.e. at least one dot,
// after the @? If not, is it a domain-literal?
// This will accept some invalid email addresses
// BUT it doesn't reject valid ones.
var atSym = string.lastIndexOf("@");
if (atSym < 1) { return false; } // no local-part
if (atSym == string.length - 1) { return false; } // no domain
if (atSym > 64) { return false; } // there may only be 64 octets in the local-part
if (string.length - atSym > 255) { return false; } // there may only be 255 octets in the domain
// Is the domain plausible?
var lastDot = string.lastIndexOf(".");
// Check if it is a dot-atom such as example.com
if (lastDot > atSym + 1 && lastDot < string.length - 1) { return true; }
// Check if could be a domain-literal.
if (string.charAt(atSym + 1) == '[' && string.charAt(string.length - 1) == ']') { return true; }
return false;
}
function testIsValidEmail(string) {
alert("'" + string + "' is " + (isValidEmail(string) ? "" : "NOT ") + " a valid email address.");
}
function checkSamples() {
var validAddress = [
'[email protected]',
'[email protected]',
'[email protected]',
'name\\@[email protected]',
'spaces\\ are\\ [email protected]',
'"spaces may be quoted"@example.com',
'!#$%&\'*+-/=.?^_`{|}~@[1.0.0.127]',
'!#$%&\'*+-/=.?^_`{|}~@[IPv6:0123:4567:89AB:CDEF:0123:4567:89AB:CDEF]',
'me(this is a comment)@example.com'
];
var invalidAddress = [
'me@',
'@example.com',
'[email protected]',
'[email protected]',
'[email protected]',
'me.example@com',
'me\\@example.com'
];
var results = new StringBuffer();
var handlesValidAddressesCorrectly = true;
results.append('<table border="1">');
results.append('<caption>Does the function accept all the valid sample addresses?</caption>');
results.append('<tr><th>Valid address</th><th>Function returns</th></tr>');
for (var i = 0; i < validAddress.length; i++) {
results.append('<tr><td>');
results.append(validAddress[i]);
results.append('</td><td>');
if (isValidEmail(validAddress[i])) {
results.append('<span class="good">true</span>');
} else {
handlesValidAddressesCorrectly = false;
results.append('<strong class="fail">false</strong>');
}
results.append('</td></tr>');
}
results.append('</table>');
var handlesInvalidAddressesCorrectly = true;
results.append('<table border="1">');
results.append('<caption>Does the function reject all the invalid sample addresses?</caption>');
results.append('<tr><th>Valid address</th><th>Function returns</th></tr>');
for (var i = 0; i < invalidAddress.length; i++) {
results.append('<tr><td>');
results.append(invalidAddress[i]);
results.append('</td><td>');
if (!isValidEmail(invalidAddress[i])) {
results.append('<span class="good">false</span>');
} else {
handlesInvalidAddressesCorrectly = false;
results.append('<em class="warning">true</em>');
}
results.append('</td></tr>');
}
results.append('</table>');
var conclusion;
if (handlesValidAddressesCorrectly) {
if (handlesInvalidAddressesCorrectly) {
conclusion = '<p><strong class="good">The function works correctly with all the sample addresses.</strong></p>';
} else {
conclusion = '<p><em class="warning">The function incorrectly accepts some invalid addresses.</em></p>';
}
} else {
conclusion = '<p><strong class="fail">The function incorrectly rejects some valid addresses.</strong></p>';
}
document.getElementById('testResults').innerHTML = conclusion + results.toString();
}
function StringBuffer() {
this.buffer = "";
}
StringBuffer.prototype.append = function(string) {
this.buffer += string;
return this;
};
StringBuffer.prototype.toString = function() {
return this.buffer;
};
validEmail.css
body {
background-color: #fff;
color: #000
}
table {
margin-bottom: 2em
}
caption {
margin-top: 2em
}
.good {
background-color: #0f0;
color: #000
}
.warning {
background-color: #fc9;
color: #000
}
.fail {
background-color: #f00;
color: #fff
}
validEmail.htm
<!DOCTYPE html>
<html lang="en">
<head>
<title>Valid email test</title>
<link rel="stylesheet" href="validEmail.css">
<script src="validEmail.js"></script>
</head>
<body onload="checkSamples()">
<h1>Unit test for email address validation functions</h1>
<h2>Interactive test</h2>
<form action="#">
<fieldset>
<legend>Email address</legend>
<label for="emailAddress">Email</label>
<input type="text" size="40" value="" name="email" id="emailAddress">
<input type="button" name="validate" value="Check address"
onclick="testIsValidEmail(this.form.email.value)">
</fieldset>
</form>
<h2>Selected samples</h2>
<p>This section shows the results of testing the function with sample strings.
The sample includes both valid strings and invalid strings
according to <a href="http://www.faqs.org/rfcs/rfc2822.html">RFC2822</a>.
</p>
<div id="testResults">You need to enable JavaScript for this unit test to work.</div>
</body>
</html>
许多 JavaScript 程序员建议通过在任何其他语句之前放置确切的语句 "use strict";
来启用 ECMAScript 5 的严格模式:[3][4][5]
"use strict";
function …
JavaScript 最佳实践
- 编码手册/验证电子邮件地址
- Christian Heilmann 撰写的“我们不再需要的六个 JavaScript 功能”.
- Apple 推荐的 JavaScript 验证函数的源代码:checkEmail() 等。
- JavaScript 表单验证 - 做正确的事
- "FORM 提交和 ENTER 键?" 讨论了在您按 Enter 时提交的表单;在您按 Enter 时未提交的表单;以及如何使表单以相反的方式工作。
- Matt Kruse 撰写的“JavaScript 最佳实践”
- "比较电子邮件地址验证正则表达式" 列出了有效和无效的电子邮件地址以及各种正则表达式及其在该列表中的有效性。
- ↑ Jan Wolter. "JavaScript Madness: Query String Parsing" 2011.
- ↑ PHP bug #39078: Plus sign in URL arg received as space.
- ↑ Nicholas C. Zakas. "It’s time to start using JavaScript strict mode". 2012.
- ↑ "What does “use strict” do in JavaScript, and what is the reasoning behind it?"
- ↑ Mozilla Developer Network: "Strict mode".