文章目录

  • JavaScript版本变迁史
  • JavaScript之前的网站
  • JavaScript的开端
  • JavaScript的各种版本
  • ECMAScript
  • ECMAScript 1-2
  • ES3
  • ES4
  • ES5的更新细节
  • ES6的更新细节
  • 进一步了解JavaScript


JavaScript版本变迁史

  JavaScript是世界上最流行和广泛传播的编程语言之一 (其他几个大概是C, C++, Java, Python) 。这门语言诞生于1995年,之后几经变迁最终以JavaScript的名称而流传于世。

  JavaScript由Brendan Eich创造,在1997年成为一个ECMA标准。ECMAScript是这门语言的官方名称,ECMAScript版本包括ES1, ES2, ES3, ES5, ES6。

ECMA是一个国际标准化组织(Ecma International is an industry association dedicated to the standardization of information and communication systems)。JavaScript符合ECMA制定的ECMAScript标准(或者说ECMAScript就是JavaScript的官方称呼),具体来说符合ECMA-262和ECMA-402两个标准。这两个标准都经过了多次修订,具有多个版本。关于ECMAScript的标准变迁可以查看developer.mozilla.org的说明。

  在2020的末尾我们回顾JavaScript的变迁史,以便更好理解如何使用这门编程语言。我们将介绍

  • JavaScript之前的网站
  • JavaScript早期
  • JavaScript标准化
  • JavaScript的版本
  • ECMAScript5的详细介绍
  • ES6的具体变化
  • 进一步了解JavaScript

JavaScript之前的网站

  在JavaS诞生之前,Web页面是相较今天是非常静态的。列表、日期和链接都在HTML中硬编码,所有种类的动态功能都需要在HTML的head中定义。一个非常著名的仍然运行的非JavaScript的网站是San Francisco FogCam:

javascript查看版本 javascript版本号_ES6


  这个网站于1994年创建,以”世界上最古老的网络摄像头“而闻名。这是人类能从网络上看到动态图像的开始。随着时间的流逝,这个网站并没有变。它仍然使用90年代的静态HTML和CSS。网站每20秒刷新一次(通过HTML文档头部的<meta>来实现)。在FogCam创建之时,JavaScript还未正式发行,但是我们已经能看到对于动态加载图片的需要。

  另一个在运行的未使用JavaScript的代表网站是创建于90年代后期参议员Bob Dole’s的总统竞选结果。

javascript查看版本 javascript版本号_js_02


  该网站是一个静态网站,它使用HTML路由 (HTML routing) 进行页面之间的跳转。网站的一切都是硬编码的,Netscape Communicator注意到了这个问题并决定创建一种允许动画、表单创建以及其他动态交互的脚本语言。

这便是JavaScript的开始。

JavaScript的开端

  实际上JavaScript一开始叫做Mocha。创建这门语言的初衷在于作为设计师和非程序员使用的高层语言。(译注:这里表述不准确,大概意思是作为易学易用的语言)当Mocha随着Netscape Navigator 2.0被提供时,其名称改为LiveScript,并在之后的版本改为JavaScript(为了蹭Java的热度)。

javascript查看版本 javascript版本号_JSON_03

随Netscape Navigator 2.0发布而首次公开发布的JavaScript


  Netscape与Sun Microsystems(美国一家出售计算机等产品的公司)在创建JavaScript的合作上引领了潮流。随着Netscape在浏览器领域取得了越来越多的成功,其他浏览器也需要提出一些新东西来跟上Netscape。
  由于法律原因,微软创造了他们自己的JavaScript版本并命名为JScript。这些新的语言提升了用户体验和用户与网站之间的交互。随着JScript的创建,微软的Internet explorer浏览器的市场份额逐渐增加。但对于JavaScript来说,这种语言分流使得标准化变得困难。即便如此,由于JavaScript在语法上和Java的相似性,JavaScript在”脚本语言的战争“中成为了赢家,随着Java越来越流行,JavaScript也成长了起来。

Java和JavaScript风马牛不相及。Java是使用虚拟机或者浏览器来执行代码的编译语言;而JavaScript则是面向浏览器的语言(在浏览器之外可以用Node.js来执行js代码)

JavaScript的各种版本

Version

Official Name

Description

ES1

ECMAScript 1(1997)

First edition

ES2

ECMAScript 2 (1998)

Editorial changes

ES3

ECMAScript 3 (1999)

Added regular expressions & try/catch

ES4

ECMAScript 4

Not released

ES5

ECMAScript 5 (2009)

Added "strict mode", JSON support, String.trim(), Array.isArray(), & Array iteration methods

ES6

ECMAScript 2015

Added let and const, default parameter values, Array.find(), & Array.findIndex()

ES6

ECMAScript 2016

Added exponential operator & Array.prototype.includes

ES6

ECMAScript 2017

Added string padding, Object.entries, Object.values, async functions, & shared memory

ES6

ECMAScript 2018

Added rest / spread properties, asynchronous iteration, Promise.finally(), & RegExp

ECMAScript

  Netscape Communicator在1997年向ECMA标准化组织提交了文档。在之后,ECMA结合Netscape的JavaScript和微软的JScript创建了一个标准,将之命名为ECMAScript(这是JavaScript和JScript共同的语言规范)。之所以命名为ECMAScript而非JavaScript的原因是JavaScript被当时的Sun Microsystems公司用作商标(之后的Oracle公司)。

ECMAScript 1-2

  ES1于1997年发行,在后一年ES2发行,两者之间差别不大。

ES3

  ES3于1999年发行,为JavaScript增加了许多新特性,这些特性在今天变成了JavaScript的标准组成部分。

  1. 严格相等: 自从ES3开始,严格相等操作符(===)作为等于操作符(==)的补充被添加到语言中。两者的区别是等于操作符不涉及类型只考虑值,严格相等操作符则与其他强类型语言的等于操作符类似(JavaScript是弱类型语言)
const compareEypeAndValue = (num, str) => {
  return num === str;
}
console.log(compareTypeAndValue(8, '8')); //false
console.log(compareTypeAndValue(8, 8)); //true
  1. 正则表达式: ES3加入了文字和构造器(literal and constructor)两种正则表达式。
  2. Literal Expressions:文字表达式在两个反斜杠中定义。(详细百度)
/[^abc]/gim
  1. Constructor Expressions:构造器表达式是RegExp类的实例。
const regex = new RegExp('[^abc]', 'gim');
  1. Switch Statement: JavaScript的switch-case-default组合与C语言几乎完全一致。
  2. Try/Catch Handling:try/catch允许你跳出“快速失败”的哲学,在程序运行异常后可以抛出异常且继续运行。
const isValidKey = (val1) => {
try {
  let obj;
  return obj.hasOwnProperty(val1);
} catch (err) {
  throw new Error("not valid key");
}
}
console.log(isValidKey(""))

ES3在发布后的十年内都是标准版本,直到2009年ES5发行

ES4

  TC39是ECMA International的免版税任务组,其主要工作是标准化ECMAScript。当需要更新和发布ES4标准时,任务组无法就规范达成共识。结果,ES4作为一个备受推崇的版本,却从未完全发布为实际标准。

ES5的更新细节

  ES5和ES6是现在为止最后发布的两个版本,这两个版本做出了很多重大的更新。在ES3发行十年后的2009年,ES5发行了,这个新版本是JavaScript创立以来的最大变化。一些新特性包括

  • “use strict”:在ES5之前,未声明变量(未使用var声明的变量)在任何情况下都可用。但自从加入"use strict"特性后,启动该特性会禁止使用未声明变量:
"use strict"
x = 5; //ReferenceError: x is not defined
  • 新的Array方法:ES5加入了一些新的array(内置类)的方法,这些方法包括
  • every():every方法检查数组中的每个变量是否满足条件
var arr = [6, 4, 5, 6, 7, 7];
arr.every(function(element) {
  return element % 2 === 0; //checks to see if even
}); // false
  • filter():显然这个方法返回满足条件的子数组
var arr = [6, 4, 5, 6, 7, 7];['
arr.filter(function(element) {
  return element/2 > 3;
})
  • forEach():这个方法与for循环很像。对于数组中的每个元素,forEach方法执行一个回调函数。
var arr = [6, 4, 5, 6, 7, 7];
arr.forEach(function(element) {
  console.log(element * 2);
})
  • indexOf() and lastIndexOf():分别返回所查询元素首个和末个的索引
var arr = [6, 4, 5, 6, 7, 7];
console.log(arr.indexOf(4)); // 1
console.log(arr.indexOf(2)); // -1
console.log(arr.indexOf(7)); // 4
console.log(arr.lastIndexOf(7)); // 5
  • isArray():查看对象是否为array
var arr = [6, 4, 5, 6, 7, 7];
var str = "Hello Educative.io";
console.log(Array.isArray(arr));
console.log(Array.isArray(str));
  • map():map方法和forEach()非常相似,区别是map返回一个新array,因此回调函数需要有返回值
var arr = 6, 4, 5, 6, 7, 7];
arr.map(function(element) {
  return element * 2;
})
  • reduce() and recuceRight():与python的reduce类似,将某个操作连续应用到序列的元素上,累计之前的结果,把一系列值规约成一个值。区别是js的reduce可以指定一个参数作为额外的操作数。
var arr = [6, 4, 5, 6, 7, 7];
var reduced = arr.reduce(funciton(curr, next) {
  return curr + next;
}, 0); # 0为额外的操作数
var reducedRight = arr.reducedRight(function(curr, next) {
  return curr + next;
}, 0)
console.log(reduced);
console.log(redecedRight);
  • some():与python的any相似,与arr.every()相反,every考虑是否所有元素满足条件而some考虑是否存在满足的元素。
  • JSON
      解析和字符串化JavaScript Object Notation(JSON)的功能在ES5标准中成为可能。JSON格式用于基本通过网络连接(通常是Web应用程序和API)传输某种结构化数据。当我们从一个应用程序传输数据时,它必须为字符串形式。使用JSON.stringify()将JavaScript对象转换为字符串,然后另一侧使用JSON.parse()将数据传输回JavaScript对象后进行转换。
var arr = [6, 4, 5, 6, 7, 7];
var obj = {
 author: "Christina Kopecky",
 title: "How to parse JSON objects",
 published: false
}
console.log("======== ARR EXAMPLE ==========");
console.log("orig arr=====>", arr);
console.log("stringified arr=====>", JSON.stringify(arr));
console.log("proof of type=====>", typeof JSON.stringify(arr));
console.log("parsed string=====>", JSON.parse(JSON.stringify(arr)));
console.log("proof of type=====>", typeof JSON.parse(JSON.stringify(arr)), "\n\n");    
console.log("======== OBJ EXAMPLE ==========");
console.log("orig obj=====>", obj);
console.log("stringified obj=====>", JSON.stringify(obj));
console.log("proof of type=====>", typeof JSON.stringify(obj));
console.log("parsed string=====>", JSON.parse(JSON.stringify(obj)));
console.log("proof of type=====>", typeof JSON.parse(JSON.stringify(obj)), "\n\n");
  • 新的Date方法
      ES5为Date类新加入两种方法Date.now()Date.valueOf(),它们都返回自1970年1月1日以来的当前时间(以毫秒为单位)。两个方法都返回自1970一月一日的毫秒值,区别是Date.valueOf()返回值是Date类的实例而Date.now()只是调用函数返回一个数值。(Date.valueOf()返回值和Date.now()并不同,也许作者意为刚加入时两者类似?)
  • getters and setters
    在ES5的更新中,存取器属性被加入语言(accessor properties)。get和set的唯一角色就是得到、设置值。在设置了get和set后,他们就像标准的类属性。
let character = {
  first_name: "Darth",
  last_name: "Vader",
  get fullName() {
    return `${this.first_name} ${this.last_name}`;
  },
  set fullName(str) {
    [this.first_name, this.last_name] = str.split(" ");
  }
};
console.log(character.fullName); //Darth Vader
character.fullName = "Luke Skywakker"
console.log(character.first_name);
console.log(character.last_name);

  ES5标准使JavaScript代码更具可读性。通过引入新的数组方法,解析和字符串化JSON的能力以及使代码创建更加严格,它使JavaScript更易于理解。

ES6的更新细节

ES5版本发布七年后,于2015年6月ES6成为新的标准。

  • Babel
    最大变化之一是ES6 JavaScript无法直接在浏览器中进行编译。我们需要使用一个名为Babel.js的编译器来生成兼容的JavaScript,这样旧的浏览器才可读取这些JavaScript。

Babel允许您在项目中使用ES6功能和语法,然后将其转换为ES5,以便可以在生产中使用它。要在构建项目时使用Babel,您需要将package.json添加到您的项目中。这是项目的所有依赖项所在的位置。

  • 如果你安装了Node和npm(yarn可替代npm),那么在项目目录下打开shell键入npm init(或者yarn init),在回答问题后package.json将会自动生成。
    使用npm/yarn将babel加入项目依赖:
npm install --save-dev babel-cli
//or
yarn add babel-cli --dev

将babel加入项目依赖后,还需要配置babel,首先安装babel-preset-env到项目:

npm install --save-dev babel-preset-env
//or
yarn add babel-preset-env --dev

然后创建.babelrc文件,写入以下内容

{
    "presets": ["env"]
}

现在,您可以通过运行构建命令来运行Babel。现在,目标文件夹应该看起来与原始文件夹完全一样,只是目标文件夹的内容是ES5代码而不是ES6。如果您碰巧使用了JavaScript库或create-react-app之类的框架,那么Babel很有可能已经为您配置了,您无需担心。

  • 使用=>创建函数
      在该功能加入JavaScript前,我们只能使用function关键字来创建函数。现在我们可以使用=>来创建函数,使用=>创建行间函数可能更加优雅(译注:为何不用lambda呢)
function add(num1, num2) {
  return num1 + num2;
}
// ES6 (implicit return)
const addImplicit = (num1, num2) => num1 + num2;
console.log(add(3, 4));
console.log(addImplicit(3, 4));

=>具有隐式return。如果函数只有一行,不需要return关键字,这也意味着不需要花括号。如果函数多于一行,则需要花括号和return语句:

//ES6 (explicit return)
const addExplicitReturn = (num1, num2) => {
  let sum = num1 + num2;
  return sum;
};
console.log(addExplicitReturn(3, 4));

还需要注意的是,当使用类时,箭头函数已绑定到“ this”关键字,因此无需实际使用bind()方法将函数绑定至类。

如果使用function关键字,则需要使用bind()方法将该方法绑定到该类。

  • Classes
      在JavaScript的原型之上,类充当语法糖。它们代替原型继承,而是将古典继承与extends关键字一起使用。总体而言,它减少了一些代码量。
class StarWarsCharacter{
 constructor(attributes) {
   this.name = attributes.name;
   this.age = attributes.age;
   this.homePlanet = attributes.homePlanet;
 }

 getCharacter = () => `${this.name} is ${this.age} years old and is from ${this.homePlanet}.`;
}

const luke = new StarWarsCharacter({ name: "Luke Skywalker", age: 23, homePlanet: "Tatooine"});

luke.getCharacter();

class Heroes extends StarWarsCharacter {
 constructor(attributes) {
   super(attributes);
   this.favoriteVehicle = attributes.favoriteVehicle;
 }
  getFavoriteVehicle = () => `${this.name} is ${this.age} and their favorite vehicle is the ${this.favoriteVehicle}`;  
}

const hans = new Heroes({ name: "Hans Solo", age: 35, favoriteVehicle: "Millennium Falcon"});

console.log(hans.getFavoriteVehicle());
  • Destructuring(析构)
      它允许我们unpack对象并将该解压缩后的值用作我们稍后在代码中引用的变量。
const state = {
  name: "Luke Skywalker",
  age: 22,
  dark_side: false
}
console.log("before destructuring");
console.log(state.name);
console.log(state.age);
console.log(state.dark_side);
const { name, age, dark_side } = state;
console.log("after destructuring");
console.log(name);
console.log(age);
console.log(dark_side);

   我们可以通过拉出属性来对其进行结构分解,将其放在花括号中,然后将其设置为对象名称。**确保在花括号前使用const关键字,**它允许我们将这些属性作为变量访问,而不是在实际对象本身上使用点符号。

