JavaScript基于对象编程和Java面向对象编程的对比

问:Java和JavaScript有什么关系?

答:Java和JavaScript的关系与雷锋和雷峰塔的关系一样的。

 

雷锋和雷峰塔只是名字相似,但Java和JavaScript就不只是命名上的相似了。JavaScript借鉴了很多Java的思想和语法(同时还有C\C++),Java早时曾被叫做C加加减减,而JavaScript则被称作C减减,可见JavaScript与众多高级语言有很多相似之处。

但是,由于JavaScript是一种基于对象(object-based)的语言而不是严格意义上的面向对象(object-oriented)语言,在进行编程时则与Java有很大区别。在这篇文章里,我将把Java和JavaScript在面向(基于)对象编程方面进行简单的对比。

实例属性(变量)的定义与调用

Java:


public class ClassA {
    public int a = 1;
    public int b = 2;
}
ClassA obj = new ClassA();
System.out.println(obj.a); //输出:1
System.out.println(obj.b); //输出:2

JavaScript:


function ClassA {
    this.a = 1; //第一种:调用this指针
};
ClassA.prototype.b = 2; //第二种:调用prototype(对象原型)
obj = new ClassA();
console.log(obj.a); //输出:1
console.log(obj.b); //输出:2

可以发现,在JavaScript中对象的属性定义有两种方法,其中调用this指针的方法与Java最为相似。

实例方法(函数)的定义与调用

Java:


public class ClassA {
    public int a() {
        return 1;
    }
    public int b() {
        return 2;
    }
}
ClassA obj = new ClassA();
System.out.println(obj.a()); //输出:1
System.out.println(obj.b()); //输出:2

JavaScript:


function ClassA() {
    this.a = function() {
        return 1;
    };
};
ClassA.prototype.b = function() {
    return 2;
}
obj = new ClassA();
console.log(obj.a()); //输出:1;
console.log(obj.b()); //输出:2;

方法和属性的定义、使用是非常相似的,没有太多变化。

类属性(静态属性)的定义与调用

Java:


public class ClassA {
    public static int A = 1;
}
System.out.println(ClassA.A); //直接调用类,输出:1
ClassA obj = new ClassA();
System.out.println(obj.A); //调用由类产生的实例,输出:1

JavaScript:


function ClassA() {
} //首先初始化对象
ClassA.A = 1; //定义“类变量”(本质是函数的变量)
console.log(Class.A); //直接调用类,输出:1

obj = new ClassA();
 // 不能通过用new产生的实例调用类属性
//console.log(obj.A);

在Java中,我们可以通过由类产生的实例调用静态属性,但JavaScript中不允许这样做,这和JavaScript中使用函数表示对象的设计有关。定义类方法
与此类似,不在赘述。

子类的定义与调用

Java:


public class ClassA {
    public int x;
    public ClassA(int a) {
        this.x = a;
    } //构造器
    public void print() {
        System.out.println("x=" + this.x);
    }
} //定义类ClassA

public class ClassB extends ClassA {
    public int y; //定义新变量

    public ClassB(int a, int b) {
        super(a); //调用父类的构造器
        this.y = b;
    } //重写构造器

    @Override
    public void print() {
        super.print(); //调用父类的方法
    } //重写父类方法

    public int sum() {
        return this.x + this.y;
    } //定义新方法
} //定义继承于ClassA的类ClassB

ClassA objA = new ClassA(1);
objA.print(); //调用ClassA的实例的print方法,输出:x=1

ClassB objB = new ClassB(2,3);
objB.print(); //调用ClassA的实例的print方法,输出:x=2\ny=3
System.out.println('x+y=' + objB.sum()); //输出:x+y=5

JavaScript:


function ClassA(a) {
    this.x = a;
    this.print = function() {
        console.log('x=' + this.x);
    };
} //定义类ClassA

ClassB.prototype = new ClassA(1);
function ClassB(a, b) {
    ClassB.prototype.constructor.apply(this, arguments); //调用父类的构造器
    this.y = b;
    this.print = function() {
        ClassB.prototype.print.call(this); //调用父类的方法
        console.log('y=' + this.y);
    }
    this.sum = function () {
        return this.x + this.y;
    }
} //定义继承于ClassA的类ClassB

var objA = new ClassA(1);
objA.print(); //输出:x=1

var objB = new ClassB(2,3);
objB.print(); //输出:x=2\ny=3
console.log('x+y=' + objB.sum()); //输出:x+y=5

JavaScript中类的继承比较复杂,这是因为JavaScript是基于对象的语言而不是面向对象的,在JavaScript中并没有“类”的概念,一切皆是函数。在这里我只是采用了一种最为通用的子类使用方法,除此之外还有很多方法,这篇文章的最后链接了很多解决方案。

最后,我在node.js的源码中找到了util.inherits的定义,如果使用node.js环境开发可进行调用。


/**
 * Inherit the prototype methods from one constructor into another.
 *
 * The Function.prototype.inherits from lang.js rewritten as a standalone
 * function (not on Function.prototype). NOTE: If this file is to be loaded
 * during bootstrapping this function needs to be revritten using some native
 * functions as prototype setup using normal JavaScript does not work as
 * expected during bootstrapping (see mirror.js in r114903).
 *
 * @param {function} ctor Constructor function which needs to inherit the
 *     prototype.
 * @param {function} superCtor Constructor function to inherit prototype from.
 */
exports.inherits = function(ctor, superCtor) {
  ctor.super_ = superCtor;
  ctor.prototype = Object.create(superCtor.prototype, {
    constructor: {
      value: ctor,
      enumerable: false,
      writable: true,
      configurable: true
    }
  });
};

一条评论

  1. 不对吧 。子类不能重写父类的构造方法。

    public class A {
    public A() {
    System.out.println(“A”);
    }

    }

    public class B extends A {

    public B() {
    System.out.println(“B”);
    }

    public static void main(String[] args) {
    B b = new B();
    }
    }

    最后打印的结果是:A,B 说明先实例化父类 然后再实例化子类的

    Reply
  2. 如您所说,“重写”这个词用得确实不是很恰当,更为恰当的说法是“重载”。但Java的构造函数重载非常复杂,一般情况下如果不显式的调用父类的构造函数,则Java会自己调用。更特别的,如果没有显式调用父类的构造函数,当父类“重载”了无参数的默认构造函数时候,Java会去调用super();当父类没有“重载”无参数的默认构造函数,而是“重载”了其他形式比如super(int),子类会产生一个编译错误:Implicit super constructor ClassFather() is undefined. Must explicitly invoke another constructor。
    所以,您说得对。
    Java和JavaScript对重写、重载构造方法的处理不是完全一样的,我在写的时候更多的是按照更为熟悉的Java的思路写的,私以为Java思路更为严谨,您觉着呢?

    Reply

文章评论: