面试:关于js 中 class 类的继承问题

112 阅读2分钟

问题

  • 在继承时,为什么需要调用 super 方法?
  • B 类继承 A 类后,可以访问到A类中的私有方法和属性吗?
  • A 类中在属性上添加方法和直接添加方法有什么区别?

先上代码

class A {
  constructor() {
    this.a1 = 'a1';
  }
  private a1: string;
  private a2 = 123
  private a3 = function() {
      console.log('a3');
  }
  private a4() {
      console.log('a4');
  }

  a5 = function() {
      console.log('a5');
    }
  a6() {
      console.log('a6');
    }
  a7 = 'a7'
  a8: string;

  static a8 = '123';
  static a9 = function() {
  	console.log('a9');
  }
  static a10() {
  	console.log('a10');
  }
  static a11: string;
}
class B extends A {
  constructor() {
    super();
    console.log(this);
  }
  private b1 = function() {
    console.log('b1')
  }
  b2 = function() {
  	console.log('b2');
  }
  private b3() {
    console.log('b3');
  }
  b4() {
    console.log('b4');
  }
  static b5() {
  	console.log('b5');
  }
}

问题一

我们先分别来看下有写super()和没有写super()语句,经过babel编译后对应的es5代码是什么 没有super()时

function B() {
    var _this;
    console.log(_assertThisInitialized(_this));
    return _assertThisInitialized(_this);
 }

有super()时

function B() {
    var _this;

    _this = _A.call(this) || this;
    
    ..省略..

    console.log(_assertThisInitialized(_this));
    return _this;
}

可以看到,如果

  • 执行了super(), 在实例化B类时,会使用函数B的this对象去执行A函数, 从而可以在B函数中访问到A函数中绑定到this对象上的属性,从而实现this的继承。
  • 如果没有执行 super(),在B函数内部 this 对象为undefined,并且无法从访问A函数中绑定到this对象上的属性;

问题二

从下面代码中可以很明显的看到,经过babel编译后在B类中可以访问到A类的私有属性和方法

问题三

A 类中使用属性添加方法和直接添加方法,如:A类中的 a3 方法和 a4 方法经过babel编译后

// a3 方法添加到了 A 函数的this对象上
_defineProperty(this, "a3", function () {
      console.log("a3");
});
// a4 方法添加到了 A 函数的 prototype 对象上
var _proto = A.prototype;
_proto.a4 = function a4() {
    console.log("a4");
};

这样会导致我们可以直接通过 A.prototype.a4 访问 a4方法,但是无法通过这样的方式访问到a3方法

如果在A类的属性或方法前添加 static 关键字,那么该属性和方法将会直接添加在A函数对象上,这些属性也无法被继承

使用babel编译后的完整代码如下:

function _assertThisInitialized(self) {
  if (self === void 0) {
    throw new ReferenceError(
      "this hasn't been initialised - super() hasn't been called"
    );
  }
  return self;
}
function _inheritsLoose(subClass, superClass) {
  subClass.prototype = Object.create(superClass.prototype);
  subClass.prototype.constructor = subClass;
  _setPrototypeOf(subClass, superClass);
}
function _setPrototypeOf(o, p) {
  _setPrototypeOf = Object.setPrototypeOf
    ? Object.setPrototypeOf.bind()
    : function _setPrototypeOf(o, p) {
        o.__proto__ = p;
        return o;
      };
  return _setPrototypeOf(o, p);
}
function _defineProperty(obj, key, value) {
  if (key in obj) {
    Object.defineProperty(obj, key, {
      value: value,
      enumerable: true,
      configurable: true,
      writable: true
    });
  } else {
    obj[key] = value;
  }
  return obj;
}

var A = /*#__PURE__*/ (function () {
  function A() {
    _defineProperty(this, "a2", 123);
    _defineProperty(this, "a3", function () {
      console.log("a3");
    });
    _defineProperty(this, "a5", function () {
      console.log("a5");
    });
    _defineProperty(this, "a7", "a7");
    this.a1 = "a1";
  }
  var _proto = A.prototype;
  _proto.a4 = function a4() {
    console.log("a4");
  };
  _proto.a6 = function a6() {
    console.log("a6");
  };
  A.a10 = function a10() {
    console.log("a10");
  };
  return A;
})();
_defineProperty(A, "a8", "123");
_defineProperty(A, "a9", function () {
  console.log("a9");
});
var B = /*#__PURE__*/ (function (_A) {
  _inheritsLoose(B, _A);
  function B() {
    var _this;
    _this = _A.call(this) || this;
    _defineProperty(_assertThisInitialized(_this), "b1", function () {
      console.log("b1");
    });
    _defineProperty(_assertThisInitialized(_this), "b2", function () {
      console.log("b2");
    });
    console.log(_assertThisInitialized(_this));
    return _this;
  }
  var _proto2 = B.prototype;
  _proto2.b3 = function b3() {
    console.log("b3");
  };
  _proto2.b4 = function b4() {
    console.log("b4");
  };
  B.b5 = function b5() {
    console.log("b5");
  };
  return B;
})(A);