1

继承

今天给大家科普一个叫做继承的大哥。之所以叫做大哥,是因为他确实是所以语言都必须具备的。而且这也是BAT公司前端面试中,必提及的一个知识点。 由于js 是由 大神 在10天之内写完了,但后来由ECMA262接手优化,最终有了现在丰满的JS。 但是关于继承的遗留问题应该算是比较严重的。 先说一下基本的js继承有哪些.

借用构造函数

啊~~~ md, 名字怎么这么怪嘞! 这个官方的说法,我们乡下人管他叫做复制(copy);
来个栗子:


function Father(name){
    this.name =name;
}
function Children(name){
    Father.call(this,name);
}
let jimmy = new Children("jimmy");
console.log(jimmy.name);  //jimmy

没错,看清楚,其实就是使用了函数对象自带的方法。call 来创建的。 将父构造函数的属性给copy到子类上,相当于建立了一个副本。但其实这种方式并没有什么卵用。 和我前一篇说过,构造函数模式不流行就是因为他的重用性 === 0. 这种继承方式的重用性(呵呵) <=0. 但是我们也不能否定他的存在,毕竟我以前也用过。

介绍一个当今比较流行的,组合继承

组合继承

组合就是融合了原型和构造器的一种继承方法。 这个应该算是一种比较稳妥的继承方式.
即能排除引用类型造成的问题,又可以实现共享的效果。

function A (){
 this.name = "jimmy";
}
A.prototype.speak = function(){
    console.log(`my name is ${this.name}`);
}
function B(){
  A.call(this);
}
B.prototype = new A();
B.prototype.constructor = B;
var b = new B();
console.log(b.name);  //jimmy 
b.speak();  //my name is jimmy

这种应该算是比较经典了,但这样会两次调用到父类型,对内存影响还是比较大的。 所以有人提出中间创建一个寄生组合式继承. 但这个真的很鸡肋。 JS的一个继承能扯出这么多东西,也是醉了。
但是es6 class 的出现,解决了这一痛点。 虽然这个东西有悖js 的原理(因为在js中是没有类这个概念的),但是实用性确实相当的好.
所以今天的主角应该是class 这个东西

class

在es6中新出了一个feature--class. 中文叫做类. 应该算是混合模式的一个总结吧。在使用class的使用,你完全可以不去写,constructor, prototype之类的东西了.
来个栗子:

//原生的混合模式:
function Point(x,y){
  this.x = x;
  this.y = y;}

Point.prototype.toString = function () {
  return '(' + this.x + ', ' + this.y + ')';
  }
var point = new Point(1,2); 
//使用class的类:
class Point {
  constructor(x, y) {  //本质上还是原型链上的方法
  this.x = x;  //这里的this和上面的一样都代表实例对象
  this.y = y;
  }

  toString() {
  return '(' + this.x + ', ' + this.y + ')';
  }
}
var point = new Point(1,2);

虽然我打心里是不愿意将类加入js里面(没准以后就变为标准了),但为了大家的理解,我还是把这个称为类吧; ):
可以看到类和原型js的写法的不同吧。我先说一下语法吧。
创建一个类,就和在java里面创建类是一样的。
初始化使用class + 类名(首字母大写) + {...}

class Hehe{
...
}
var hehe new Hehe();

初始化话,上面也已经说了。在类中需要注意一个叫做constructor的方法,这个就相当于构造函数里面的内容,他会在类初始化时,自动执行. 所以可以在constructor里面传递一些参数.

class Jimmy{
    constructor(name){
        this.name = name;  //这里的this和构造函数里面的this的属性是一致的
    }
}
let jimmy = new Jimmy("jimmy");
console.log(jimmy.name);  //jimmy

当然在类中还可以添加方法。具体做法如下:

class Jimmy{
    constructor(name){
        this.name = name;  //这里的this和构造函数里面的this的属性是一致的
    }
    speak(){
        console.log(`my name is ${this.name}`);
    }
}
let jimmy = new Jimmy("jimmy");
jimmy.speak();  //my name is jimmy;

需要注意,请不要手贱在每个{} 后面加上 "," 像这样

class Jimmy{
    constructor(name){
        this.name = name; 
    },
    speak(){
        console.log(`my name is ${this.name}`);
    }
}

到时候出个bug, 我看你哭去吧。
而这样定义的方法其实是存在于Jimmy.prototype 上的。
我们检验一下Jimmy的类型;

Jimmy instanceof Function; //true

所以说 其实 类就是js混合模式的一个syntax candy(语法糖).
当然类也可以使用字面量形式书写(废话,类本来就是函数类型)

const MyClass = class Me {  
  getClassName() {
  return Me.name;  //这里的Me 就和使用函数是一样的道理
  }};
var MyClass = function Me(){
    console.log(Me.name);  //Me
}

关键看你喜欢用哪一种了。
说一说类的继承吧, 这应该是我最喜欢class的原因了.

class的继承

在class中是使用extends来实现继承的效果的. 还有一个特性 super. 这个用来指向父类的实例,如果大家记得应该会了解到上面说的继承模式吧。 而这里的super算是es6新定义的一个东西。

class Point {
  constructor(x, y) {
  this.x = x;
  this.y = y;
   }
  }

class ColorPoint extends Point {
  constructor(x, y, color) {
  this.color = color; // 这里会报错,ignore这句就可以了
  super(x, y);  //这里相当于初始化父类,然后规定class里面this的指向
  this.color = color; // 正确
   }
   show(){
    console.log(this.color);  
    console.log(super.x);
    console.log(this.x);
   }
  }
  var color = new ColorPoint(1,2,"red");
  color.show();
  //依次得到, red , 1 ,1 

我艹,super和this到底应该怎么区分嘞?
很简单,上面已经说了,super是指向父类的实例,而this是在这里是构造函数上原型的指针。 其实由于继承,使用this也可以访问到x; 所以推荐如果你有强迫症的话,父类上的方法和属性可以使用super调用,子类自定的属性和方法使用this调用。 当然我喜欢直接使用this(三包服务)。
还有一点,那我怎么检测我的父类到底是谁嘞(隔壁王叔叔)?
如果你在浏览器的Console 里面,可以直接使用.__proto__来进行检测的(脚本里不行)。在脚本里,js提供了一个方法getPrototypeOf()来进行检测

//上述例子
Object.getPrototypeOf(ColorPoint); //Point
//相当于浏览器里面的
ColorPoint.__proto__;  //Point

关于继承我就扯这么多吧。 虽然说继承很重要,但我们用的地方不是很多,原因是----因为我们不牛逼。。。 大家如果有空可以去翻一翻jquery或者zepto的源码,看一看是不是用到了prototype,继承等相关的技术。而且他们封装性都是超级好的。没有一个直接写在全局里面的。


villainhr
7.8k 声望2.2k 粉丝