JavaScript по умолчанию использует прототипное наследование. Хотя понятия класса не существует, его функция может выступать в роли конструктора. Конструктор в сочетании с этим и новым может создать Java-подобный класс. Таким образом, JavaScript может эмулировать наследование на основе классов, расширяя себя.
JavaScript, как и другие объектно-ориентированные языки, использует ссылки на типы объектов. Переменная, содержащая объект, представляет собой просто адрес, а данные базового типа — это значение. При хранении объектов на прототипах могут возникнуть подводные камни.
Давайте сначала рассмотрим первый пример
Скопируйте код кода следующим образом:
вар создать = функция() {
функция Фн() {}
возвращаемая функция (родительский) {
Fn.prototype = родительский
вернуть новый Fn
}
}()
вар родительский = {
имя: 'Джек',
возраст: 30,
женат: ложь
}
вар дочерний = создать (родительский)
console.log(дочерний)
Функция инструмента создания реализует базовое наследование прототипа. При каждом вызове метода create новый объект копируется на основе родительского объекта. Все свойства нового объекта берутся из родительского объекта. Здесь родительский элемент имеет три атрибута, каждый из которых является базовым типом данных: строка, число и логическое значение.
Теперь измените дочерний элемент, чтобы увидеть, повлияет ли это на родителя.
Скопируйте код кода следующим образом:
child.name = 'лилия'
ребенок.возраст = 20,
child.isMarried = правда
console.log(дочерний)
console.log(родительский)
Результаты следующие:
То есть изменение дочернего элемента не повлияет на родительского.
Давайте посмотрим на другой пример
Скопируйте код кода следующим образом:
вар создать = функция() {
функция Фн() {}
возвращаемая функция (родительский) {
Fn.prototype = родительский
вернуть новый Fn
}
}()
вар родительский = {
данные: {
имя: 'Джек',
возраст: 30,
женат: ложь
},
язык: ['Ява']
}
вар дочерний = создать (родительский)
child.data.name = 'лилия'
child.data.age = 20
child.data.isMarried = true
child.language.push('javascript')
console.dir(дочерний)
console.dir(родительский)
Обратите внимание, что здесь два атрибута родителя, данные и язык, являются ссылочными типами: один является объектом, а другой — массивом. Дочерний элемент по-прежнему наследует от родителя, а затем дочерний элемент изменяется. Результаты следующие.
Как видите, родительский элемент в это время также был изменен, а имя, возраст и т. д. дочернего элемента остались прежними. Об этом следует помнить при использовании прототипного наследования.
Лучший способ использовать наследование:
1. Атрибуты данных принимают наследование классов (зависит от этого), так что их также можно настроить с помощью параметров, когда они новые.
2. Метод использует прототипное наследование, что позволяет экономить память. В то же время переопределение методов подклассами не повлияет на родительский класс.
Ниже приведена функция инструмента записи, которая удовлетворяет двум вышеуказанным пунктам.
Скопируйте код кода следующим образом:
/**
* @param {String} имя класса
* @param {String/Function} superCls
* Фабрика @param {Function}
*/
функция $class(имя, суперкласс, фабрика) {
if (суперкласс === '') суперкласс = объект
функцияclazz() {
if (typeof this.init === 'функция') {
this.init.apply(это, аргументы)
}
}
var p = clazz.prototype = новые суперклипы
clazz.prototype.constructor = clazz
clazz.prototype.className = имя класса
вар супр = superCls.prototype
окно[имя класса] = clazz
Factory.call(p, выше)
}
Размещая тип объекта в прототипе родительского класса, будьте осторожны, когда подклассы изменяют его. В этом случае все экземпляры подклассов, наследуемых от родительского класса, будут изменены. И баги, вызванные этим, найти очень сложно.
В ES5 был добавлен новый API для реализации прототипного наследования: Object.create. Вы можете использовать его для замены самостоятельно реализованной функции создания, описанной выше, следующим образом:
Скопируйте код кода следующим образом:
вар родительский = {
имя: 'Джек',
возраст: 30,
женат: ложь
}
вар дочерний = Object.create(родительский)
console.log(дочерний)