Передние знания (4) -js функция рукописного ввода статей

JavaScript

1. Смоделируйте новое

1-1. Сценарии использования

Когда необходимо создать несколько объектов, если цикл будет"создание дубликата"Многие переменные занимают память. Если он генерируется с новым, то повторяющиеся свойства внутри находятся в"прототип", он не занимает память.

1-2. Значение

Сохранить код, относится к синтаксическому сахару, может иметь все функции в конструкторе"свойства и методы", а также"расширять".

1-3. Этапы реализации

  1. Входящие параметры: конструктор и параметры функции
  2. Создать 1 пустой объект
  3. Сделать так, чтобы __proto__ пустого объекта указывало на прототип конструктора (прототип)
  4. Привяжите это к пустому объекту и выполните конструктор
  5. Определите возвращаемое значение конструктора, если это объект, верните это значение напрямую. Если нет, вернуть объект, созданный в начале

1-4. Реализация кода

буквальный код ("Текстовый код такой же, как код изображения ниже, но выделение кода изображения более мощное."):

// 新建构造函数--用来new的函数
// 构造函数是一种特殊的方法:用来在创建对象时为对象成员变量赋初始值
function Dog(name){
  // 这里的this指向window,所以下面才需要重新将this指向自身,这样才能将属性挂载成功
  this.name = name
  this.say = function() {
    console.log("my name is" + this.name);
  }
}

// 手写new函数
function _new(fn,...arg){
  const obj = {};
  // 将构造函数的原型挂载到新对象上
  Object.setPrototypeOf(obj, fn.prototype)
  // 将this指向新对象,执行构造函数
  let result = fn.apply(obj, arg);
  return result instanceof Object ? result : obj
}
// 验证
var dog = _new(Dog, "caigou")
dog.say();// my name is caigou

Код изображения:

2. Имитация экземпляра

2-1. Сценарии использования

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

2-2 Использование

Такие как :

console.log(1 instanceof Number);                    // false
console.log(true instanceof Boolean);                // false 
console.log('str' instanceof String);                // false  
console.log([] instanceof Array);                    // true
console.log(function(){} instanceof Function);       // true
console.log({} instanceof Object);                   // true
console.log(null instanceof Object);                 // false

2-3 Преимущества и недостатки

  • "преимущество": Может различать массив, объект и функцию, подходит для оценки объектов экземпляра пользовательского класса.
  • "недостаток": числовые, логические, строковые базовые типы данных не могут быть оценены

2-4. Этапы реализации

  1. Входящие параметры — это экземпляр L слева и конструктор R справа.
  2. обрабатывать границы, возвращать false, если объект должен быть обнаружен как примитивный тип
  3. Взять прототип входящего параметра отдельно
  4. Определить, принял ли прототип слева значение null, если он равен нулю, вернуть false; если прототипы с обеих сторон равны, вернуть true, в противном случае продолжить принимать прототип левого прототипа.

2-5. Реализация кода

"буквальный код"(Текстовый код такой же, как код изображения ниже, но код изображения выделен сильнее):

// 手写instanceof函数
function instance_of(L, R){
  // 验证如果为基本数据类型,就直接返回false,因为instanceof只能判断引用类型
  const baseType = ['string', 'number','boolean','undefined','symbol']
  if(baseType.includes(typeof(L)) || L === null) { return false }

  let Lp = L.__proto__;
  let Rp = R.prototype//函数才有prototype属性
  while(true){
    if(Lp === null){//找到最顶层还是没找到,说明不匹配
      return false
    }
    if(Lp === Rp){
      return true
    }
    Lp = Lp.__proto__
  }
}
// 测试
var a = instance_of([],Array)
console.log(a) //true

"Код изображения:"

3. Имитация вызова

3-1. Сценарии использования

Измените указатель этого.
Вообще говоря, this в es5 всегда указывает на метод, вызвавший его последним;
Стрелочная функция ES6 — это нестрелочная функция ближайшего слоя стрелочной функции.
Итак, мы видим, что это часто сбивает с толку, поэтому js реализует новую функцию, которая поможет нам избежать этой путаницы.
Используйте call, apply, bind (эти три примерно одинаковы), это укажет на входящий объект. Когда объект не существует:

  • В строгом режиме: указывает на undefined
  • В нестрогом режиме: это указывает на окно

3-2. Этапы реализации

  1. Входящие параметры:"объект, на который указывает this",а также"Функции вызова"параметры для передачи
  2. Границы обработки:
  • Объект не существует, это указывает на окно;
  • Вызывается не функция, выдает ошибку
  1. Буду"Функции вызова"подняться на"объект, на который указывает this"по атрибуту fn.
  2. воплощать в жизнь"объект, на который указывает this"В функции fn и передать параметры, удалить атрибут fn и вернуть результат.

3-3. Реализация кода

"буквальный код"(Текстовый код такой же, как код изображения ниже, но код изображения выделен сильнее):

// 手写call函数
// call是在函数原型上的,所以我们这里也要挂载到原型上
Function.prototype.call2 = function(obj, ...ary) {
  obj = obj || window; //obj为空则指向window
  obj.fn = this; //this指被调用的函数,将函数挂载到当前对象的fn属性上
  if (typeof this != "function") {
    //边界处理
    throw new TypeError("Erorr");
  }
  /**此时this指向的对象的结构
    obj:{
      fn:bar(){}
    }
   */ 
  //this指向最后一个调用函数(fn)的对象(obj),所以指向obj
  var result = obj.fn(...ary);//this指向最后一个调用函数的对象,所以指向obj
  // 执行完以后删除,因为对象原本没有这个fn属性
  delete obj.fn;
  return result;
};

// 测试
var value = 2;
var obj1 = {
  value:1
}
function bar(name, age){
  var myObj = {
    name:name,
    age:age,
    value:this.value
  }
  console.log(this.value,myObj)
}
bar.call2(null) //打印 2 {name: undefined, age: undefined, value: 2}
bar.call2(obj1,"zi","shu")// 打印 1 {name: "zi", age: "shu", value: 1}

"Код изображения:"

4. Смоделируйте глубокую копию

4-1. Сценарии использования

Когда нам нужно использовать объект и изменить значение объекта, но мы не хотим изменять исходный объект, например:

  • Когда дочерний компонент vue получает значение реквизита, переданное родительским компонентом, и дочернему компоненту необходимо изменить это значение, непосредственное изменение значения реквизита сообщит об ошибке. В настоящее время мы можем глубоко скопировать значение реквизита в данных, а затем изменить соответствующее значение в данных.

4-2.Сравнение различных методов

  1. JSON.parse(JSON.stringify(copyObj))
    "преимущество"Простой в использовании
    "недостаток"
  • Если функция в объекте, обычная, дата не может быть скопирована
  • Не удается скопировать свойства и методы в цепочке прототипов объекта copyObj
  • Когда уровень данных очень глубокий, стек переполняется
  1. 手写拷贝
    "преимущество"Можно рассматривать разные ситуации
    "недостаток"сложнее в реализации

4-3 Разница между присваиванием, поверхностным копированием и глубоким копированием

4-4. Этапы реализации

  1. Если переданный объект не существует, вернуть null; если это специальный объект, новый специальный объект.
  2. Создайте объект objClone для хранения клонированного объекта.
  3. Затем пройдитесь по объекту, если это базовые данные, поместите их прямо в objClone
  4. Если это объект, рекурсивно.

4-5. Реализация кода

"буквальный код"(Текстовый код такой же, как код изображения ниже, но код изображения выделен сильнее):

// 手写深拷贝函数
function deepClone(obj){
  if(obj == null){
    return null
  }
  if(obj instanceof RegExp){
    return new RegExp(obj)
  }
  if(obj instanceof Date){
    return new Date(obj)
  }
  var objClone = Array.isArray(obj) ? [] : {}
  for(let key in obj){
    if(obj.hasOwnProperty(key)){
    //如果还是对象,就递归
      if(obj[key] && typeof obj[key] === "object"){
        objClone[key] = deepClone(obj[key])
      }else{
        objClone[key] = obj[key]
      }
    }
  }
  return objClone
}
// 测试
var dog = {
  name: {
    chineseName:"狗"
  }
}
var newDog = deepClone(dog)
newDog.name.chineseName = "新狗"
console.log("newDog",newDog)//{ name: { chineseName:"新狗"}}
console.log("dog",dog)//{ name: { chineseName:"狗"}}

"Код изображения:"

5. Функция защиты от сотрясений, написанная от руки.

5-1. Сценарии использования

В нашем обычном процессе разработки существует множество сценариев, которые"Частые триггерные события", например, поле поиска отправляет запросы в режиме реального времени, onmousemove, resize, onscroll и т. д.
Для производительности в некоторых случаях мы не можем или не хотим запускать событие часто.Функция анти-встряски заключается в выполнении обратного вызова через n секунд после запуска события."Срабатывает снова в течение n секунд",но"повторное время".

5-2.Этапы реализации

  1. Входящий параметр - функция исполнения Fn, время задержки Wait.
  2. Определите переменную таймера n с начальным значением null.
  3. Возвращает функцию.Если n не равно нулю, это означает, что таймер уже есть и его нужно очистить.
  4. В противном случае повторное время.

5-3. Реализация кода

"буквальный код"(Текстовый код такой же, как код изображения ниже, но код изображения выделен сильнее):

// 手写防抖函数
function debounce(fn, wait){
  var timer = null;
  return function() {
    // 有定时器了,在规定时间内再触发就要清除前面的定时,重新计时
    if(timer !== null){
      clearTimeout(timer)
    }
    // 重新计时
    timer = setTimeout(fn, (wait));
  }
}
// 测试
function handle(){
  console.log(Math.random())
}
// 窗口大小改变,触发防抖,执行handle
window.addEventListener("resize",debounce(handle,1000))

"Код изображения:"

"Как запомнить антишейк и троттлинг?"

  • Антишейк это вибрато("Задерживать"): Anti-shake задерживает выполнение
  • дросселирование, т.е."первый класс": выполняется только один раз во время острых ощущений

6. Ручная функция дросселирования

6-1. Сценарии использования

Когда событие срабатывает, оно гарантировано"определенный период времени"Только внутри"позвони один раз"функция. Например, когда страница прокручивается, запрос отправляется время от времени.

6-2. Этапы реализации

  1. Входящий параметр — это функция выполнения fn, а время ожидания — это ожидание.
  2. Сохраните начальное время сейчас.
  3. Возвращает функцию, которая, если время ожидания превышено, выполняет функцию и обновляет ее до текущего времени.

6-3. Реализация кода

"буквальный код"(Текстовый код такой же, как код изображения ниже, но код изображения выделен сильнее):

// 手写节流函数
function throttle(fn, wait,...args){
  var pre = Date.now();
  return function() {
    // 函数可能会有入参
    var context = this
    var now = Date.now()
    if(now - pre >= wait){
      // 将执行函数的this指向当前作用域
      fn.apply(context,args)
      pre = Date.now()
    }
  }
}
// 测试
var name = "夏"
function handle(val){
  console.log(val+this.name)
}
// 滚动鼠标,触发防抖,执行handle
window.addEventListener("scroll", throttle(handle,1000,"龙"))

"Код изображения:"

7. Дедупликация рукописного массива

7-1.новый метод установки

let arr = [1,2,2,2,33,33,4]
console.log([...new Set(arr)])//[1,2,33,4]

7-2.Рукописная функция дедупликации (переход для получения уникальных значений в новый массив)

let arr = [1,2,2,2,33,33,4]
let arrNew = []
arr.forEach((item,index)=>{
  if(arrNew.indexOf(item)===-1){
  //或者 if(!arrNew.includes(item)){
    arrNew.push(item)
  }
})
console.log(arrNew)//[1,2,33,4]

7-3 Ключ использования карты не может быть повторен

// 利用map的键不能重复,去掉某个属性相同的项
function uniqBy(arr,key){
  return [...new Map(arr.map(item=>[item[key],item])).values()]
}
const singers = [
  {id:1,name:"lo"},
  {id:1,name:"ming"},
  {id:2,name:"li"}
]
console.log(uniqBy(singers,"id"))// [{id:1,name:"ming"},{id:2,name:"li"}]

"Код изображения:"

8. Слияние массивов

8-1.concat

let arr = ["a","b"]
let arr1 = ["c","d"]
console.log(arr.concat(arr1))//["a","b","c","d"]

8-2.es6 дисплей

let arr2 = [...arr,...arr1]
console.log(arr2)//["a","b","c","d"]

"Код изображения:"

9. Расширение массива

9-1. flat

let arr = [1,2,[3,4],[5,6,[7,8,9]]]
console.log(arr.flat(Infinity))//[1, 2, 3, 4, 5, 6, 7, 8, 9]

9-2. join, split

console.log(arr.toString().split(",").map(Number))//[1, 2, 3, 4, 5, 6, 7, 8, 9] 
console.log(arr.join().split(",").map(Number))//[1, 2, 3, 4, 5, 6, 7, 8, 9] 

"Код изображения:"

10. Определите, является ли это массивом

let arr = [1,2]

10-1. instanceof

console.log(arr instanceof Array)

10-2. constructor

console.log(arr.constructor === Array)

10-3. constructor

console.log(arr.constructor === Array)

10-4. Такие методы, как push, чтобы определить, есть ли у объекта массив

console.log(!!arr.push && !!arr.concat)

10-5. constructor

console.log(Array.isArray(arr))

11. Сортировать

(1) Пузырьковая сортировка

концепция

Начиная с первого элемента, сравните текущий элемент со следующим индексированным элементом. Если текущий элемент больше, то поменять местами местами и повторять до тех пор, пока не будет сравнен последний элемент.

Анимированная демонстрация
冒泡排序
Пузырьковая сортировка
шаг
  1. Сначала пройдите общее количество чисел, чтобы сравнить их с другими числами.
  2. Сколько раз сравнивать каждое число с другими числами
  3. Поменяться местами, если предыдущее число меньше последнего
рукописный код
function bubbleSort(arr) {
    var len = arr.length;
    //多少个数要跟其它数比较
    for (var i = 0; i < len; i++) {
    //每个数要跟其它数比较多少次
        for (var j = 0; j < len - 1 - i; j++) {
            if (arr[j] > arr[j+1]) {        //相邻元素两两对比
                //元素交换
               [arr[j+1],arr[j]] = [arr[j],arr[j+1]];       
            }
        }
    }
    return arr;
}
//测试
let arr = [1,44,6,77,3,7,99,12]
console.log(bubbleSort(arr))// [1, 3, 6, 7, 12, 44, 77, 99]

(2) Быстрая сортировка

концепция

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

Анимированная демонстрация
шаг
  1. Сначала выполните обработку границ
  2. Определите переменные массива слева и справа
  3. Возьмите среднюю позицию массива, найдите среднее значение через эту позицию и одновременно удалите его в исходном массиве.
  4. Пройдите массив, определите размер текущего элемента и поместите его на соответствующую сторону
  5. После обхода выполните рекурсию по левому и правому массивам, а затем соедините среднее число.
рукописный код
  function quickSort(arr) {
      if (!Array.isArray(arr)) return;
      if (arr.length <= 1) return arr;
      var left = [], right = [];
      // 以中间位置为下标
      var num = Math.floor(arr.length / 2);
      // arr.splice(num,1)取下标为num的这个数(会改变原数组),结果是数组的形式,所以加[0]
      var numValue = arr.splice(num, 1)[0];
      for (var i = 0; i < arr.length; i++) {
        if (arr[i] > numValue) {
          right.push(arr[i]);
        } else {
          left.push(arr[i]);
        }
      }
      return [...quickSort(left), numValue, ...quickSort(right)]
    }
//测试
let arr = [1,44,6,77,3,7,99,12]
console.log(quickSort(arr))// [1, 3, 6, 7, 12, 44, 77, 99]

12. Наследование

(1) Наследование цепи протокола

Введение

"основной:"

  • родительский класс"пример"как подкласс"прототип"

"преимущество:"

  • Простота реализации и легкость понимания

"недостаток:"

  • Свойства прототипа, содержащие значения ссылочного типа, являются общими для всех экземпляров, что может привести к модификации одного экземпляра."повлияет"Другой экземпляр.
  • Чтобы добавить свойства и методы в подкласс, его необходимо выполнить после оператора, такого как new Cat(), а не в конструкторе.
    Например, при создании экземпляра Child нельзя передавать параметры в Cat. Если вы хотите добавить его, вы можете добавить его только после new Cat()
    Из-за этих двух проблем цепочки прототипов редко используются на практике по отдельности.
рукописный код
//父类
function Cat () {
    this.name = '橘猫';
}
Cat.prototype.getName = function () {
    console.log(this.name);
}
//子类
function Child () {}
// 将父类的实例作为子类的原型
Child.prototype = new Cat();
//测试
var child1 = new Child();//我们要取出里面的getName,需要通过对象的属性的方式取出来(obj.fn),所以要new出一个对象
console.log(child1.getName()) // 橘猫
решить путаницу
  • Некоторые люди могут не понять, зачем определять дочернюю функцию:

    • Поскольку наследование — это дочерний класс, наследующий родительский класс, наш родительский класс — это конструктор Cat. Тогда отсутствует подкласс, Итак, чтобы создать подкласс, обратите внимание: когда мы говорим родительский класс, подкласс ссылается на конструктор
  • Конструктор Child уже есть, зачем new Child():

    • По сути, этот шаг заключается в проверке наличия метода родительского класса в подклассе. Сам по себе Child — это просто конструктор, нам нужно получить его экземпляр, чтобы увидеть, есть ли этот метод в его экземпляре, чтобы определить, завершило ли Child наследование.

(2) Заимствование наследования конструктора

Введение

"основной:"

  • Используйте .call() и .apply() для"конструктор родительского класса"Введение функции подкласса эквивалентно"копировать"Атрибуты экземпляра родительского класса передаются дочернему классу,"нет прототипа".

"преимущество:"

    1. Наследовать только свойства конструктора родительского класса, а не свойства прототипа родительского класса
    1. Устраняет 2 недостатка наследования цепочки прототипов.
    1. Может наследовать несколько свойств конструктора (вызывать несколько).
    1. В дочернем экземпляре вы можете передавать параметры родительскому экземпляру

"недостаток:"

    1. Наследоваться могут только свойства конструктора родительского класса.
    1. Повторное использование конструктора невозможно. (вспомните каждый раз, когда вы его используете)
    1. Каждый новый экземпляр имеет раздутую копию конструктора родительского класса.
      Поэтому этот метод редко используется в одиночку.
рукописный код
//父类
function SuperType(name){
      this.name=name;
}
//子类
function Child(name,age){
      //将SuperType中的this指向了Child,从而将父类构造函数SuperType引入子类函数Child
      //Child继承了SuperType,同时还传递了参数
      SuperType.call(this,name);
      //实例属性
      this.age=age;
}
//测试
var Child1=new Child("mary",22);
console.log(Child1.name);  //mary
console.log(Child1.age);  //22
console.log(Child1 instanceof SuperType);  //false
решить путаницу
  • Как понять преимущество 1:

    • Поскольку наследование использует"call", что эквивалентно добавлению исходной функции SuperType"this"заменяется"Child", поэтому у Child также есть атрибут name. Уведомление:"Мы не использовали прототипы полностью".
  • Как понять преимущество 2:

    • из-за"нет прототипа", поэтому свойства не будут общими для всех экземпляров, вы можете добавить свойства и передать параметры в конструкторе: например, SuperType.call(this,name), передать его родительскому классу"name"значение (значение свойства"динамичный"из). а также"прототипное наследование", родительский класс может только"писать до смерти": this.name = 'оранжевый кот'. Кроме того, this.age=age в Child также добавляется непосредственно в конструктор."Атрибуты".

(3) Комбинированное наследование

Введение

"основной:"

  • Наследование цепочки прототипов и наследование построения сочетают в себе преимущества двух режимов."передать параметры"а также"мультиплекс"
  • Идея такова:
    • путем заимствования"Конструктор"для достижения"Наследование свойств экземпляра"
    • использовать"Сеть прототипов"реализовать пару"Наследование методов-прототипов"
    • Таким образом, повторное использование функций достигается путем определения методов в прототипе, и можно гарантировать, что каждый экземпляр будет иметь свои собственные свойства.
  • — наиболее часто используемый шаблон наследования в JavaScript.

"преимущество:"

    1. Может наследовать свойства прототипа родительского класса, может передавать параметры и может использоваться повторно.
    1. Свойства конструктора, представленные каждым новым экземпляром, являются закрытыми.

"недостаток:"

    1. Вызванный дважды (вызов один раз, новый один раз) конструктора родительского класса (потребление памяти), конструктор подкласса заменит конструктор родительского класса в прототипе.
рукописный код
//父类
function SuperType(name){
      this.name=name;
      this.colors=["red", "blue", "green"];
}
SuperType.prototype.sayName=function(){
      console.log(this.name);
};
//子类
function Child(name, age){
      //继承【属性】    构造继承
      SuperType.call(this,name);
      this.age=age;
}
//继承【方法】     原型继承
Child.prototype=new SuperType();
Child.prototype.constructor=Child;
//测试
Child.prototype.sayAge=function(){
      console.log(this.age);
};
var Child1=new Child("mary", 22);
Child1.colors.push("black");
console.log(Child1.colors);   //red,blue,green,black
Child1.sayName();  //mary
Child1.sayAge();  //22

(4) Наследование прототипа

Введение

"основной:"

  • Это фиктивная реализация ES5 Object.create, которая"входящий объект"так как"объект создан"Прототип.
  • Подобно копированию объекта, обертывание его функцией.

"преимущество:"
См. раздел Наследование цепочки прототипов.

"недостаток:"
См. раздел Наследование цепочки прототипов.

рукописный код
function createObj(o) {
    function F(){}
    F.prototype = o;
    return new F();
}
//测试
var person = {
    name: 'kevin',
    friends: ['daisy', 'kelly']
}

var person1 = createObj(person);
var person2 = createObj(person);

person1.name = 'person1';
console.log(person2.name); // kevin

person1.firends.push('taylor');
console.log(person2.friends); // ["daisy", "kelly", "taylor"]

Примечание. При изменении значения person1.name значение person2.name не изменилось не потому, что person1 и person2 имеют независимые значения имени, а потому, что person1.name = 'person1', значение имени добавляется к person1, а не потому, что person1.name = 'person1' Изменено значение имени в прототипе.

(5) Паразитическое наследование

Введение

"основной:"

  • Это оболочка прототипного наследования.
  • Создайте функцию, которая просто инкапсулирует процесс наследования, внутренне улучшает объект в той или иной форме и, наконец, возвращает объект.

"преимущество:"

  • Пользовательский тип не создавался, потому что он просто обертывал оболочку для возврата объекта (это), и эта функция, естественно, стала новым созданным объектом.

"недостаток:"

  • Как и в случае с заимствованным шаблоном конструктора, метод создается каждый раз при создании объекта.
  • Прототипы не используются и не могут быть использованы повторно.
рукописный код
//在原型式的外面套了个函数
function createObj (o) {
    //这一步可以看做是原型式继承的简写
    var clone = object.create(o);
    //新增属性
    clone.sayName = function () {
        console.log('hi');
    }
    return clone;
}

(6) Наследование паразитарного состава

Введение

"основной:"
На самом деле это комбинация 3 методов наследования: паразитного, наследования по цепочке прототипов, наследования конструкции.

  • Паразитический: вернуть объект внутри функции, а затем вызвать
  • комбинация:
    • 1. Прототип функции равен другому экземпляру.
    • 2. Используйте apply или call, чтобы ввести в функцию еще один конструктор, который может передавать параметры

"преимущество:"

  • Исправлена ​​проблема с наследованием композиции

"недостаток:"

  • Как и в случае с заимствованным шаблоном конструктора, метод создается каждый раз при создании объекта.
  • Прототипы не используются и не могут быть использованы повторно.
рукописный код
//父类
function Parent (name) {
    this.name = name;
    this.colors = ['red', 'blue', 'green'];
}
Parent.prototype.getName = function () {
    console.log(this.name)
}
//子类
function Child (name, age) {
    //构造继承
    Parent.call(this, name);
    this.age = age;
}

// 关键的三步
// 套了一层函数,寄生式继承
var F = function () {};

F.prototype = Parent.prototype;
//原型链继承
Child.prototype = new F();


var child1 = new Child('kevin', '18');

console.log(child1);

В этой статье используетсяmdniceнабор текста