Используйте setTimeout вместо setInterval для прерывистых вызовов
var executeTimes = 0;
var intervalTime = 500;
var intervalId = null;
// 放开下面的注释运行setInterval的Demo
intervalId = setInterval(intervalFun,intervalTime);
// 放开下面的注释运行setTimeout的Demo
// setTimeout(timeOutFun,intervalTime);
function intervalFun(){
executeTimes++;
console.log("doIntervalFun——"+executeTimes);
if(executeTimes==5){
clearInterval(intervalId);
}
}
function timeOutFun(){
executeTimes++;
console.log("doTimeOutFun——"+executeTimes);
if(executeTimes<5){
setTimeout(arguments.callee,intervalTime);
}
}
Код относительно прост, мы просто снова вызываем setTimeout в методе setTimeout для достижения цели прерывистого вызова.
Дело в том, почему автор предлагает использовать setTimeout вместо setInterval? В чем разница между прерывистым вызовом в стиле setTimeout и традиционным прерывистым вызовом setInterval?
Разница в том, что прерывистый вызов setInterval начинает отсчет времени до выполнения предыдущего метода, например, если время интервала составляет 500 мс, то последний метод будет помещен в последовательность выполнения независимо от того, был ли выполнен предыдущий метод в то время. В это время будет возникать проблема.Если время выполнения предыдущего метода превышает 500 мс, а добавление составляет 1000 мс, это означает, что после завершения выполнения предыдущего метода последний метод будет выполняться немедленно, потому что прерывистое время в это время превысило 500 мс.
var executeTimes = 0;
var intervalTime = 500;
var intervalId = null;
var oriTime = new Date().getTime();
// 放开下面的注释运行setInterval的Demo
// intervalId = setInterval(intervalFun,intervalTime);
// 放开下面的注释运行setTimeout的Demo
setTimeout(timeOutFun,intervalTime);
function intervalFun(){
executeTimes++;
var nowExecuteTimes = executeTimes;
var timeDiff = new Date().getTime() - oriTime;
console.log("doIntervalFun——"+nowExecuteTimes+", after " + timeDiff + "ms");
var delayParam = 0;
sleep(1000);
console.log("doIntervalFun——"+nowExecuteTimes+" finish !");
if(executeTimes==5){
clearInterval(intervalId);
}
}
function timeOutFun(){
executeTimes++;
var nowExecuteTimes = executeTimes;
var timeDiff = new Date().getTime() - oriTime;
console.log("doTimeOutFun——"+nowExecuteTimes+", after " + timeDiff + "ms");
var delayParam = 0;
sleep(1000);
console.log("doTimeOutFun——"+nowExecuteTimes+" finish !");
if(executeTimes<5){
setTimeout(arguments.callee,intervalTime);
}
}
function sleep(sleepTime){
var start=new Date().getTime();
while(true){
if(new Date().getTime()-start>sleepTime){
break;
}
}
}
(Функция сна, предоставленная Даниэлем, используется здесь для имитации времени работы функции) Выполните демо-метод setInterval и увидите консоль
doIntervalFun——1, after 500ms
VM2854:19 doIntervalFun——1 finish !
VM2854:16 doIntervalFun——2, after 1503ms
VM2854:19 doIntervalFun——2 finish !
VM2854:16 doIntervalFun——3, after 2507ms
VM2854:19 doIntervalFun——3 finish !
VM2854:16 doIntervalFun——4, after 3510ms
VM2854:19 doIntervalFun——4 finish !
VM2854:16 doIntervalFun——5, after 4512ms
VM2854:19 doIntervalFun——5 finish !
Можно обнаружить, что интервал между fun2 и fun1 близок к 1000 мс, что точно соответствует времени выполнения fun1, а это означает, что fun2 выполняется сразу после выполнения fun1, что противоречит первоначальному замыслу нашего прерывистого вызова.
Комментируем демо-метод setInterval, отпускаем демо-метод setTimeout, запускаем и смотрим консоль
doTimeOutFun——1, after 500ms
VM2621:32 doTimeOutFun——1 finish !
VM2621:29 doTimeOutFun——2, after 2001ms
VM2621:32 doTimeOutFun——2 finish !
VM2621:29 doTimeOutFun——3, after 3503ms
VM2621:32 doTimeOutFun——3 finish !
VM2621:29 doTimeOutFun——4, after 5004ms
VM2621:32 doTimeOutFun——4 finish !
VM2621:29 doTimeOutFun——5, after 6505ms
VM2621:32 doTimeOutFun——5 finish !
Это, наконец, нормально, разница между fun1 и fun2 составляет 1500 мс = 1000 + 500, а fun2 выполняется через 500 мс после выполнения fun1.
Закрытие
- Реализовать частные переменные Если мы напишем функцию со значением имени в ней, мы можем позволить любому получить доступ к атрибуту имени, но только небольшое количество людей может изменить атрибут имени, и мы можем использовать замыкания, чтобы написать, какие люди в значении setName имеют разрешение модифицировать.
var person = function(){
//变量作用域为函数内部,外部无法访问,不会与外部变量发生重名冲突
var name = "FE";
return {
//管理私有变量
getName : function(){
return name;
},
setName : function(newName){
name = newName;
}
}
};
- кеш данных Предположим, мы выполняем вычислительно затратную функцию и возвращаем значение, которое также используется в других функциях.В этом случае замыкание может быть использовано для сохранения данных в памяти для использования другими функциями (это я видел в других блогах, специфика не очень понятны, если вам интересно, вы можете самостоятельно ознакомиться с соответствующей литературой).
недостаток: Вызывает слишком много потребления памяти, при неправильном обращении это вызовет утечку памяти.
Разница между forEach и картой в массиве
В большинстве случаев нам приходится просматривать массив, и часто используются два метода: forEach и map. Давайте поговорим о том, что у них общего
- Та же точка перебирают каждый элемент в массиве Методы ForEach и map поддерживают 3 параметра каждый раз, когда выполняется анонимная функция, параметры: item (каждый текущий элемент), index (значение индекса), arr (исходный массив) Это в анонимных функциях все указывает на окно может только пройти через массив не изменит исходный массив
- разница метод карты
1. Метод карты возвращает новый массив, а элементы в массиве — это значения, обработанные вызывающей функцией исходного массива. 2. Метод карты не обнаруживает пустые массивы, а метод карты не изменяет исходный массив. 3. Поддержка браузеров: поддерживаются chrome, Safari1.5+, Opera, IE9+,
array.map(function(item,index,arr){},thisValue)
var arr = [0,2,4,6,8];
var str = arr.map(function(item,index,arr){
console.log(this); //window
console.log("原数组arr:",arr); //注意这里执行5次
return item/2;
},this);
console.log(str);//[0,1,2,3,4]
Если arr — пустой массив, метод карты также возвращает пустой массив.
forEach метод
- Метод forEach используется для вызова каждого элемента массива и передачи элемента функции обратного вызова. 2. forEach не будет вызывать функцию обратного вызова для пустого массива.
Array.forEach(function(item,index,arr){},this)
var arr = [0,2,4,6,8];
var sum = 0;
var str = arr.forEach(function(item,index,arr){
sum += item;
console.log("sum的值为:",sum); //0 2 6 12 20
console.log(this); //window
},this)
console.log(sum);//20
console.log(str); //undefined
Независимо от того, является ли arr пустым массивом или нет, forEach возвращает неопределенное значение. Этот метод просто выполняет каждый элемент массива в качестве аргумента обратного вызова.
Разница между for in и for of
Для обхода массива обычно используется цикл for.ES5 также может использовать forEach.ES5 имеет функцию обхода массива, включая map, filter, some, every, reduce, reduceRight и т. д., но их возвращаемые результаты отличаются. Но если вы используете foreach для обхода массива, вы не можете разорвать цикл, используя break, и вы не можете вернуться к внешней функции, используя return.
Array.prototype.method=function(){
  console.log(this.length);
}
var myArray=[1,2,4,5,6,7]
myArray.name="数组"
for (var index in myArray) {
console.log(myArray[index]);
}
Использование for in также может пройти по массиву, но есть следующие проблемы:
-
Индекс индекса — это строковое число, которое нельзя использовать непосредственно для геометрических операций.
-
Возможно, порядок обхода не соответствует внутреннему порядку фактического массива.
-
Использование for in перебирает все перечисляемые свойства массива, включая прототипы. Например, способ-прототип метода и атрибуты имени верхнего каштана.
Таким образом, for in больше подходит для перебора объектов, не используйте for in для перебора массивов.
Таким образом, в дополнение к использованию цикла for, как правильно обходить массив для достижения наших ожиданий (то есть без обхода метода и имени), цикл for of в ES6 даже лучше.
Array.prototype.method=function(){
  console.log(this.length);
}
var myArray=[1,2,4,5,6,7]
myArray.name="数组";
for (var value of myArray) {
console.log(value);
}
Помните, что for in перебирает индекс (то есть ключ) массива, а for of перебирает значения элементов массива.
for of проходит только элементы в массиве, не включая метод атрибута прототипа массива и имя индекса
Обход объектов обычно используется for in для обхода ключевых имен объектов.
Object.prototype.method=function(){
  console.log(this);
}
var myObject={
  a:1,
  b:2,
  c:3
}
for (var key in myObject) {
console.log(key);
}
for in может перейти к методу метода прототипа myObject.Если вы не хотите проходить метод прототипа и свойства, вы можете судить об этом внутри цикла.hasOwnPropery
метод, чтобы определить, является ли свойство свойством экземпляра объекта
for (var key in myObject) {
  if(myObject.hasOwnProperty(key)){
    console.log(key);
  }
}
То же самое можно сделать через ES5Object.keys(myObject)
Получает массив свойств экземпляра объекта, исключая методы и свойства прототипа.
Object.prototype.method=function(){
  console.log(this);
}
var myObject={
  a:1,
  b:2,
  c:3
}
Object.keys(myObject).forEach(function(key,index){  
console.log(key,myObject[key])
})
Реализовать метод EventEmitter
Ядром EventEmitter является инкапсуляция функций запуска событий и прослушивания событий. Будьте осторожны, когда вы отвечаете на использование сообщения emit в vue. Метод EventEmitter в основном включает в себя методы включения, испускания, один раз и выключения.
class Event {
constructor() {
this.events = Object.create(null);
}
on(name, fn) {
if (!this.events[name]) {
this.events[name] = []
}
this.events[name].push(fn);
return this;
}
emit(name, ...args) {
if (!this.events[name]) {
return this;
}
const fns = this.events[name]
fns.forEach(fn => fn.call(this, ...args))
return this;
}
off(name,fn) {
if (!this.events[name]) {
return this;
}
if (!fn) {
this.events[name] = null
return this
}
const index = this.events[name].indexOf(fn);
this.events[name].splice(index, 1);
return this;
}
once(name,fn) {
const only = () => {
fn.apply(this, arguments);
this.off(name, only);
};
this.on(name, only);
return this;
}
}
Разница между let, var и const
varВо-первых, это проблема области действия, var предназначена не для области на уровне блока, а для области действия функции. Например:
function runTowerExperiment(tower, startTime) {
var t = startTime;
tower.on("tick", function () {
... code that uses t ...
});
... more code ...
}
Это нормально, потому что переменная t доступна в функции обратного вызова, но что, если мы снова назовем переменную t в функции обратного вызова?
function runTowerExperiment(tower, startTime) {
var t = startTime;
tower.on("tick", function () {
... code that uses t ...
if (bowlingBall.altitude() <= 0) {
var t = readTachymeter();
...
}
});
... more code ...
}
Последний перезапишет первый.
Вторая проблема — петли. См. следующий пример:
var messages = ["Meow!", "I'm a talking cat!", "Callbacks are fun!"];
for (var i = 0; i < messages.length; i++) {
setTimeout(function () {
document.write(messages[i]);
},i*1500);
}
Выходной результат: undefined Поскольку после цикла for i устанавливается равным 3, поэтому его значение недоступно.
letДля решения этих проблем ES6 предлагает синтаксис let. let может быть объявлен в {}, if, for, и его использование такое же, как и var, но область действия ограничена уровнем блока. Но разве в javascript нет области блока? Мы поговорим об этом позже. Еще один важный момент заключается в том, что для переменных, определенных с помощью let, не существует продвижения по переменной.
Переменное продвижение Вот краткое упоминание о том, что называется переменным продвижением.
var v='Hello World';
(function(){
alert(v);
var v='I love you';
})()
Вывод приведенного выше кода: undefined.
Почему это так? Это происходит из-за подъема переменных, что означает подъем объявления переменной в начало функции, например:
(function(){
var a='One';
var b='Two';
var c='Three';
})()
На самом деле это:
(function(){
var a,b,c;
a='One';
b='Two';
c='Three';
})()
Итак, наш пример сейчас таков:
var v='Hello World';
(function(){
var v;
alert(v);
v='I love you';
})()
Таким образом, он вернет undefined.
Это также проблема с var, и у нас нет этой проблемы с let. Потому что он сообщит о синтаксической ошибке:
{
console.log( a ); // undefined
console.log( b ); // ReferenceError!
var a;
let b;
}
Давайте посмотрим на область действия let.
function getVal(boo) {
if (boo) {
var val = 'red'
// ...
return val
} else {
// 这里可以访问 val
return null
}
// 这里也可以访问 val
}
И после использования let:
function getVal(boo) {
if (boo) {
let val = 'red'
// ...
return val
} else {
// 这里访问不到 val
return null
}
// 这里也访问不到 val
}
То же самое в цикле for:
function func(arr) {
for (var i = 0; i < arr.length; i++) {
// i ...
}
// 这里访问得到i
}
После использования пусть:
function func(arr) {
for (let i = 0; i < arr.length; i++) {
// i ...
}
// 这里访问不到i
}
То есть пусть работает только внутри фигурных скобок.
constДавайте поговорим о const, const представляет постоянный индекс значения.
const aa = 11;
alert(aa) //11
aa = 22;
alert(aa) //报错
Но значение константы никогда не может быть изменено до сборки мусора, поэтому ее нужно использовать с осторожностью.
Следует также отметить, что, как и в других языках, объявлениям констант должно быть присвоено начальное значение. Даже если нам нужна неопределенная константа, нам нужно объявить:
const a = undefined;
Область действия на уровне блоков Наконец, давайте упомянем область действия на уровне блоков, о которой мы только что говорили.
Раньше в javascript не было области на уровне блоков, мы все использовали () для имитации области на уровне блоков.
(function(){
//这里是块级作用域
})();
Но в ES6 {} может быть непосредственно блочным. Таким образом, содержимое внутри {} не может быть доступно за пределами {}.
Мы можем посмотреть на следующий код:
if (true) {
function foo() {
console.log("1" );
}
}else {
function foo() {
console.log("2" );
}
}
foo(); // 1
В известном нам JavaScript вывод этого кода равен 1. Это называется поднятием объявления функции, и оно поднимает не только имя функции, но и определение функции.
Но в ES6 этот код может выдать ReferenceError. Из-за области действия {} на уровне блоков доступ к foo() извне невозможен, то есть объявления функций ограничены областью действия на уровне блоков, как и переменные определения let.
цикл событий
Начиная с обещания, process.nextTick, setTimeout, разговор об очереди заданий в цикле событий
Краткое введение: рассказ о порядке выполнения promise.resove, setTimeout, setImmediate, process.nextTick в очереди EvenLoop
1. Введение проблемы Цикл событий известен, это означает, что основной поток циклически считывает задачи из «очереди задач», например
пример 1:
setTimeout(function(){console.log(1)},0);
console.log(2)
//输出2,1
В приведенном выше примере мы понимаем, что сначала выполняется задача синхронизации в основном потоке, а когда задача основного потока завершается, задача считывается из цикла событий, поэтому сначала выводится 2, а затем выводится 1.
Порядок, в котором цикл обработки событий считывает задачи, зависит от ограничений различных правил чтения задач в очереди заданий. Возьмем следующий пример:
Пример 2:
setTimeout(function () {
console.log(3);
}, 0);
Promise.resolve().then(function () {
console.log(2);
});
console.log(1);
//输出为 1 2 3
Первый вывод 1, проблем нет, потому что задача синхронизации выполняется первой в основном потоке Проблема здесь в том, как определить приоритет выполнения задач setTimeout и Promise.then.
2. Порядок выполнения в очереди заданий Очереди в Очереди заданий делятся на два типа: макрозадачи и микрозадачи. Возьмем пример, чтобы увидеть положения исполнительного листа, мы установили
очередь макрозадач содержит задачи: a1, a2, a3 очередь микрозадач содержит задачи: b1, b2, b3
Порядок выполнения: сначала выполнить задачу в начале очереди marco-задач, то есть задачу a1.После завершения выполнения выполнить все задачи в очереди микрозадач, то есть выполнить b1, b2 , b3 последовательно, и очистить микрозадачу после выполнения.Задача в задаче, затем выполнить вторую задачу в марко-задаче, и зациклиться по очереди.
Поняв порядок выполнения очередей макрозадач и микрозадач, давайте посмотрим на задачи, фактически содержащиеся в этих двух типах очередей в реальных сценариях (в качестве примера возьмем движок узла V8), в узле V8 реальная задача порядок для этих двух типов следующий:
Очередь макрозадач на самом деле содержит задачи: скрипт (основной программный код), setTimeout, setInterval, setImmediate, ввод-вывод, отрисовка пользовательского интерфейса
Очередь микрозадач на самом деле содержит задачи: process.nextTick, Обещания, Object.observe, MutationObserver
Порядок выполнения, который мы получаем из этого, должен быть таким:
скрипт (основной программный код)—>process.nextTick—>Promises…—>setTimeout—>setInterval—>setImmediate—>I/O—>рендеринг пользовательского интерфейса
В ES6 очередь макрозадач также называется ScriptJobs, а микрозадача также называется PromiseJobs.
3. Пример последовательности выполнения в реальной среде
(1) setTimeout и обещание
Пример 3:
setTimeout(function () {
console.log(3);
}, 0);
Promise.resolve().then(function () {
console.log(2);
});
console.log(1);
Давайте возьмем пример из Раздела 1 в качестве примера, и порядок следования здесь следующий:
script (основной программный код) --> обещание --> setTimeout Соответствующие выходы: 1 ——> 2————> 3 (2) process.nextTick и обещание, setTimeout
Пример 4:
setTimeout(function(){console.log(1)},0);
new Promise(function(resolve,reject){
console.log(2);
resolve();
}).then(function(){console.log(3)
}).then(function(){console.log(4)});
process.nextTick(function(){console.log(5)});
console.log(6);
//输出2,6,5,3,4,1
Этот пример более сложный, здесь следует отметить, что при определении промиса часть построения промиса выполняется синхронно, поэтому проблема легко решается.
Сначала проанализируйте порядок выполнения очереди заданий:
скрипт (основной программный код) ——>process.nextTick——>обещание——>setTimeout
I) Основная часть: Строительная часть, определяющая промис, является синхронной, поэтому сначала выводится 2, а затем основная часть выводится 6 (в случае синхронизации строго в соответствии с заданной последовательностью)
II) process.nextTick: вывод 5
III) обещание: Часть обещания здесь, строго говоря, на самом деле является частью обещания.затем, а вывод равен 3,4
IV) setTimeout: последний вывод 1
Полный порядок выполнения: 2—>6—>5—>3—>4—>1
(3) Более сложные примеры
setTimeout(function(){console.log(1)},0);
new Promise(function(resolve,reject){
console.log(2);
setTimeout(function(){resolve()},0)
}).then(function(){console.log(3)
}).then(function(){console.log(4)});
process.nextTick(function(){console.log(5)});
console.log(6);
//输出的是 2 6 5 1 3 4
Разница между этим случаем и нашим примером в (2) заключается в том, что при построении обещания нет синхронного разрешения, поэтому promise.then не существует в текущей очереди выполнения, только когда обещание передается из ожидающего в разрешение. метод, и это разрешение завершается за время setTimout, поэтому в итоге выводятся 3,4.
тип и экземпляр
ECMAScript является слабо типизированным и нуждается в средствах одновременного определения типа данных данной переменной, а оператор typeof (не функция!) отвечает за предоставление этой информации.
typeof может использоваться для обнаружения примитивных и ссылочных типов данных.
Формат синтаксиса следующий:
typeof variable
Возвращает 6 строковых результатов:
- "undefined" - если значение не определено
- "boolean" - если значение является логическим
- "строка" - если значение является строкой
- "число" - если значение числовое
- "object" - если это значение является объектом или нулем
- "функция" - если значение является функцией Пример:
console.log(typeof 'hello'); // "string"
console.log(typeof null); // "object"
console.log(typeof (new Object())); // "object"
console.log(typeof(function(){})); // "function"
typeof в основном используется для обнаружения основных типов данных: числовых, строковых, логических, undefined, потому что, когда typeof используется для обнаружения значений ссылочного типа, для любого экземпляра объекта Object (включая null) typeof возвращает значение «объект», нет никакого способа различать — это тип объекта, который не очень полезен для фактического кодирования.
instanceof используется для определения того, является ли переменная экземпляром объекта.
typeof — очень мощный помощник при обнаружении базовых типов данных, но при обнаружении значения ссылочного типа этот оператор не очень полезен, обычно мы не хотим знать, что значение является объектом, а хотим знать, что это такое. это тип объекта. На этом этапе мы можем использовать оператор instanceof, предоставляемый ECMAScript.
Формат синтаксиса следующий:
result = variable instanceof constructor
Возвращает логическое значение:
- true — оператор instanceof возвращает true, если переменная является экземпляром данного ссылочного типа.
- false — оператор instanceof возвращает false, если переменная не является экземпляром данного ссылочного типа. Пример:
function Person(){}
function Animal(){}
var person1 = new Person();
var animal1 = new Animal();
console.log(person1 instanceof Person); // true
console.log(animal1 instanceof Person); // false
console.log(animal1 instanceof Object); // true
console.log(1 instanceof Person); //false
var oStr = new String("hello world");
console.log(typeof(oStr)); // object
console.log(oStr instanceof String);
console.log(oStr instanceof Object);
// 判断 foo 是否是 Foo 类的实例
function Foo(){}
var foo = new Foo();
console.log(foo instanceof Foo);
// instanceof 在继承中关系中的用法
console.log('instanceof 在继承中关系中的用法');
function Aoo(){}
function Foo(){}
Foo.prototype = new Aoo();
var fo = new Foo();
console.log(fo instanceof Foo);
console.log(fo instanceof Aoo)
По соглашению все значения ссылочного типа являются экземплярами Object. Поэтому оператор instanceof всегда будет возвращать значение true при тестировании значения ссылочного типа и конструктора объекта. Если оператор instanceof используется для обнаружения примитивных значений, он всегда будет возвращать false, поскольку примитивы не являются объектами.
console.log(Object.prototype.toString.call(null));
// [object Null]
undefined
console.log(Object.prototype.toString.call([1,2,3]));
//[object Array]
undefined
console.log(Object.prototype.toString.call({}));
// [object Object]
Несколько методов общего наследования
Наследование цепочки прототипов
Определение Используйте прототип, чтобы позволить одному ссылочному типу наследовать свойства и методы другого ссылочного типа. код
function SuperType(){
this.property = 'true';
}
SuperType.prototype.getSuperValue = function(){
return this.property;
}
function SubType(){
this.subProperty = 'false';
}
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function(){
return this.subProperty;
}
var instance = new SubType();
alert(instance.getSuperValue());//true
- Преимущества Просто и понятно, легко реализовать, добавить свойства и методы прототипа в родительский класс, и подкласс может получить к нему доступ.
- Недостатки Для функций, содержащих значения ссылочного типа, все экземпляры указывают на один и тот же ссылочный адрес.Измените один, и другие изменятся. Вы не можете передавать параметры, такие как конструктор супертипа
Наследование конструктораОпределение вызывает конструктор супертипа внутри конструктора подтипа
код
function SuperType(){
this.colors = ['red','yellow'];
}
function SubType(){
SuperType.call(this);
}
var instance1 = new SubType();
instance1.colors.push('black');
var instance2 = new SubType();
instance2.colors.push('white');
alert(instance1.colors);//'red','yellow','black'
alert(instance2.colors);//'red','yellow','white'
- Преимущества Простой и понятный, напрямую наследующий свойства и методы конструктора супертипа.
- Недостатки Все методы определены в конструкторе, поэтому повторное использование функций невозможно, а свойства и методы прототипа в супертипе не видны подтипу, в результате все типы могут использовать только режим конструктора.
наследование композиции
Определение Используйте цепочку прототипов для реализации наследования свойств и методов с несколькими прототипами и используйте конструктор для реализации наследования экземпляров.
код
function SuperType(name){
this.name = name;
this.colors = ['red','black'];
}
SuperType.prototype.sayName = function()
{
alert(this.name);
}
function SubType(name,age){
SuperType.call(this,name);
this.age = age;
}
SubType.protptype = new SuperType();
SubType.protptype.sayAge = function(){
alert(this.age);
}
- Преимущества Решение двух проблем в конструкторах и прототипном наследовании
- Недостаток Конструктор супертипа вызывается дважды всякий раз, когда