当前位置

网站首页> 程序设计 > 开源项目 > 程序开发 > 浏览文章

js痛点之继承 - 前端的那些事

作者:小梦 来源: 网络 时间: 2024-07-16 阅读:

继承

今天给大家科普一个叫做继承的大哥。之所以叫做大哥,是因为他确实是所以语言都必须具备的。而且这也是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,继承等相关的技术。而且他们封装性都是超级好的。没有一个直接写在全局里面的。

热点阅读

网友最爱