Array的析构与上面的代码类似,不同的是使用花括号。

const arr_state = [ "Luke Skywalker", 22, false];
 console.log("before destructuring");
 console.log(arr_state[0]);
 console.log(arr_state[1]);
 console.log(arr_state[2]);
 const [ name, age, dark_side ] = arr_state;
 console.log("after destructuring");
 console.log(name);
 console.log(age);
 console.log(dark_side);
  • let and const   ES6加入了两个新的声明变量的关键字,他们一定程度上取代了var关键字。在ES6之前,JavaScript仅仅有函数作用域和全局作用域。加入let和const后,我们有了块作用域(有点类似python的闭包,但在if-else嵌套作用域是python没有的)
let x = 5;
function blockExample() {
  let x = 2;
  if (x >= 3) {
    let x = 10;
    console.log(x, "inside if block");
  } else {
    let x = 1;
    console.log(x, "inside else block")
  }
  console.log(x, "inside function");
}
blockExample();
console.log(x, "global example");

**let关键字暗含作用域,在同一作用域下重新声明相同变量将引发语法错误(使用var看起来不报错)。

var x = 3;
var x = 120; // no errors

使用let报错

let x = 5;
let x = 120; // syntax error

const在声明不变量时很有用。尝试将const变量赋值将引发错误。

  • Promises
      Promises处理异步JavaScript编程更加优雅,在ES6以前,异步调用是通过使用回调函数进行的,这可能会使代码非常复杂并令人费解。

注意:Promises将异步逻辑包装在net程序包中以使代码更具可读性。

console.log("before promise")
let promise = new Promise((resolve, reject) => {
  let resolvedFlag = false;
  //this is just a flag so we can intentionally throw the response to test logic
  console.log("this is eventually going to be an API call");
  resolvedFlag = true;//flip resolved to true once all console logs are done
  if (resolvedflag) {//if resolved is true invoke the resolve function
    resolve("Promise resolved THIS IS THE RESPONSE");
  } else {// else invoke the reject function with a new Error object with message
    reject(new Error("Promise failed"));
    console.log("after promise");
  }
});
promise.then(resp => {
  console.log(resp);//promise response
});
  • rest和spread操作符(译注:即…操作符,用作unpack)
      rest运算符和spread运算符语法相同,但用途不同。在函数参数之前使用rest运算符来指示应将多个参数分配给该参数。(译注:这里类似python的*符号)
function restExample(a, ...b) {
  console.log(a);
  ocnsole.log(b);
}
restExample(1, 2, 3, 4, 5, 6);

spread运算符由array使用(译注:与python的*符号类似,拆包),用法见下面的例子:

function spreadExample(arr) {
  let newArr = [2, 4, 6, 8];
  console.log("arr", arr);
  let combinedArr = [...newArr, ...arr]
  let arrWithOtherContents = ["a", ...newArr, {b: "c", d: "e"}, true, ...arr];
  console.log(arrWithOtherContents);
  console.log("combined", combinedArr);
}
console.log(spreadExample([1, 3, 5, 7, 9]))

上面代码的运行结果:

javascript查看版本 javascript版本号_ES6_04

  • 模板文字Template Literals
      在ES6中,我们不再需要使用+操作符连接文字、空格和变量来构成一个大的字符串,使用template literals来构造表达式将变量嵌入字符串是更优雅的做法(译注:与python的format格式化显示异曲同工)
let name = "Jane";
let holiday = "Christmas";
//pre-ES6:
console.log(name + "'s favorite holiday is " + holiday);
//ES6+:
console.log(`${name}'s favorite holiday is ${holiday}`);

进一步了解JavaScript

  自ES6发布以来,该标准化每年都在进行更新。您可以随时关注ECMA International的ECMA-262标准,以了解JavaScript的新功能。这些标准具有可免费在线阅读的PDF格式的标准。
  学习JavaScript,你还需要了解下面这些概念:

  • Map
  • Set
  • Generators
  • async/await