一个 Java 程序员对 JavaScript 的理解
TLDR 本文是一个 Java 程序员初学 JavaScript 的记录
知道吗?《重构》要出第二版了
是吗,和第一版有啥区别?
语言从 Java 改成 JavaScript 了
哦
我对 Java 挺熟的,但是 JavaScript 只写过一些前端脚本,用过 nodejs 但也就是复制黏贴做点 CRUD, 这个水平去看书恐怕不行。
那就去学呗, JavaScript 那么流行,学了不亏。
可我总觉得 JavaScript 很复杂,应该说很乱,各种各样的坑,Java 就很工整.
你说的工整是什么意思?
JavaScript 是脚本语言啊,各种变量,函数散落在各处,看的头晕。
这方面语言本身是这样设计的,函数是一等公民,不像 Java,函数必须被定义在类中,这大概就是你觉得 Java “工整“ 的原因吧。
函数是一等公民我还能理解,可是 JavaScript 为啥没有类啊,不对,好像 es6 有 class,但是还要转换,好麻烦。
慢慢来,先别考虑 es6,你要类来干嘛?
当然是创建对象啊。
在 JavaScript 中,创建对象很容易啊
const person = {
firstName: 'foo'
}
console.log(person.firstName);
额,你这只是属性没有方法啊, 这样的对象是没有灵魂的
要方法也行,function 也是一个对象,我们可以把函数直接赋值给 person 对象的一个属性
const person = {
firstName: 'foo',
hello: function() { console.log("hello world") }
}
person.hello();
额,看起来和 Java 的对象很相似了,但是,下次我需要另一个 person,总不能每次都复制一大段代码吧。
办法当然有,不过还是慢慢来,目前我们知道 JavaScript 可以很方便的创建对象,但是没有类,那么我们可以手动创建一个功能和类类似的东西。
等等,让我想想,如果是 Java 的话,除了 new 一个对象外,得到对象的方式还可以是工厂类,工厂类一般都使用静态方法,所以使用对象替换也完全可行。
没错,我们可以创建一个 PersonFactory 来创建各种 person 对象,顺便说一句,Java 的静态方法实在是太不 OO 了,直接使用对象更合理。
function PersonFactory(last_name) {
let person = {}
person.last_name = last_name
person.hello = function (last_name) {
console.log(`hello, I am ${this.last_name}.`)
}
return person
}
person = PersonFactory('foo')
person.hello();
看着还行,但是比起 Java 的原生 Class 还是太麻烦了。
淡定,这只是一个演示,JavaScript 有原型继承,可以使用 prototype 来实现,这才是最常用的方式(上面的写法也很浪费内存,因为方法不能共享)。
Person.prototype.hello = function(last_name) {
console.log(`hello, I am ${this.last_name}.`)
}
function Person(last_name) {
let person = Object.create(Person.prototype)
person.last_name = last_name
return person;
}
person = Person("foo")
person.hello()
原来是这样,我明白了,其实这样已经实现了 Java 的 class 功能了,难怪一开始你说 es6 的 class 只是语法糖而已,那 class 的写法是?
使用 es6 的 class 的话就是这样:
class Person {
constructor(last_name) {
this.last_name = last_name
}
hello(){
console.log(`hello, I am ${this.last_name}.`)
}
}
const person = new Person('foo')
person.hello()
这样就和 Java 基本一样了。等等,这个 constructor 是啥啊?
就是构造器,看来我们步子迈的有点大了,我们先看看上面的例子中, person 的构造器是什么:
function Person(last_name) {
this.last_name = last_name
}
var person = new Person("foo")
console.log(person.constructor)
看到没,就是 Person 函数,可见,构造器就是构造函数的引用,它告诉我们 person 对象是哪儿来的。我们可以通过 instanceof
操作符来判断对象和构造器函数的关系:
console.log(person instanceof Person)
明白了,对了,上面的代码里还有一个 this, 听说 javascript 里的 this 非常难以理解,到底是怎么回事呢?
嗯, 其实一点也不难,我们先看看 Java 中的 this 吧。
public class Person{
private String lastName;
public Person(String lastName) {
this.lastName = lastName;
}
public void hello() {
System.out.println("hello, I am " + lastName);
}
}
Person person = new Person("foo")
person.hello();
这个例子很简单,带 this 的 lastName 是私有属性, 不带 this 的 lastName 是传入的参数,这看似是一个可以死记硬背的知识点。
但是我们可以换个角度看看,在 Java 中,可以调用方法的实例必然是 person 对象,所以 this 肯定是指向 person 对象的。 而在 JavaScript 中,函数是独立存在的,因此调用方和函数可以没有任何关系。
不过原理是类似的,那就是 this 指向的就是方法(函数)的调用方。
我们可以看看下面这个例子:(first name 的 例子)
function Person(last_name) {
console.log("My name is: " + this.first_name + " " + this.last_name);
}
Person.prototype.hello = functon() {
console.log("hello, my name is" + this.first_name + " " + this.last_name);
}
var person = new Person("foo")
person.first_name = "bar"
person.hello();
这个例子可以说明,this 和它“所在”的函数是没有任何关系的,在 function Person 中,根本没有定义 first_name 这个属性,这个属性是我们定义在 person 对象中的。 当 hello 函数被执行时, this 就是 person 对象,所以 this.first_name
就是 person 对象 first_name 属性。 当然 JavaScript 这样的动态语言,肯定不止看上去那么简单,不过这个解释对于理解大部份情况的代码已经足够了。
现在看来 JavaScript 也不是那么 “神奇” 和难以理解了,看来我以前对于面向对象的理解太片面了。
其实刚才我教你的都是很基础的部分,不过既然你已经把思路从 Java 中的 Class 转到 javaScript 的 object 了,那就是一个很好的开始了。