前言
java和javascript并非周杰和周杰伦 雷锋和雷峰塔 老婆和老婆饼的一点关系也没有
作为从java后端转型到前端的一员,应该对这两种语言有一定的了解,随着nodeJS的发展,解放了前端的局限性,使得前端技术的发展速度也越来越快,随着ES6的诞生,一步一步让JS的功能更加完善也更加强大,通过javascript与java的对比,让大家更加了解到计算机语言的共通性,更加熟悉ES6的新特性
区别:
Javascript与Java在浏览器中所执行的方式不一样。
Javascript是一种解释性语言,其源代码在发往客户端执行之前不需经过编译,而是将文本格式的字符代码发送给客户,即Javascript语句本身随Web页面一起下载下来,由浏览器解释执行。
而Java是一种编译型语言,Java的源代码在传递到客户端执行之前,必须经过编译,因而客户端上必须具有相应平台上的仿真器或解释器,它可以通过编译器或解释器实现独立于某个特定的平台编译代码。
Javascript与Java代码格式不一样。
Javascript的代码是一种谋咀址格式,可以直接嵌入HTML文档中,并且可动态装载,编写HTML文档就像编辑文本文件一样方便,其独立文件的格式为X.Js。
Java是一种与HTML无关的格式,必须通过像HTML中引用外媒体那么进行装载,其代码以字节代码的形式保存在独立的文档中,其独立文件的格式为X.class。
Javascript与Java的加载方式不同。
Javascript采用动态加载,即Javascript的对象引用在运行时进行检查。
Java采用静态加载,即Java的对象引用必须在编译时的进行,以使编译器能够实现强类型检查。
Javascript与Java实现面向对象的方式不同
Java语言通过类实现面向对象,对象是类的实例,而Javascript则是通过构造函数作为对象的模板,使用prototype实现继承。
相似的语法:
class 类与继承 私有变量
先上两段代码: java中定义类:
public class Person{
private String name;
private int age;
public Person(String name,int age){
this.name=name;
this.age=age;
}
public void getInfo(){
System.out.println(name+age);
}
}
Es6中定义一个类:
class Person{
constructor(name,age){
this.name=name;
this.age=age;
}
getInfo(){
return this.name+','+this.age;
}
}
//调用
let person=new Person("koala","123");
类中的变量
二者异
在 java中可以直接声明各种类型的私有变量
在ES6中的类不可以直接在类中声明私有变量,声明后会报错。 注意:但是随着v8的更新,在node12版本中,ES增加了一些新规范,其中就有 支持类的私有变量 这一条。 代码如下:
class Greet {
#name = 'World';
get name() {
return this.#name;
}
set name(name) {
this.#name = name;
}
sayHello() {
console.log(`Hello, ${this.#name}`);
}
}
在类的外部或去#name变量会抛出异常
const greet = new Greet()
greet.#name = 'NewName';
// -> SyntaxError
console.log(greet.#name)
// -> SyntaxError
类中的构造函数
- 二者同:
如果声明一个一个类的时候没有声明构造函数,那么会默认添加一个空的构造函数,构造函数在new实例化一个对象的时候会被调用
- 二者异:
在ES6中,可以在构造函数中直接定义类方法(类方法也可以是箭头函数),代码如下
constructor(name,age){
this.name=name;
this.age=age;
this.getInfo()=()=>{
console.log("name"+this.name+"sex"+this.sex);
}
}
类中的方法
- 二者同:
有参,无参函数,函数调用方式相同。静态方法,ES6中用static声明一个静态方法,方法只能用类名直接调用,不能通过类的实例调用
- 二者异:
ES6在类中声明函数,无需使用function关键字,java的类中必须使用关键字声明函数。
ES6方法内部访问类属性的时候需要this来访问,java不需要。
ES6的构造函数中可以定义函数,java不可。
类中的继承
- 二者同:
继承关键字都是extends,super方法的使用
- 二者异:
继承的调用:
ES6需要注意的是super只能调用父类方法,而不能调用父类的属性,方法定义再原型链中,属性定义在类的内部
java中,super关键字,可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。
继承过程中的构造函数:
ES6中,子类中,super方法是必须调用的,因为子类本身没有自身的this对象,需要通过super方法拿到父类的this对象。在子类中,没有构造函数,那么在默认的构造方法内部自动调用super方法,继承父类的全部属性,子类的构造方法中,必须先调用super方法,然后才能调用this关键字声明其它属性。(子类的this就是在这里调用super之后,拿到父类的this,然后修改这个this来的)
js的重载
这个方法在JQuery之父John Resig写的《secrets of the JavaScript ninja》中,这种方法充分的利用了闭包的特性!
var old = object[name]; //把前一次添加的方法存在一个临时变量old里面
object[name] = function () { // 重写了object[name]的方法
// 如果调用object[name]方法时,传入的参数个数跟预期的一致,则直接调用
if (fn.length === arguments.length) {
return fn.apply(this, arguments);
// 否则,判断old是否是函数,如果是,就调用old
} else if (typeof old === "function") {
return old.apply(this, arguments);
}
}
}
addMethod(window, 'fn', (name) => console.log(`我是${name}`))
addMethod(window, 'fn', (name, age) => console.log(`我是${name},今年${age}岁`))
addMethod(window, 'fn', (name, age, sport) => console.log(`我是${name},今年${age}岁,喜欢运动是${sport}`))
/*
* 实现效果
*/
window.fn('林三心') // 我是林三心
window.fn('林三心', 18) // 我是林三心,今年18岁
window.fn('林三心', 18, '打篮球') // 我是林三心,今年18岁,喜欢运动是打篮球
Typescript 和 java很像
TypeScript是JavaScript的超集。添加了可选的静态类型和面向对象编程。
意思就是在ts中可以直接书写js。在我的第一感觉里,js就像是编译后的可执行文件,而ts就像是Java语言,或者Scala语言等。不过,这也只是类比而已,ts中的很多语法,其实大多数是编译期用的,在真正的js文件里,抹除了很多的信息。
如上图,ts文件通过tsc编译器,生成普通的js文件。接下来,就可以使用node命令执行这个普通的js文件。 下面是一段简单的ts代码。是不是和Java很像?
class Animal {
public name;
protected a;
private b: string;
constructor(name) {
this.name = name;
}
sayhi() {
return `my name is ${this.name}`;
}
}
class Cat extends Animal {
constructor(name) {
super(name)
}
sayhi() {
return "meow " + super.sayhi()
}
static iaAnimal(a) {
return a instanceof Animal;
}
}
function gen<T extends Animal>(name: T): void {
console.log(name.name)
}
下面简单介绍一下一些基本的语法,当然了,有些语法是ES6的,但我也把它揉在一块了。
类型相关
由于js是一门弱类型的语言,有很多的运行时转换,就不能使用类似于Java一样的强类型转换方式,所以typescript可以在编译阶段通过语言特性增强一些类型检查的功能。而在运行时,大多数根本就没有这样的判断,所以ts更像是一个过程工具。
对于一门语言来说,肯定离不开基本数据类型和自定义类型。ts提供了一系列的关键字作为特殊类型代号,其他的都好说,唯一让我有点兴趣的是联合类型,这非常有趣的一个特性。
typeof关键字用于判断是否是某种类型string表明是字符串类型,它不同于Java,首字母是小写boolean和Boolean类型是不同的number直接表示数字类型,没有那么多麻烦的精度问题(0b、0O、0x指明进度问题)any是万能类型,相当于Java中的Object,全部是any相当于是普通js。所以,如果你恨ts,就可以一路any到天明never表示那些永不存在的值类型object表示非原始类型,和Java中的不太一样string | number类似这样的是联合类型,这也是非常神奇的一点。这里只允许这两种类型的转换,并且能调用的方法,要取两者交集- `` 之间的字符串可以使用类似shell的语法,做模版
${} readonly这竟然是个关键字,表明只读属性[propName: string]: any;这一行代码值得研究,但不推荐这么做number[]数组和Java类似,不过这是声明后置的语法,值使用[]声明,而不是{}function函数和javascript的没什么区别,有两种声明方式。lambda对js来说肯定是强项=>的语法也比较恶心人,和ES6联合起来可以写一大篇文章...rest注意这个东西!类似Java中变参的意思as是一个关键字,我们可以理解为Java的cast,但它也仅仅是语法检查而已,运行时并无法控制。(window as any)很酷,但容易出错
声明相关
let用来声明普通变量,作用域小,{}之内var作用域大,函数级别或全局const只读变量,是静态的;readonly却是动态的,只不过声明后不能改而已declare var声明全局变量(.d.ts后缀的文件,这是一种约定)declare function声明全局方法declare class全局类declare enum全局枚举类型declare namespace全局命名空间export这个主要是用于npm的,后续可以使用import导入
那什么是declare呢?私以为这个类似于python中的__init__.py文件,用于暴露一些接口和函数,另外为代码自动补全提供了基本数据。
两个默认的约定。配置了tsconfig.json以后,可以直接执行tsc命令进行编译。///三斜杠指令,很丑。
关于Class
从Java过来的同学,会发现这些概念和Java是类似的,不过ts的语法更加简单。
getset竟然是关键字,后面可直接跟上函数。可以改变属性的赋值和读取行为!static、instanceof、public、protected、private这些也都是有的,真的感觉和写Java没什么两样constructor默认是构造方法,不像是Java要和class的名词一样abstract也有,表明子类必须实现,没什么两样- 关于类和接口的区别,我觉得熟悉java的,对ts来说就是透明的
- 范型在Java里,语法也是非常的变态,因为你很多时候不知道要把
<>放在什么地方。在ts中,一样的难受。具体怎么熟悉,只有在实践中磨练了
关于type、interface、class
interface定义了接口,这里的接口有意思,可以声明实体,但是必须全部赋值才行。可以通过在成员变量前面加?的方式来表明非必须,但很丑;?也可以定义函数的可选参数,6的很type和interface一样,在编译时,会被抹除。两者语法有细微差别,同时type可以定义更多类型,比如基本类型、联合类型、元组等class可以在里面实现方法,有点Java的味道了,所以不会被编译器抹除。javascript使用构造函数模拟class。
开发工具
tsc是typescript的编译器。如果编译出错,可以指定底层的语法特性。
tsc --target es6
复制代码
建议配置在tsconfig.json文件里,会被自动识别。
{
"compilerOptions": {
"module": "commonjs",
"outDir": "lib",
"declaration": true,
"target":"es6"
}
}
复制代码
vscode,通过.d.ts文件,可以做到自动补全和语法检查。但针对于复杂的个性化配置,还是无法做到类似idea那样智能的提示和配置。
由此造成的后果就是,手头上必须有一份参考文档,并对参考文档的目录和功能熟悉。在遇到相应的配置参数时,不得不翻阅到相应的地方,然后拷贝过来。这对于一个javaer来说,实在是太痛苦了。