search

Home  >  Q&A  >  body text

javascript - 我想知道这两种JS原型继承的定义方式的区别

今天想再深入理解一下原型继承,发现以下这两种继承方式都没什么问题,请问大牛们,这两种方式有区别吗,区别是啥?

function People(){}
People.prototype = {role : 'user'}
function Male(){}
Male.prototype = People.prototype;
var m = new Male()
m.role // 输出 user
m instanceof People // 输出 true
function People(){}
People.prototype = {role : 'user'}
function Male(){}
Male.prototype = new People(); // 这里跟上面定义方式不同
var m = new Male()
m.role // 输出 user
m instanceof Male
天蓬老师天蓬老师2822 days ago306

reply all(4)I'll reply

  • 巴扎黑

    巴扎黑2017-04-10 15:21:44

    有区别!

    原形链不一样。第一个的原型链是:m->People.prototype,第二个的原型链是:m->{// People实例}->People.prototype

    提问者提问:这两种写法除了原型链条上的区别外,如果在运用到实际的工作中,会不会遇到坑?

    会!!第一种比较可能遇到坑!看代码:

    javascript// 假设全局存有 EventProxy 构造函数
    // 我想让继承 People 的所有实例具有 EventProxy 的事件方法,如:
    ...
    var m = new Male();
    
    m.on('beat', function () {
      console.log('好疼');
    });
    
    m.emit('beat'); // =>'好疼'
    

    第一种继承实现:

    javascriptfunction People () {
      EventProxy.call(this);
    }
    
    function Male () {}
    
    Male.prototype = People.prototype;
    
    var m = new Male()
    
    m.on('beat', function () {
      console.log('好疼');
    }); // 报错!!!,不存有on方法
    

    第二种继承实现:

    javascriptfunction People () {
      EventProxy.call(this);
    }
    
    function Male () {}
    
    Male.prototype = new People();
    
    var m = new Male()
    
    m.on('beat', function () {
      console.log('好疼');
    }); // 不报错,正常
    

    当然对于第一种继承实现,也是有方法避免报错的:

    javascriptfunction People () {
      EventProxy.call(this);
    }
    
    function Male () {
      People.apply(this, arguments);
    }
    
    Male.prototype = People.prototype;
    
    var m = new Male()
    
    m.on('beat', function () {
      console.log('好疼');
    }); // 不报错,正常
    

    总结:在javascript的原型继承的世界里,这是十分灵活自由的,所以当然也会存有很多很多的坑。对于很多初学者或小白(比如我)是很难明白那种方法比较正统,也很难明白那种方法比较正确,但这不重要,毕竟这些‘哲理’上的东西需要经验的积累,但至少需要知道当发生报错时,究竟是什么错并如何解决!

    PS:对于第二种继承实现和对第一种继承实现的完善同样是有区别的。

    reply
    0
  • 大家讲道理

    大家讲道理2017-04-10 15:21:44

    可以参考下ruanyf的http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_inhe...

    reply
    0
  • PHP中文网

    PHP中文网2017-04-10 15:21:44

    经我思考,Male.prototype = People.prototype 的方式不可用,因为Male继承自People,但是Male还需要具备自身所拥有的属性,如果直接使用 Male.prototype = People.prototype 的方式,未来会污染到父类原型链,比如我给 Male.prototype.role = 'male user',那么所有的People.prototype都被污染了!

    因为 Male.prototype 是一个原型引用,改变引用类型,所有的引用都会改变。

    因此,Male.prototype = new People() 才是正统!


    一个网友建议使用:Male.prototype = Object.create(People.prototype) 我觉得也是赞赞的。

    reply
    0
  • PHP中文网

    PHP中文网2017-04-10 15:21:44

    JavaScript的继承是基于原型链而非“类”,其所谓继承不过是在原型链中查找其本身不存在的属性而已。
    目前最有效的设计,是在子类型中调用父类型构造函数,同时指定子类型的prototype属性为一个以父类型的prototype为原型的对象,这样可以构建合理的原型链。
    还是拿传统的动物来举例:

    function Animal(name) {
        if (!(this instanceof Animal)) {
            //防止没有使用new调用此函数时污染全局环境
            return new Animal(name);
        }
        this.name = name;
    }
    Animal.prototype.type = 'Animal';
    Animal.prototype.run = function () {
        console.log(
            this.type + ' ' + this.name +  ' is running!'
        );
    };
    

    如上我们建立了一个Animal类型,其name属性由参数name确定,其实例的原型中的type属性由Animal.prototype.type来确定。

    var animalJoe = new Animal('Joe');
    animalJoe.run(); // Animal Joe is running!
    

    下面将建立一个Dog类型,继承自Animal:

    function Dog(name, color) {
        if (!(this instanceof Dog)) {
            return new Dog(name, color);
        }
        Animal.call(this, name); //调用父类型构造函数初始化子类型的实例
        this.color = color;
    }
    Dog.prototype = Object.create(Animal.prototype); //Dog.prototype的原型即Animal.prototype
    Dog.prototype.constructor = Dog; //指定构造函数,方便以后使用
    console.log(Dog.prototype.__proto__ === Animal.prototype); //true
    Dog.prototype.type = 'Dog'; //覆盖了父类型原型中的type属性
    
    var dogJack = new Dog('Jack');
    dogJack.run(); //Dog Jack is running!
    console.log(dogJack instanceof Animal); //true
    console.log(dogJack instanceof Dog); //true
    console.log(dogJack.__proto__ === Dog.prototype); //true
    console.log(dogJack.__proto__.__proto__ === Animal.prototype); //true
    

    为方便继承可以采用如下函数:

    function extend(Pt, St, np) {
        //指定原型
        St.prototype = Object.create(Pt.prototype);
        St.prototype.constructor = St;
        //扩展新的原型
        for (var i in np) {
            St.prototype[i] = np[i];
        }
    }
    

    reply
    0
  • Cancelreply