它的出生,并不高贵。
1995年,Web尚处于婴儿期,绝大数计算机用户还不能告诉你网站到底为何物,而且在当时开发一个网站也是很困难的一件事。微软当时也是刚刚意识到互联网的重要性,Google在当时也只是在Little Rascals中杜撰的一词。
1996年早期,JavaScript创建不久,被提交到ECMA进行标准化。它涵盖了JavaScript的核心语法和DOM 0级的一个子集,并且大多数浏览器是以某种形式实现这个规范。我们很少听到有人说ECMAScript,主要是因为它的名字太长了,不方便记忆,但是它和JavaScript是一回事。
其实JavaScript成功的原因很简单,你不需要担心数据类型,只要用var
就可以声明一个变量了,往往优势的背后总是隐藏着劣势,在过去,没有人把它当作面向对象的,你不用考虑层级之间的关系,可以很快的上手,门槛很低,这就导致了问题的出现。
不幸的是,JavaScript的童年是没有掌声和鲜花的,活的很委屈。大量的高危安全严重损害了它早期的名望。更重要的是大多程序员对它的态度都是消极,认为JavaScript缺乏开发工具,离开了浏览器就不能开发了,在他们眼中就是一个"脚本玩具",JavaScript在编程世界里面就想一只“丑小鸭”。
“对,人们对JavaScript的批评一点也没有错,它开始认识到自己的缺点,并且它决心要不断的变好。”
故事结束,其实JavaScript的遭遇真的比我们有些人还要惨。
一段老式的浏览器嗅探程例
function Redirect(){
var WhatBrowser;
var WhatVersion;
//浏览器的版本
WhatVersion=navigator.appVersion.toUpperCase();
//浏览器的类型
WhatBrowser=navigator.appName.toUpperCase();
if(WhatBrowser.indexOf("MICROSOFT")>=0){
if(WhatVersion.indexOf("3")>=0){
console.log("MICROSOFT3");
}else{
console.log("MICROSOFT");
}
}
if(WhatBrowser.indexOf("NETSCAPE")>=0){
if (WhatVersion.indexOf("2")) {
console.log("NETSCAPE2")
}else{
console.log("NETSCAPE");
}
}
}
这种嗅探浏览器版本的代码流行了很长一段时间。也就意味着,对于不同对的浏览器,你需要采取不同的策略,我们需要做两个版本,一个是为IE设计的,另一个是为NETSCAPE设计的,这显然是兼容的好方法。但在很长的时间它确实是唯一的方法。
JavaScript可以很慢,对,没错,直至今日,你也可以很容易写出性能很差的代码。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>性能很差的JavaScript举例</title>
<script type="text/javascript">
function badTest(){
var startTime = new Date().valueOf();
var s ="";
for(var i=0;i<10000000;i++){
s+="This is a test string";
}
return new Date().valueOf() - startTime;
}
function goodTest(){
var startTime = new Date().valueOf();
var stringBuffer = new Array();
for(var i=0;i<10000000;i++){
stringBuffer.push("This is a test string");
}
var s = stringBuffer.join("");
return new Date().valueOf() - startTime;
}
function betterTest(){
var startTime = new Date().valueOf();
var stringBuffer = new Array();
for(var i=0;i<10000000;i++){
stringBuffer[stringBuffer.length]="This is a test string";
}
var s = stringBuffer.join("");
return new Date().valueOf() - startTime;
}
function doTests(){
var htm="";
htm+="Time badTest took:"+badTest()+"<br>";
htm+="Time goodTest took:"+goodTest()+"<br>";
htm+="Time betterTest took:"+betterTest()+"<br>";
document.getElementById("result").innerHTML=htm;
}
</script>
</head>
<body>
<a href="javascript:void(0);" onClick="doTests();">Click here to test</a>
<div id="result"></div>
<body>
</html>
在IE中的结果是:
从数字上大家应该有个很直观的感受,也就是使用不同的方法去实现同样的一种功能,所花的时间上面是有明显的差别的。这里我使用的浏览器已经是比较新的版本了,想想以前不同代码之间所花时间的差距,自己脑补。
万恶的根源:开发者!
与语言本身需要进化一样,开发者同样需要不断进化。他们需要知道什么好用什么不好用,并且开发人员需要自己努力去寻找比较好的方法。如果你正在使用Java,那么很有确切地知道使用字符串拼接是件坏事。而字符串缓冲区是你的好朋友。但是在JavaScript中,没有字符缓冲区。
不够高效的编码示例
function Person1(firstName,lastName){
this.firstName = firstName;
this.lastName=lastName;
this.toString=function(){
return this.firstName+" "+this.lastName;
}
}
function Person2(attrs){
this.firstName=attrs["firstName"];
this.lastName=attrs["lastName"];
this.toString=function(){
return this.firstName+" "+this.lastName;
}
}
function showPerson(){
var p1 = new Person1("Frank","Zammetti");
var p2 = new Person2({"firstName":"Frank","lastName":"Zammetti"});
document.getElementById("divPerson").innerHTML =p1+"<br>" +p2;
}
这里我们有俩个不同的函数来表示人:person1和person2,Person1的构造函数接受两个参数,firstName和lastName。Person2接受一个单独的数组参数attrs,是属性的数组。showPerson函数创建了两个同样的人。一个使用Person1,另一个使用Person2,当我们想添加别的属性来描述一个人会发生什么呢?对于Person1 ,我们为构造函数增加更多的参数。对于Person2,只需要添加适当的字段设置代码,这里的差别就在于程序的可扩展性问题。其实person1不仅可扩展性差,可读性也差;通过调用var p1 = new Person1("Frank","Zammetti");你知道frank,Zammetti这个字符串什么含义吗?而person2在调用构造函数你便可以知道frank是这个人的名,zammetti是这个人的型。
JavaScript缺乏块级作用域的示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript没有块级作用域</title>
<script type="text/javascript">
function test(){
var i=1;
if(1){
var i=2;
if(1){
var i=3;
//网页中弹窗
alert(i);
}
alert(i);
}alert(i);
}
</script>
</head>
<body onLoad="test();">
//正常如果有块级作用域的编程语言,结果应该321,而对于JavaScript,结果是333
</body>
</html>
上面所有的一切,确实只是没有经验的开发人员会干的事情,因为他们还不知道如何做得更好,这并不是JavaScript的错。这个锅,它不背。
不断的进化:接近可用性
在第一批相对没有经验的开发人员使用JavaScript之后,新一轮开始隐现,人们认识到一些普遍存在的错误并开始修正。
最重要的是,有经验的开发人员已经看到JavaScript的能力并开始用自己聪明才智来改造它,正是因为这样的人,才有了今天的JavaScript。
编写代码的好习惯,增加代码的可读性
- 写代码需要有缩进
- 参数名称要具有描述性
- 引号要使用一致,不要一会双引号,一会单引号
- 结尾最好加上分号
- 统一使用花括号,可能代码只有一行
- 函数的名字需要赋予实际意义
- 在函数前后需要加上注释
- 注意转义字符和拼写
另外一个重要的概念是外部的JavaScript文件,也就是说,不要把结构,样式,逻辑分开,有自己单独的文件,不要全部嵌套在一起,分开便于后期的修改和扩展,也便于代码的重复使用。
专业的JavaScript
在过去的几年里,主流的浏览器已经达到共识,相互之间基本上都能兼容JavaScript,所以现在很少写分支代码。你现在可能会发现的问题其实与JavaScript无关,那都是Dom惹的祸。
假设我们想在文档上挂一个KeyDown的钩子。我们可以这做
document.onkeydown=KeyDown;
但在Firefox中,你必须这么做:document.captureEvent(Event.KEYDOWN);
两个浏览器基本的keydown()函数的原型都是:function keyDown(e){};
看一个更为复杂的例子:
document.onkeydown=KeyDown;
if(document.layers){
document.captureEvent(Event.KEYDOWN);
}
function keyDown(e){
var ev = (e)?e:(window.event)?window.event:null;
if(ev){
return (ev.charCode)?ev.charCode:
((ev.keyCode)?ev.keyCode:((ev.which)?ev.which:null));
}
return -1;
}
这段代码兼容任何浏览器。第二行的第一个if只在非IE浏览器中为true,那些浏览器的document对象会有layers属性。这个时候,调用captureEvent();然后再keyDown()的内部,第一行会把ev设置为传入的参数e。如果document.layers属性不存在,则使用window.event。但是如果尤铭设置window.event,那么则ev设置为null。然后如果设置了ev,则找出键值:分别看看里面对象是charCode还是keyCode,如果ev是null,则返回-1;
面向对象的JavaScript
大多数程序员的生活从发现原型(prototype)那天开始就改变了。一旦他们发现所有JavaScript对象都可以通过原型来扩展,并且这个功能允许他们创建自定义的类,那么一切都变了。
var answer = 0;
function addNum(num1,num2){
answer =num2+ num1;
}
function subNum(num1,num2){
answer =num1 - num2;
}
function multiNum(num1,num2){
answer=num1 *num2;
}
function divideNum(num1,num2){
if(num2){
answer=num1/num2;
}else{
answer=0;
}
}
这段代码,从语法和功能上来说,没有任何问题,无非就是进行一些简单的算术运算。但是它组织的够好吗?我看不怎么样。在全局作用域里answer变量代码味太浓,每个函数都是如此,都是孤立的函数,都是全局作用域里面。我们要避免全局污染。在任何编程中,使用全局变量会被认为是一个坏习惯,因为不在全局变量范围内,我们就可以随意的去修改它。缺乏结构意味着函数与函数之间没有结构关系,并且没有逻辑的分组帮助人们从更高级别去理解它。
快来使用面向对象的思想吧!
面向对象,更加专业
function NumberFunctions(){
var answer = 0;
}
NumberFunctions.prototype.addNumbers = function (num1,num2){
this.answer =num2+ num1;
}
NumberFunctions.prototype.subtractNumbers = function (num1,num2){
this.answer =num1 - num2;
}
NumberFunctions.prototype.multiplyNumbers = function (num1,num2){
this.answer=num1 *num2;
}
NumberFunctions.prototype.divideNumbers = function (num1,num2){
if(num2){
this.answer=num1/num2;
}else{
this.answer=0;
}
}
NumberFunctions.prototype.toString =function(){
return this.answer;
}
测试代码
// 使用构造函数与创建对象
var nf = new NumberFunctions();
//调用这个对象的方法
nf.addNumbers(2,1);
//通过控制来查看结果
console.log(nf):
//同理
nf.multiplyNumbers(4,5);
console.log(nf);
结果:
这样写的优点
- 没有对全局变量的污染
- 所有函数实际上都是NumberFunctions类的成员,因此构造了一个清晰的关系
- 基本的面向对象:数据和操作的函数都封装的很好
这里再提一个概念“柔性衰减”(graceful degradation):它是针对某个版本的浏览器设计的网页应该对旧的浏览器采取柔性衰减的策略:即使是不够好的,也至少能用。同样的概念适用在JavaScript中。
--------------------End--------------------
我就是番茄,爱生活,爱学习,爱宠物的家伙!
如果觉得写的还行,记得打赏哦!