什么是面向对象?JavaScript是面向对象语言吗?

面向对象语言都有一个标志就是类的概念,通过类可以创建多个对象具有相同方法和属性。在JavaScript中没有类的概念,因此他的继承和面向对象语言不太一样,是基于原型对象的一种继承方案。

原型链继承

利用原型让一个引用类型继承另外一个引用类型的方法和属性,通过让待继承的引用类型的原型对象指向另一个类型的实例。
存在的问题:

  • 引用值共享问题
  • 创建子类时,无法向其超类进行参数传递
  • 不能用对象字面量语法向原型中添加属性(改变原型指针,会切断原型链)

    1. function Child() {
    2. this.name = ['1','2']
    3. }
    4. function Son () {}
    5. Son.prototype = new Child();
    6. var son1 = new Son();// 无法向父类传递参数
    7. var son2 = new Son()
    8. son1.name.push('3')
    9. //son2.name 也会跟着改变

    借用构造函数继承

    子类的构造函数内部调用父类的构造函数,能够解决传递参数的问题。

  • 不存在原型继承后,函数无法复用

  • 超类原型中的方法无法被继承,所有类型只能使用构造函数中的方法

    1. function Parent() {
    2. this.name = [‘foo’,’bar’];
    3. this.funcLog = function () {
    4. console.log(‘能够使用’);
    5. }
    6. }
    7. //想要继承自Father,通过借用构造函数形式,将this绑定到
    8. function Child(...args) {
    9. Father.apply(this,args)
    10. }

    组合继承

    使用原型式继承实现对原型中属性和方法的继承,使用构造函数实现对实例属性的继承。
    将原型链继承和借用构造函数融为一体,能够实现向父类传递参数并且避免了引用值的问题,是最常用的一种继承方案。

    1. function Parent(name) {
    2. this.name = [‘foo’,’bar’];
    3. }
    4. Parent.prototype.func = function () {
    5. console.log(‘继承方法成功’);
    6. }
    7. function Child(…args) {
    8. Parent.call(this,args)
    9. }
    10. Child.prototype = new Parent();
    11. // 由于原型对象被重写,需要将constructor指针归位
    12. Child.prototype.constructor = Child;

    原型式继承

    借助原型式继承可以对已有的对象创建一个新对象(继承自已有的对象),这种实现继承的方法不需要创建构造函数。
    前提是你必须有一个对象作为另外一个对象的继承基础,ES5对该模式封装为:Object.create(对象,属性描述符)
    问题:

  • 和原型链继承一样的问题,对引用值的共享

    1. function Parent(name) {
    2. this.name = [‘foo’,’bar’];
    3. }
    4. Parent.prototype.func = function () {
    5. console.log(‘继承成功’);
    6. }
    7. function myCreate (o) {
    8. // 中转函数
    9. function F () {}
    10. F.prototype = o;
    11. return new F();
    12. }

    寄生组合继承(圣杯继承)

    最完美的一种继承方案,需要注意的是重写原型后,需要将constructor 进行归位操作。
    问题:

  • 调用两次超类构造函数

    1. function _extend (sub,super) {
    2. var _prototype = _create(super);
    3. _prototype.constructor = sub;
    4. sub.prototype = _prototype;
    5. function _create(super) {
    6. function F () {}
    7. F.prototype = super.prototype;
    8. return new F ()
    9. }
    10. }

    ES6继承
    一个类可以去继承其他类里面的东西,这里定义一个叫Person的类,然后在constructor里面添加两个参数:name和birthday;
    下面再添加一个自定义的方法intro,这个方法就是简单地返回this.name和this.birthday;

    1. class Person{
    2.   constructor(name,birthday){
    3.     this.name = name;
    4.     this.birthday= birthday;
    5.   }
    6.   intro(){
    7.     return '${this.name},${this.birthday}';
    8.   }
    9. }

    然后再定一个Chef类,使用extends去继承Person这个类,如果这个类里面有constructor方法,就要在constructor方法里面使用super,它可以去调用父类里面的东西 ```javascript class Chef extends Person{   constructor(name,birthday){     super(name,birthday);   } }

let zhangsan = new Chef(‘zhangsan’,’1988-04-01’); console.log(zhangsan.intro()); //zhangsan,1988-04-01 ``` 因为Chef这个类继承了Person类,所以在Person类里面定义的方法可以直接使用