Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

原型链与继承 #23

Open
zhaozy93 opened this issue Aug 31, 2017 · 0 comments
Open

原型链与继承 #23

zhaozy93 opened this issue Aug 31, 2017 · 0 comments

Comments

@zhaozy93
Copy link
Owner

原型链与继承

众所周知JS的继承最主要是通过原型链来实现。最近踩了一个坑,所以再来回顾一下继承相关的概念。

Problem

  function p(){
    this.num = 1;
  }
  p.prototype.age = 2;
  p.prototype.num = 2;
  p.prototype.add = function(){
    this.num ++;
    this.age++;
  };
  let a = new p();
  a.num;
  a.age;
  a.add();
  a.num;
  a.age;

其实这段代码和继承一点关系都没有,只是实例化而已。来看一下后面几行的输出是什么

  a.num; // 1, p的num属性屏蔽了原型链上面的num属性
  a.age;  // 2,  按照原型链一环一环向上查找,终于找到了age属性
  a.add(); // 执行一个函数,这里面的问题就大了
            // 首先执行this.num++ 比较容易理解,a对象本身就有num属性, 好像没啥问题
            // this.age++ 因为a上面(hasOwnProperty)没有age属性,但是原型链上有这个属性,是不是就把原型链上的自增了呢
            // 答案肯定是否定的,this.age属性的读会按照原型链一环环向上查找,
            // 但是this.age属性的写和原型链一点关系也没有,但是呢this.age++ => this.age = this.age + 1;  需要先去读取this.age拿读取后的结果作为增的基础因子,再将结果直接赋值给对象a
  a.num;   // 2,  刚刚自增了一次, 所以是2
  a.age;   // NaN   undefined++ 肯定是NaN了

继承与实例化

  function p(){
    this.num = 1;
  }
  p.prototype.num = 2;
  let a = new p();

上面这段最普通的new代码叫 实例化,尽管这里面也涉及到原型链,但它不属于继承。

继承最基本的要求是要有一个父类、一个子类。

这可能不是一个难点,也不是一个复杂点,但恰好之前对这个概念有点模糊,认为实例化也属于继承的一种实现方式,概念上有点混淆。但现在应该清楚的能区分开,实例化和继承是没有关系滴。

继承的方式

原型链继承

将实例化的父类作为子类原型, 缺点非常明显啦,不太容易实现多继承,构造父类是不够灵活。

// 定义一个动物类
function Animal (name) {
  // 属性
  this.name = name || 'Animal';
  // 实例方法
  this.sleep = function(){
    console.log(this.name + '正在睡觉!');
  }
}
// 原型方法
Animal.prototype.eat = function(food) {
  console.log(this.name + '正在吃:' + food);
};

function Cat(){ 
}
Cat.prototype = new Animal();
Cat.prototype.name = 'cat';

// Test Code
var cat = new Cat();

构造继承

构造继承解决了前面不能实现多类继承的缺点,但是有更大的一个缺点就是不能继承父类原型链。 其实只是用this来执行了一个特殊函数而已。

function Cat(name){
  Animal.call(this);
  this.name = name || 'Tom';
}

// Test Code
var cat = new Cat();

实例继承

利用new操作符可以返回对象的特点, 在内部实例化一个父类,然后将父类return, 缺点非常大就是子类不能像普通类一样书写 Cat.prototype.key = ....

function Cat(name){
  var instance = new Animal();
  instance.name = name || 'Tom';
  return instance;
}

// Test Code
var cat = new Cat();

拷贝继承

拷贝继承就是最原始的方法,想办法将父类的属性都拷贝到子类上。 但是不能拷贝父类原型链,因此也不太完美

function Cat(name){
  var animal = new Animal();
  for(var p in animal){
    Cat.prototype[p] = animal[p];
  }
  Cat.prototype.name = name || 'Tom';
}

// Test Code
var cat = new Cat();

组合继承

这种方法看起来不错,既能保证了父类的静态属性又能保证父类的原型链,只是需要实例化父类两次,有点浪费

function Cat(name){
  Animal.call(this);
  this.name = name || 'Tom';
}
Cat.prototype = new Animal();

// Test Code
var cat = new Cat();

寄生组合继承

这个特别像是平时用到的super(), 虽然也实例化了两个类,但是第二次是一个super类,基本空白,只用于原型链传递,不会将静态属性与方法赋值两次, 但是这种方法需要提炼一下,以供日常使用。 比较完美,但比较麻烦。

function Cat(name){
  Animal.call(this);
  this.name = name || 'Tom';
}
(function(){
  // 创建一个没有实例方法的类
  var Super = function(){};
  Super.prototype = Animal.prototype;
  //将实例作为子类的原型
  Cat.prototype = new Super();
})();

// Test Code
var cat = new Cat();

总结

其实日常工作可能对继承使用的并不是特别多,但作为基础概念还是要牢记的, 文章较多的参考了其余的blog,主要在于加深印象。

参考

http://www.cnblogs.com/humin/p/4556820.html

@zhaozy93 zhaozy93 mentioned this issue May 1, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant