Это вторая статья из серии о повторном изучении JS.Первоначальная цель написания этой серии — закрепить мою основу JS. Поскольку это повторное обучение, я определенно не буду вводить точку знаний с нуля.Если вы столкнетесь с каким-либо контентом, который вы не можете понять, пожалуйста, найдите информацию самостоятельно.
прототип
Наследование достигается с помощью прототипов. Конечно, прототипы не в центре внимания этой статьи. Давайте рассмотрим их.
На самом деле концепция прототипа очень проста:
- Все объекты имеют свойство
__proto__
Указатель на объект, прототип - Доступ к прототипу каждого объекта можно получить через
constructor
Найдите конструктор, конструктор тоже можно передатьprototype
Найден прототип - Все функции доступны через
__proto__
оказатьсяFunction
объект - Все объекты могут пройти
__proto__
оказатьсяObject
объект - переход между объектами
__proto__
Соединённые вместе, это называется цепочкой прототипов. Свойства, которых нет в текущем объекте, можно искать слой за слоем по цепочке прототипов до верхнего уровня.Object
объект
На самом деле, это самое главное в прототипе, совершенно незачем читать эти длинные статьи о том, что такое прототип, новички будут все больше и больше путаться.
Конечно, если вы хотите узнать больше о подробном содержании прототипов, вы можете прочитать меня.ранее написанные статьи.
ES5 реализует наследование
В общем, есть два способа реализовать наследование в ES5.Я уже писал этот контент раньше, поэтому просто скопировал его и использовал.
В целом, я думаю, что эта часть контента больше касается интервью на данный момент.
наследование композиции
Наследование композиции является наиболее часто используемым методом наследования.
function Parent(value) {
this.val = value
}
Parent.prototype.getValue = function() {
console.log(this.val)
}
function Child(value) {
Parent.call(this, value)
}
Child.prototype = new Parent()
const child = new Child(1)
child.getValue() // 1
child instanceof Parent // true
Суть вышеуказанного метода наследования заключается в передаче конструктора подклассаParent.call(this)
Наследовать свойства родительского класса, а затем изменить прототип дочернего класса наnew Parent()
наследовать функции родительского класса.
Преимущество этого метода наследования в том, что конструктор может передавать параметры, он не будет делиться с родительским классом ссылочным свойством, и функции родительского класса можно использовать повторно, но есть и недостаток в том, что конструктор родительского класса вызывается при наследовании функции родительского класса, в результате чего прототип подкласса имеет больше ненужных свойств родительского класса, что является пустой тратой памяти.
Наследование паразитарного состава
Этот метод наследования оптимизирует комбинированное наследование.Недостаток комбинированного наследования в том, что конструктор вызывается при наследовании функции родительского класса.Нам нужно оптимизировать только этот момент.
function Parent(value) {
this.val = value
}
Parent.prototype.getValue = function() {
console.log(this.val)
}
function Child(value) {
Parent.call(this, value)
}
Child.prototype = Object.create(Parent.prototype, {
constructor: {
value: Child,
enumerable: false,
writable: true,
configurable: true
}
})
const child = new Child(1)
child.getValue() // 1
child instanceof Parent // true
Суть приведенной выше реализации наследования состоит в том, чтобы присвоить прототип родительского класса подклассу и установить конструктор в подкласс, что не только решает проблему бесполезных атрибутов родительского класса, но и корректно находит конструктор подкласса.
Как Babel компилирует классы ES6
Почему в предыдущей статье было сказано, что наследование реализации ES5 больше касается интервью, потому что мы можем использовать его прямо сейчасclass
реализовать наследование.
ноclass
В конце концов, это что-то из ES6.Чтобы быть более совместимым с браузерами, мы обычно компилируем код ES6 через Babel. Далее давайте посмотрим, как выглядит код, скомпилированный Babel.
function _possibleConstructorReturn (self, call) {
// ...
return call && (typeof call === 'object' || typeof call === 'function') ? call : self;
}
function _inherits (subClass, superClass) {
// ...
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
enumerable: false,
writable: true,
configurable: true
}
});
if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}
var Parent = function Parent () {
// 验证是否是 Parent 构造出来的 this
_classCallCheck(this, Parent);
};
var Child = (function (_Parent) {
_inherits(Child, _Parent);
function Child () {
_classCallCheck(this, Child);
return _possibleConstructorReturn(this, (Child.__proto__ || Object.getPrototypeOf(Child)).apply(this, arguments));
}
return Child;
}(Parent));
Вышеприведенный код является частью кода, скрыт некоторый неосновной код, давайте прочитаем_inherits
функция.
Код для установки прототипной части подкласса на самом деле точно такой же, как и для паразитного наследования композиции, и сбоку также видно, что этот метод реализации является лучшим. Но в этой части кода есть еще одно предложениеObject.setPrototypeOf(subClass, superClass)
, на самом деле функция этого кодаЧтобы унаследовать статический метод родительского класса, два метода наследования, которые мы реализовали ранее, не имеют этой функции.
потомChild
Код конструктора в основном аналогичен предыдущей реализации. Таким образом, способ реализации наследования в Babel по-прежнему представляет собой паразитическое комбинированное наследование.
Проблемы с наследством
Поговорив о том, как реализовать наследование, теперь давайте рассмотрим, является ли наследование хорошим выбором?
Вообще лично я не очень люблю наследование, и вот тому причины.
Давайте сначала посмотрим на код. Если мы теперь хотим описать несколько различных марок автомобилей, автомобиль должен быть родительским классом, а затем каждая марка автомобиля является подклассом.
class Car {
constructor (brand) {
this.brand = brand
}
wheel () {
return '4 个轮子'
}
drvie () {
return '车可以开驾驶'
}
addOil () {
return '车可以加油'
}
}
Class OtherCar extends Car {}
Эта часть кода на данный момент выглядит нормально и реализует несколько основных функций автомобиля, а также мы можем расширять различные автомобили через подклассы.
Но теперь есть транспортные средства на новой энергии, и транспортные средства на новой энергии не нуждаются в дозаправке. Конечно, кроме функции заправки, еще необходимы основные функции нескольких других автомобилей.
Если новое энергетическое транспортное средство напрямую наследует родительский класс автомобилей, возникает первая проблема — проблема горилл и бананов. Смысл этого вопроса в том, что нам сейчас нужен только банан, но мы получаем гориллу, держащую банан, на самом деле горилла нам не нужна, но родительский класс вынужден отдать ее дочернему классу. Хотя наследование может переопределить методы родительского класса, оно не выбирает, что наследовать.
Кроме того, одному родительскому классу сложно описать все сценарии, что приводит к необходимости добавления нескольких разных родительских классов для описания большего количества сценариев. При непрерывном расширении код обязательно будет дублироваться, что также является одной из проблем наследования.
В дополнение к двум вышеупомянутым проблемам, наследование также имеет сильную связь, несмотря ни на что, подкласс будет связан со своим родительским классом.
Поскольку существует сильная связь, архитектура должна быть хрупкой. Как только возникнет проблема с дизайном нашего родительского класса, это сильно повлияет на сопровождение. Поскольку все подклассы связаны с родительским классом, изменение чего-либо в родительском классе может привести к необходимости изменения всех подклассов.
Как решить проблему наследства
Наследование больше связано с описанием того, чем является вещь. Если описание будет нехорошим, возникнут всевозможные проблемы. Итак, есть ли у нас способ решить эти проблемы? Ответ - комбинация.
Что такое комбинация? Вы можете думать об этой концепции как о том, что у вас есть все виды частей, вы можете делать все виды продуктов из этих частей, и комбинация больше описывает, что может делать вещь.
Теперь реализуем корпус предыдущей машины, объединив ее.
function wheel() {
return "4 个轮子";
}
function drvie() {
return "车可以开驾驶";
}
function addOil() {
return "车可以加油";
}
// 油车
const car = compose(wheel, drvie, addOil)
// 新能源车
const energyCar = compose(wheel, drive)
Из приведенного выше псевдокода вы, должно быть, поняли, что композиция лучше наследования. Все, что вы хотите описать, вы можете сделать это, объединив несколько функций. Код чистый и простой для повторного использования.
наконец
Фактически, основной целью этой статьи является содержание следующих двух подразделов.Если у вас есть какие-либо вопросы, пожалуйста, не стесняйтесь общаться со мной в области комментариев.
Все мои циклы статей будут в моемGithubОн обновляется первым, и желающие могут обратить на него внимание. В этом году я в основном перепишу следующие три столбца.
- Переучи JS
- Реагировать продвинуто
- Переопределить компонент
Наконец, если вы считаете, что контент полезен, вы можете обратить внимание на мой официальный аккаунт «Внешняя часть действительно забавная», вас ждет много хорошего.