JavaScript中关于“对象”、“继承”的困惑

在Java中对象是由类实例化的,在类中已经设置好了属性和方法,实例化后的对象也就有了这些属性和方法,是先有的属性和方法,后有的对象及属性的值;
而在JavaScript中呢?我不太懂了,找不到对应Java中类的概念,属性和值好像是一起产生的,比如这个,求指点:

 var obj = {a:1,b:2,x:function(){return this.a+this.b;}};

如果我想再创建一个属性方法一样,但是值不一样的对象,怎么做才能代码复用呢?你看这样对吗:

function MyClass(a,b,c=999){
    this.a=a;
    this.b=b;
    this.c=c;
}
MyClass.prototype={
    x:function(){return this.a+this.b;}
}
var obj1 = new MyClass(1,2);
var obj2 = new MyClass(3,4);

如果我想复用上面代码,在基础上增加一些属性和方法,也就是说要实现Java中的“继承”,我应该怎么做呢,你看这样对吗:


function MyClass(a,b,c=999){
    this.a=a;
    this.b=b;
    this.c=c;
}
MyClass.prototype.x=function(){return this.a+this.b;};
function MySubClass(a,b,d){
    MyClass.apply(this,[a,b]);
    this.d=d;
}
for(everything in MyClass.prototype){
     MySubClass.prototype[everything] = MyClass.prototype[everything];
}
MySubClass.prototype.y=function(){return this.c;}

var obj1 = new MyClass(1,2);
var obj3 = new MySubClass(5,6,7);

为了继承父类的属性,我使用了apply方法,为了继承父类的方法,我把父类的prototype对象复制到子类的prototype,然后再修改。。。这样做之后,我的子类确实继承并扩展了父类的属性。不过我发现,我这个写法有问题:
图片描述

从控制台查看obj3,属性a、b以及方法x是这个对象固有属性,看不出来是继承自父类的,这和我理解的Java中的继承是不一样的,我理解的继承是obj3没有a这个属性,访问obj3.a应该从MyClass.prototype中找,所以我以上写的应该是错的。。。
于是我把前面推翻,换了一个写法:

function MyClass(a,b){
this.a=a;
this.b=b;
}
MyClass.prototype={
    a:0,
    b:0,
    c:999,
    x:function(){return this.a+this.b;}
};
function MySubClass(a){
this.a=a;
}
function Temp(){}
Temp.prototype=MyClass.prototype;
MySubClass.prototype=new Temp();
MySubClass.prototype.d=0;
MySubClass.prototype.y=function(){return this.c;}
MySubClass.prototype.a=888;
var obj1 = new MyClass(1,2);
var obj3 = new MySubClass(3);

console.info(obj1);
console.info(obj3);
console.info(MyClass.prototype);
console.info(MySubClass.prototype);

子类的prototype我不从父类复制过来,改成继承过来,但是我查看控制台,发现和前一本版一样,父类的属性仍然在子类直接找到了,同时还出现了别的问题,构造函数变成Object了:图片描述

我现在更困惑了,我写的最后两个版本哪个更对呢
—————————————————————————————————————————
好像在控制台中查看对象,看不出来这个对象的属性是直属属性还是继承来的属性,也找不到这个对象的父类的信息,__proto__指的是构造这个对象的原型对象,__proto__是原型链,不是Java中的继承链,和子类父类间的继承没有关系,我这样理解对吗
—————————————————————————————————————————
还有一个问题:

console.info(obj3.__proto__);//MySubClass {x=function(),y=function()}
console.info(obj3.__proto__.constructor);//MySubClass(a,b,d)
console.info(MySubClass.prototype);////MySubClass {x=function(),y=function()}
console.info(MySubClass.prototype.constructor);//MySubClass(a,b,d)
console.info(MySubClass.prototype.constructor===MySubClass);//true

对象obj3.__proto__指的是创建这个对象的原型对象,也就是MySubClass.prototype,这没问题,我能理解;那这个原型对象是怎么来的呢?好像是天生就有,而且这个原型对象的contructor指的是构造函数MySubClass(叫构造函数对不对?),这是为什么呢,语法规定的?万一是个匿名构造函数呢,它指向什么?
提到匿名函数,我又测试了一段代码:

var noName1=function(a,b){
this.a=a;
this.b=b;
}
var noName2=new Function("a","b","this.a=a;this.b=b");
console.info(noName1)//function()
console.info(noName1.prototype)//Object{ }
console.info(noName1.prototype.constructor)//function()
console.info(noName2)//anonymous(a,b)
console.info(noName2.prototype)//anonymous{ }
console.info(noName2.prototype.constructor)//anonymous(a,b)
console.info(noName1.constructor===noName2.constructor)//true   Function()

从这段代码可以看出来,函数也是对象,noName1和noName2首先是函数,同时也是对象,都是由构造函数Function产生,但又有点区别,原型不同,一个是Object空对象,另一个是anonymous,我的理解对吗

阅读 3.6k
3 个回答

JS是基于对象而不是面向对象的,它的继承是伪继承。

你的代码是对的,虽然并不全面,但是初步能用了,而且体现出了JS中继承技巧的精髓,看来你对JS还是有相当了解的。

PS:JS中函数参数是没有默认值的。


补充回答:

JS中可以通过原型链继承来做到你想实现的效果:

function A(name) {
    this.name = name;
    this.a = 'I am A';
}
A.prototype = {
    hi : function() {
        console.log(this.name);
    }
};

function B(name) {
    this.b = 'I am B';
    // 这句是关键,创建A对象,并放到B对象的原型链上
    this.__proto__ = new A(name);
}

var b = new B('bbb'); // {b : 'I am B'},里面没有父类A的相关信息
b.hi(); // bbb

JavaScript 中没有真正的数据复制,所以相比“模拟类”的方式,用这种直接返回对象的方式也许是更自然的。

    var MyClass = function (a, b) {    
        return {
            a: a,
            b: b,
            c: 999,
            x: function () {
                return this.a + this.b;
            }        
        };
        
    };
    
    
    var MySubClass = function (a, b, d) {
        var obj = MyClass(a, b)
        obj.d = d;
        return obj;
    };
    
    var obj1 = MyClass(1, 2);
    var obj3 = MySubClass(5, 6, 7);

来看看结果:

运行结果

这种模式写出来的 obj1obj3 中的 x 是直属属性,并非 __proto__ 中的属性,也许好处就是避免了 @代码宇宙 所提到的 __proto__ 的兼容性问题,然后 …… 也不用胡思乱想了 :)

补充:
JavaScript 中还有一个 Object.create 的东西:

var Obj = {
    a: "a",
    b: "b",
    c: "c"
};
    
var obj = Object.create(Obj);

来看看结构:
clipboard.png

详情可见《你不知道的 JavaScript》。

现代大部分面向对象语言基于类的面向对象程序设计语言。
由于历史原因,JS是原形继承(来自于古老的self语言,一种基于原型的面向对象程序设计语言)。

如果希望了解更多可以参考:
http://www.ruanyifeng.com/blog/2011/06/birth_of_javascript.html
http://segmentfault.com/a/1190000002596600

如果楼主希望在ES5中实现类似于基于类的继承可以参考:
http://www.cnblogs.com/sanshi/archive/2009/07/08/1519036.html

又或者使用Babel、Typescript等于处理工具引入ECMAScript6的特性,将会有语言语法上获得类继承、参数默认值等一些新特性的支持。
参考:http://es6.ruanyifeng.com/#docs/class

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