1. Смоделируйте новое
1-1. Сценарии использования
Когда необходимо создать несколько объектов, если цикл будет"создание дубликата"Многие переменные занимают память. Если он генерируется с новым, то повторяющиеся свойства внутри находятся в"прототип", он не занимает память.
1-2. Значение
Сохранить код, относится к синтаксическому сахару, может иметь все функции в конструкторе"свойства и методы", а также"расширять".
1-3. Этапы реализации
- Входящие параметры: конструктор и параметры функции
- Создать 1 пустой объект
- Сделать так, чтобы __proto__ пустого объекта указывало на прототип конструктора (прототип)
- Привяжите это к пустому объекту и выполните конструктор
- Определите возвращаемое значение конструктора, если это объект, верните это значение напрямую. Если нет, вернуть объект, созданный в начале
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. Этапы реализации
- Входящие параметры — это экземпляр L слева и конструктор R справа.
- обрабатывать границы, возвращать false, если объект должен быть обнаружен как примитивный тип
- Взять прототип входящего параметра отдельно
- Определить, принял ли прототип слева значение 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. Этапы реализации
- Входящие параметры:"объект, на который указывает this",а также"Функции вызова"параметры для передачи
- Границы обработки:
- Объект не существует, это указывает на окно;
- Вызывается не функция, выдает ошибку
- Буду"Функции вызова"подняться на"объект, на который указывает this"по атрибуту fn.
- воплощать в жизнь"объект, на который указывает 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.Сравнение различных методов
-
JSON.parse(JSON.stringify(copyObj))
"преимущество"Простой в использовании
"недостаток"
- Если функция в объекте, обычная, дата не может быть скопирована
- Не удается скопировать свойства и методы в цепочке прототипов объекта copyObj
- Когда уровень данных очень глубокий, стек переполняется
-
手写拷贝
"преимущество"Можно рассматривать разные ситуации
"недостаток"сложнее в реализации
4-3 Разница между присваиванием, поверхностным копированием и глубоким копированием
4-4. Этапы реализации
- Если переданный объект не существует, вернуть null; если это специальный объект, новый специальный объект.
- Создайте объект objClone для хранения клонированного объекта.
- Затем пройдитесь по объекту, если это базовые данные, поместите их прямо в objClone
- Если это объект, рекурсивно.
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.Этапы реализации
- Входящий параметр - функция исполнения Fn, время задержки Wait.
- Определите переменную таймера n с начальным значением null.
- Возвращает функцию.Если n не равно нулю, это означает, что таймер уже есть и его нужно очистить.
- В противном случае повторное время.
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. Этапы реализации
- Входящий параметр — это функция выполнения fn, а время ожидания — это ожидание.
- Сохраните начальное время сейчас.
- Возвращает функцию, которая, если время ожидания превышено, выполняет функцию и обновляет ее до текущего времени.
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) Пузырьковая сортировка
концепция
Начиная с первого элемента, сравните текущий элемент со следующим индексированным элементом. Если текущий элемент больше, то поменять местами местами и повторять до тех пор, пока не будет сравнен последний элемент.
Анимированная демонстрация
шаг
- Сначала пройдите общее количество чисел, чтобы сравнить их с другими числами.
- Сколько раз сравнивать каждое число с другими числами
- Поменяться местами, если предыдущее число меньше последнего
рукописный код
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) Быстрая сортировка
концепция
В наборе данных найдите контрольную точку, создайте два массива, сохраните левый и правый массивы соответственно и используйте рекурсию для следующего сравнения.
Анимированная демонстрация
шаг
- Сначала выполните обработку границ
- Определите переменные массива слева и справа
- Возьмите среднюю позицию массива, найдите среднее значение через эту позицию и одновременно удалите его в исходном массиве.
- Пройдите массив, определите размер текущего элемента и поместите его на соответствующую сторону
- После обхода выполните рекурсию по левому и правому массивам, а затем соедините среднее число.
рукописный код
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() для"конструктор родительского класса"Введение функции подкласса эквивалентно"копировать"Атрибуты экземпляра родительского класса передаются дочернему классу,"нет прототипа".
"преимущество:"
-
- Наследовать только свойства конструктора родительского класса, а не свойства прототипа родительского класса
-
- Устраняет 2 недостатка наследования цепочки прототипов.
-
- Может наследовать несколько свойств конструктора (вызывать несколько).
-
- В дочернем экземпляре вы можете передавать параметры родительскому экземпляру
"недостаток:"
-
- Наследоваться могут только свойства конструктора родительского класса.
-
- Повторное использование конструктора невозможно. (вспомните каждый раз, когда вы его используете)
-
- Каждый новый экземпляр имеет раздутую копию конструктора родительского класса.
Поэтому этот метод редко используется в одиночку.
- Каждый новый экземпляр имеет раздутую копию конструктора родительского класса.
рукописный код
//父类
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.
"преимущество:"
-
- Может наследовать свойства прототипа родительского класса, может передавать параметры и может использоваться повторно.
-
- Свойства конструктора, представленные каждым новым экземпляром, являются закрытыми.
"недостаток:"
-
- Вызванный дважды (вызов один раз, новый один раз) конструктора родительского класса (потребление памяти), конструктор подкласса заменит конструктор родительского класса в прототипе.
рукописный код
//父类
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набор текста