第一种继承方式
这种方式有个缺陷就是不能给父类传参数。
function Animal () {
this.bigName = 'animal'
}
Animal.prototype.eat = function () {
console.log('eat')
}
function Bear (name) {
Animal.call(this, name)
this.name = name
}
Bear.prototype = new Animal()
Bear.prototype.constructor = Bear
Bear.prototype.say = function () {
console.log('say')
}
let bear = new Bear('small Bear')
Object.create
- Object.create和Object.setPrototypeOf的区别?
1.Object.create的用法
// 第一个参数为原型链上的属性
// 第二个属性为实例上的属性
var obj = Object.create({protytypeProp: 666}, {
p1: {
value: 123,
enumerable: true,
configurable: true,
writable: true,
},
p2: {
value: 'abc',
enumerable: true,
configurable: true,
writable: true,
}
});
Object.create实现
function create (prototype) {
const Noop = function () {}
Noop.prototype = prototype
return new Noop()
}
Object.create在继承中的应用
function Animal () {
this.bigName = 'animal'
}
Animal.prototype.eat = function () {
console.log('eat')
}
function Bear (name) {
Animal.call(this, name)
this.name = name
}
Bear.prototype = Object.create(Animal.prototype, {
constructor: {
value: Bear
}
})
Bear.prototype.say = function () {
console.log('say')
}
let bear = new Bear('small Bear')
Object.setPrototypeOf
1.Object.setPropertyOf的用法
// .Object.setPrototypeOf将一个对象设置为另一个对象的原型对象。
var a = {};
var b = {x: 1};
Object.setPrototypeOf(a, b);
Object.getPrototypeOf(a) === b // true
a.x // 1
Object.setPrototypeOf的实现
// 仅适用于Chrome和FireFox,在IE中不工作:
Object.setPrototypeOf = Object.setPrototypeOf || function (obj, proto) {
obj.__proto__ = proto;
return obj;
}
Object.setPrototypeOf在继承中的应用
function Animal () {
this.bigName = 'animal'
}
Animal.prototype.eat = function () {
console.log('eat')
}
function Bear (name) {
Animal.call(this, name)
this.name = name
}
Object.setPrototypeOf(Bear.prototype, Animal.prototype)
Bear.prototype.say = function () {
console.log('say')
}
let bear = new Bear('small Bear')
ES6的继承
子类必须在constructor
方法中调用super
方法,否则新建实例时会报错。如果子类不写constructor
方法,内部会自动补充一个空的构造函数。
class Parent {}
class Child extends Parent {
}
let c = new Child()
Child.prototype.__proto__ === Parent.prototype
ES6和ES5的区别
ES5先是在构造函数中得到了
this
对象,再调用Parent.call(this, ...args)
将父类的属性添加到this
上。ES6得先调用super
得到this
指向,再将自己的属性附加到this
上。ES6 可以继承原生数据结构,而ES5不可以,原因是原生数据类型会忽略传入的this
function myArray () {
Array.call(this)
}
let arr = new myArray()
function myArray () {
Array.call(this)
}
myArray.prototype = Object.create(Array.prototype, {
constructor: {
value: myArray,
writeable: true,
enumerable: true,
writeable: true
}
})
let arr = new myArray()
arr[0] = 1
arr.length === 0 //true
注意点
- 因为ES6继承的机制(上面描述的),所以需要先调用
super
,再使用this,否则会报错
class Parent {}
class Child extends Parent {
constructor (name) {
this.name = name
super()
this.name = name
}
}
new Child() // Uncaught ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
- 继承后的关系为什么要这样设计呢?
class P1 {}
class P2 extends P1 {}
class P3 extends P2 {}
Object.getPrototypeOf(P3) === P2 // true
P3.__proto__ === P2 // true
P2.__proto__ === P1 // true
P3.prototype.__proto__ === P2.prototype // true
P2.prototype.__proto__ === P1.prototype // true
// Object.getPrototypeOf模拟
function getPrototypeOf (obj) {
return obj.__proto__
}
因为同时要继承静态属性
class A {
}
class B {
}
// B 的实例继承 A 的实例
Object.setPrototypeOf(B.prototype, A.prototype);
// B 继承 A 的静态属性
Object.setPrototypeOf(B, A);
const b = new B();
-
super
作为方法只能用在子类的构造函数中调用,用在其他地方就会报错 -
super
还能作为对象使用,构造函数或者普通函数都可以。这时候super
指的就是Parent.prototype
-
super
作为对象还能在静态方法中调用,这时候super指向的就是父类自身,而不是Parent.prototype
- 看一个
babel
转义后的例子,里面的继承方式其实很简单。
代码
class P {
constructor () {
this.x =1
}
aaa () {
console.log('aaa')
}
}
P.x = 1
class C extends P {
}
let v = new C ()
编译后
"use strict";
function _typeof(obj) {
if (
typeof Symbol === "function" &&
typeof Symbol.iterator === "symbol"
) {
_typeof = function _typeof(obj) {
return typeof obj;
};
} else {
_typeof = function _typeof(obj) {
return obj &&
typeof Symbol === "function" &&
obj.constructor === Symbol &&
obj !== Symbol.prototype
? "symbol"
: typeof obj;
};
}
return _typeof(obj);
}
function _possibleConstructorReturn(self, call) {
if (
call &&
(_typeof(call) === "object" || typeof call === "function")
) {
return call;
}
return _assertThisInitialized(self);
}
function _assertThisInitialized(self) {
if (self === void 0) {
throw new ReferenceError(
"this hasn't been initialised - super() hasn't been called"
);
}
return self;
}
function _getPrototypeOf(o) {
_getPrototypeOf = Object.setPrototypeOf
? Object.getPrototypeOf
: function _getPrototypeOf(o) {
return o.__proto__ || Object.getPrototypeOf(o);
};
return _getPrototypeOf(o);
}
function _inherits(subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError(
"Super expression must either be null or a function"
);
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: { value: subClass, writable: true, configurable: true }
});
// 注意,这里用于继承父类的静态属性
if (superClass) _setPrototypeOf(subClass, superClass);
}
function _setPrototypeOf(o, p) {
_setPrototypeOf =
Object.setPrototypeOf ||
function _setPrototypeOf(o, p) {
o.__proto__ = p;
return o;
};
return _setPrototypeOf(o, p);
}
function _instanceof(left, right) {
if (
right != null &&
typeof Symbol !== "undefined" &&
right[Symbol.hasInstance]
) {
return right[Symbol.hasInstance](left);
} else {
return left instanceof right;
}
}
function _classCallCheck(instance, Constructor) {
if (!_instanceof(instance, Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
var P =
/*#__PURE__*/
(function() {
function P() {
_classCallCheck(this, P);
this.x = 1;
}
_createClass(P, [
{
key: "aaa",
value: function aaa() {
console.log("aaa");
}
}
]);
return P;
})();
P.x = 2;
var C =
/*#__PURE__*/
(function(_P) {
_inherits(C, _P);
function C() {
_classCallCheck(this, C);
return _possibleConstructorReturn(
this,
_getPrototypeOf(C).apply(this, arguments) // 在这里继承实例属性,注意传进去的是C,所以_getPrototypeOf得到的是P
);
}
return C;
})(P);
var v = new C();
原型链
怎么理解Function.prototype === Function.proto === Object.proto?
- Function.prototype是个函数!任何函数的
__proto__
都是Function.prototype - Object也是个构造函数,所以Object.proto指向Function.prototype
怎么理解Object.prototype.proto === null
只是表示这个对象没有原型对象而已,是原型链的终点而已,没啥其他的。
解决任何原型链问题的三句话:
当 new 一个函数的时候会创建一个对象,『函数
.prototype
』 等于 『被创建对象.__proto__
』一切函数都是由 Function 这个函数创建的,所以
Function.prototype
=== 『被创建的函数.__proto__
』一切函数的原型对象都是由 Object 这个函数创建的,所以
Object.prototype
=== 『一切函数.prototype.__proto__
』
几个点
- 要注意继承实例属性、静态属性
- 要继承父类的实例属性,都是靠在子类中
call
或apply
调用实现。 - 这些继承方式都可以直接通过修改proto来修改父类的方法,不是很安全