Промежуточные и продвинутые вопросы по фронтенд-разработке, часто задаваемые на интервью

интервью

Используйте 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.

Закрытие

  1. Реализовать частные переменные Если мы напишем функцию со значением имени в ней, мы можем позволить любому получить доступ к атрибуту имени, но только небольшое количество людей может изменить атрибут имени, и мы можем использовать замыкания, чтобы написать, какие люди в значении setName имеют разрешение модифицировать.
var person = function(){   
    //变量作用域为函数内部,外部无法访问,不会与外部变量发生重名冲突   
    var name = "FE";      
    return {
      //管理私有变量   
       getName : function(){   
           return name;   
       },   
       setName : function(newName){   
           name = newName;   
       }   
    }   
}; 
  1. кеш данных Предположим, мы выполняем вычислительно затратную функцию и возвращаем значение, которое также используется в других функциях.В этом случае замыкание может быть использовано для сохранения данных в памяти для использования другими функциями (это я видел в других блогах, специфика не очень понятны, если вам интересно, вы можете самостоятельно ознакомиться с соответствующей литературой).

недостаток: Вызывает слишком много потребления памяти, при неправильном обращении это вызовет утечку памяти.

Разница между 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 метод

  1. Метод 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(){
&emsp;&emsp;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 также может пройти по массиву, но есть следующие проблемы:

  1. Индекс индекса — это строковое число, которое нельзя использовать непосредственно для геометрических операций.

  2. Возможно, порядок обхода не соответствует внутреннему порядку фактического массива.

  3. Использование for in перебирает все перечисляемые свойства массива, включая прототипы. Например, способ-прототип метода и атрибуты имени верхнего каштана.

Таким образом, for in больше подходит для перебора объектов, не используйте for in для перебора массивов.

Таким образом, в дополнение к использованию цикла for, как правильно обходить массив для достижения наших ожиданий (то есть без обхода метода и имени), цикл for of в ES6 даже лучше.

Array.prototype.method=function(){
&emsp;&emsp;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(){
&emsp;&emsp;console.log(this);
}
var myObject={
&emsp;&emsp;a:1,
&emsp;&emsp;b:2,
&emsp;&emsp;c:3
}
for (var key in myObject) {
  console.log(key);
}

for in может перейти к методу метода прототипа myObject.Если вы не хотите проходить метод прототипа и свойства, вы можете судить об этом внутри цикла.hasOwnProperyметод, чтобы определить, является ли свойство свойством экземпляра объекта

for (var key in myObject) {
&emsp;&emsp;if(myObject.hasOwnProperty(key)){
&emsp;&emsp;&emsp;&emsp;console.log(key);
&emsp;&emsp;}
}

То же самое можно сделать через ES5Object.keys(myObject)Получает массив свойств экземпляра объекта, исключая методы и свойства прототипа.

Object.prototype.method=function(){
&emsp;&emsp;console.log(this);
}
var myObject={
&emsp;&emsp;a:1,
&emsp;&emsp;b:2,
&emsp;&emsp;c:3
}
Object.keys(myObject).forEach(function(key,index){&emsp;&emsp;
    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);
    
}
  • Преимущества Решение двух проблем в конструкторах и прототипном наследовании
  • Недостаток Конструктор супертипа вызывается дважды всякий раз, когда

公众号