让你的JavaScript成为真正的Java Script

JavaScript为什么叫“JavaScript”?

要回答这个问题还得从JavaScript的历史说起。

1995年5月,Netscape创建了称为Mocha(摩卡)的浏览器端的脚本语言。

可是没过多久,同年9月就改名为“LiveScript”。

又过了没多久,同年12月,Netscape与sun公司合作,将其改名成“JavaScript”了,意为像Java一样的Script(脚本语言)。

为什么最终确定了“JavaScript”这个名字?
“JavaScript”和“Java”到底有多少相似度呢?
我们通过几段代码来简单地比较一下~
既然是“像Java一样”,那么就以Java为准进行对比。而Java作为一门纯正的面向对象的语言(一切皆为对象类),我们就从Java面向对象的3个特性出发进行对比。

封装

封装作为首要特性,指的是能将对象的属性或函数隐藏或暴露出来。

我们创建一个Animal对象,让其有个私有属性name,同时只能通过对象上的set/get方法修改/获取name值。
具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// Java
class Animal {
private String name;
public void set(String name) {
this.name = name;
}
public String get() {
return this.name;
}
}
public class Test {
public static void main(String[] args) {
Animal animal = new Animal();
animal.set("dog");
System.out.println(animal.get()); // "dog"
}
}
// JavaScript(ECMAScript5)
function Animal() {
var name = ''
this.set = function(a) {
name = a
}
this.get = function() {
return name
}
}
var animal = new Animal()
animal.set('dog')
console.log(animal.get()) // 'dog'

对于“封装”特性,两者都能实现,但实现方式还是有些区别。

  • Java中的类即是对象,用关键字privatepublic即可达到封装的效果。
  • JavaScript中的作用域为函数,所以需要函数来实现。

继承

Java中的继承特性可以让子类获得父类中定义的属性和方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Dog extends Animal {
public String bark() {
return "wang wang wang!!!";
}
}
public class Test {
public static void main(String[] args) {
Dog dog = new Dog();
dog.set("dog");
System.out.println(dog.get()); // "dog"
System.out.println(dog.bark()); // "wang wang wang!!!"
}
}

JavaScript中实现继承得依赖函数中的prototype属性,将prototype属性指向父类的实例。

1
2
3
4
5
6
7
8
9
10
11
function Dog() {
this.bark = function() {
return 'wang wang wang!!!'
}
}
Dog.prototype = new Animal()
var dog = new Dog()
dog.set('dog')
dog.get() // 'dog'
dog.bark() // 'wang wang wang!!!'

在继承上,JavaScript和Java相比,出现了一些差异:需要在函数声明之后,让其prototype属性指向父类实例以实现继承的效果,继承多个父类时还需要将多个实例进行合并。代码的可维护性和可读性方面,显然不Java如在类的声明时进行定义。

多态

多态我们以函数为例,当一个函数需要对不同的输入结果进行不同的操作时,一般会使用函数重载。下面的例子我们编写一个bark的方法,来针对不同的输入进行显示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Cat {
// 当输入字符串时,直接显示
public String bark(String sound) {
return sound;
}
// 当为输入时,输出默认值
public String bark() {
return "...";
}
}
public class Test {
public static void main(String[] args) {
Cat cat = new Cat();
System.out.println(cat.bark()); // "..."
System.out.println(cat.bark("miao~")); // "miao~"
}
}

而JavaScript则比较“悲催”,语言本身不支持重载函数,如果多次定义函数则会被覆盖,所以只能通过判断入参来实现多态。

1
2
3
4
5
6
7
8
9
10
11
12
function Cat() {
this.bark = function(sound) {
if(undefined !== sound && null !== sound) {
return sound
} else {
return '...'
}
}
}
var cat = new Cat()
console.log(cat.bark()) //'...'
console.logcat.bark('miao')) // 'miao'

在多态这个特性上,JavaScript和Java的区别进一步被放大,因为JavaScript既没有变量类型声明,函数也不支持重载。

更多

JavaScript和Java的语言差异当然可以列出更多,比如最受人诟病之一的便是JavaScript是弱类型语言,这使得代码的健壮性下降:函数可能传入任何可能引起错误的值,但是却只能在执行时发现。当然更高级的接口功能JavaScript原生也是不支持的。

从上面的代码分析可以看出,JavaScript和Java还存在着不小的差异,令人欣慰的时ES6版本对JavaScript有不小的提升,让其更高效和健壮。更令人赞叹的是Typescript这种胶水语言的出现,让代码成为更接近Java的Script。

我们用Typescript把上面3个类重写一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 封装
class Animal {
private name:string;
public set(name:string):void {
this.name = name
}
public get():string {
return this.name
}
}
// 继承
class Dog extends Animal {
public bark():string {
return 'wang wang wang!!!'
}
}
// 多态
class Cat {
public call():string
public call(sound:string):string
public call(sound?:string):string {
if(undefined !== sound && null !== sound) {
return sound
} else {
return '...'
}
}
}

和Java语言相比,在封装继承特性上写法非常相似,有一些小小的差异比如大小写,类型申明的前后位置,而在多态特性上,Typescript增加了类型声明,但是受限于JavaScript语言限制,只能有一个函数实现,仍然避免不了参数判断。

总结

如果说ES6让JavaScript更像python,那么Typescript作为ES6的超集,让JavaScript实至名归——变得更像Java。
这样的好处一方面是让开发者能写出健壮、高效的代码,另一方面是使得JavaSript越来越有可能变成运行在Node.js上的后端语言。对于前端开发者,还是对于JavaScript的全栈开发者来说,都是意义重大的。

亚里士朱德 wechat
更多WEB技术分享请订阅微信公众号“WEB学习社”