Сердцевина меча с шестью импульсами — расчесывание основных знаний JS

JavaScript

предисловие

Цель этой статьи

из JS运行,设计,数据,应用Четыре угла, чтобы разобраться в основных точках знаний JS

Схема темы

  1. JS запустить

    • переменное продвижение
    • контекст выполнения
    • объем
    • let
    • цепочка прицелов
    • Закрытие
    • цикл событий
  2. JS-дизайн

    • прототип
    • Сеть прототипов
    • this
    • call
    • apply
    • bind
    • new
    • наследовать
  3. JS-данные

    • тип данных
    • Хранение данных (глубокое и поверхностное копирование)
    • Оценка типа данных (неявное преобразование, равенство и равенство, два объекта равны)
    • Манипуляции с данными (обход массива, обход объекта)
    • Расчет данных (ошибка расчета)
  4. JS-приложение

    • Анти-шейк, дросселирование, каррирование

1. JS работает

условно разделить на четыре этапа

  1. лексический анализ: Разделите строку в коде js на осмысленные блоки кода, называемые词法单元

    • Когда браузер просто получает файл JS или сегмент кода скрипта, он будет думать, что это длинная строка.
    • Это непонятно, поэтому разбейте его на осмысленные фрагменты, например:var a = 1
  2. Разбор:Буду词法单元поток в抽象语法树(AST)и обработайте сгенерированные узлы дерева AST,

    • Например, если используется синтаксис ES6, используются let и const, и их необходимо преобразовать в var.

Зачем вам нужно абстрактное синтаксическое дерево?

  • Абстрактное синтаксическое дерево не зависит от конкретной грамматики, не зависит от деталей языка, в нем удобно делать множество операций
  • С другой стороны, существует много языков, C, C++, Java, Javascript и т. д., и у них разные языковые спецификации.
  • Однако после преобразования в абстрактное синтаксическое дерево оно становится непротиворечивым, что удобно для компилятора при выполнении дальнейших операций, таких как добавления, удаления, изменения и проверки.
  1. Этап предварительного разбора:

    • правила масштабирования будут определены
    • Подъем переменных и функций
  2. этап выполнения:

    • Создайте контекст выполнения и сгенерируйте стек контекста выполнения
    • Выполнить исполняемый код в соответствии с циклом событий

1 Область применения

Определяет область действия функций и переменных

  • разделен на全局作用域а также函数作用域,
  • В отличие от языков C и JAVA, JS не имеет области действия на уровне блоков, которая представляет собой просто область видимости фигурных скобок.

2. Подъем переменных и функций

Объявления глобальных переменных и функций подняты

  • Есть три способа объявить функцию,
    • function foo() {}
    • var foo = function () {}
    • var foo = new Function()
    • можно разделить на две категории,直接创建а также变量赋值
  • 变量赋值函数а также赋值普通变量Приоритет по положению, имя переменной такое же, как и первое перезаписывается
  • Непосредственное создание функции имеет более высокий приоритет, чем присвоение переменной, то же самое имя занимает первое, которое не имеет ничего общего с позицией, то есть прямое создание функции имеет наивысший приоритет даже после объявления переменной. .

3. Контекст выполнения

Есть разные области видимости, разные среды исполнения, нам нужно управлять переменными этих контекстов

  • Среда выполнения делится на три типа, контекст выполнения соответствует среде выполнения
    • глобальная среда выполнения
    • среда выполнения функции
    • среда выполнения eval (проблемы с производительностью не упоминаются)
  1. глобальный контекст выполнения
    • Сначала найдите объявление переменной,
    • Найдите снова объявление функции
  2. контекст выполнения функции
    • Сначала найдите параметры функции и объявления переменных
    • присвоить фактический параметр формальному параметру
    • найти объявление функции
  • Когда несколько функций вложены, будет несколько контекстов выполнения, что требует执行上下文栈для технического обслуживания, последний вошел первым
  • Контекст выполнения содержит变量环境а также词法环境
  • 变量环境Он содержит переменные, которые можно использовать в текущей среде.
  • Текущая среда бесполезна, вот и все作用域链

4. Цепочка областей

  • Цитирование определения высоты JS:Цепочка областей действия для обеспечения упорядоченного доступа к переменным и функциям, к которым имеет доступ контекст выполнения.
  • Порядок поиска переменных не в порядке стека контекста выполнения, а в порядке词法作用域решенный
  • Лексическая область静态作用域, который определяется позицией объявления функции, независимо от того, где вызывается функция, поэтому js такой особенный

5. Статическая и динамическая область видимости

  • Лексическая область видимости определяется при написании кода или определении
  • В то время как динамическая область определяется во время выполнения (this也是!)
    var a = 2;
    function foo() {
        console.log(a); // 静态2 动态3
    }
    function bar() {
        var a = 3;
        foo();
    }
    bar();

Закрытие

  • из-за作用域Ограничение , мы не можем получить доступ к переменным, определенным внутри функции, за пределами области действия функции, и фактические потребности требуют этого, что используется здесь闭包
  • Цитирование определения JS Definitive Guide:Замыкание — это функция, которая имеет доступ к переменной в области видимости другой функции.

1. Функция закрытия

  • for循环遍历进行事件绑定При выводе значения i это длина цикла for + 1
  • Это не то, что нам нужно, потому что JS не имеет области действия на уровне блоков, значение i, определенное в var, не уничтожается и сохраняется в глобальной переменной окружения.
  • Значение i, полученное при специальном выполнении события, является значением i после нескольких вычислений в глобальной переменной.
    for(var i = 0;i < 3;i++){
        document.getElementById(`item${i+1}`).onclick = function() {
            console.log(i);//3,3,3
        }
    }    
  • Функция закрытия: внешняя функция была выполнена, переменные, на которые ссылается внутренняя функция внешней функции, все еще хранятся в памяти, и набор переменных может быть вызван闭包
  • В процессе компиляции для внутренней функции движок JS просканирует этот метод один раз.Если будет ссылка на переменную внешней функции, пространство кучи будет создано и заменено новым.Closureобъект, используемый для хранения переменных закрытия
  • Используйте эту функцию, чтобы добавить уровень закрытия в метод для хранения текущего значения i и привязать событие к функции, возвращаемой вновь добавленной анонимной функцией.
for(var i = 0;i < 3;i++){
    document.getElementById(`item${i+1}`).onclick = make(i);
}
function make(e) {
    return function() {
        console.log(e)//0,1,2
};

заключительная записка

  • Мы непреднамеренно написали замыкание, и переменные, на которые внутренняя функция ссылается на внешнюю функцию, все еще хранятся в памяти,
  • То, что должно быть уничтожено, не уничтожено,由于疏忽或错误造成程序未能释放已经不再使用的内存, в результате чего内存泄漏
  • В то же время обратите внимание, что замыкание не вызовет утечек памяти, наше неправильное использование замыкания и есть утечка памяти.

цикл событий

  • Выполнение кода JS на основе цикла событий
  • JS — это один поток, который гарантирует, что выполнение не будет заблокировано асинхронным
  1. исполнительный механизм
    • Проще говоря, один стек выполнения и две очереди задач
    • Когда макрозадача найдена, она помещается в очередь макрозадач, а когда обнаруживается микрозадача, она помещается в очередь микрозадач.
    • Когда стек выполнения пуст, выполните все микрозадачи в очереди микрозадач, а затем возьмите макрозадачу из очереди макрозадач для выполнения.
    • так цикл
  2. Макросы и микрозадачи macroTask: setTimeout, setInterval, ввод-вывод, рендеринг пользовательского интерфейса микрозадача: обещание.затем

2. JS-дизайн

1. Прототип

  1. Дизайн JS
  • Появились новые операторы и конструкторы, но нет понятия классов, а используются прототипы для имитации классов для достижения наследования.
  1. Путешествие по JS-дизайну
  • В начале разработки JS дал короткое время и был определен как простой язык веб-скриптов, не слишком сложный, и хотел имитировать концепцию Java (именно поэтому JS называется JavaScript)
  • Таким образом, заимствование из Java对象,构造函数,newконцепция оператора, при этом отказываясь от сложногоclass(класс) концепция
  1. механизм наследования
  • нужен继承Механизм для связывания всех объектов вместе, вы можете использовать конструктор
  • Но недостатком конструктора для создания экземпляра объекта является то, что无法共享属性和方法
  1. prototypeАтрибуты
  • Для решения вышеуказанных проблем введениеprototypeСобственность, то есть мы часто говорим, что прототип
  • установить один для конструктораprototypeАтрибуты и методы, которые должны быть общими для объектов экземпляра, помещаются в этот объект.

После того, как весь базовый проект завершен, следующие API-интерфейсы являются логичными.

прототип

  • Каждый объект js связан с другим объектом при его создании.
  • Этот объект является прототипом, и каждый объект наследует свойства от прототипа.

proto

  • Каждый объект имеет свойство __proto__, которое указывает на прототип объекта.
  • Свойство прототипа конструктора равно свойству __proto__ экземпляра объекта.
  • Это свойство не является каноническим свойством в ES5, это просто синтаксический сахар для удобства получения прототипов в браузере.
  • мы можем использоватьObject.getPrototype()метод получения прототипа

constructorПрототипы не указывают на экземпляры, потому что конструктор может иметь несколько экземпляров объекта. Но есть прототипы, указывающие на конструкторы, и каждый прототип имеет атрибут конструктора, указывающий на связанный с ним конструктор.

function Per() {} // 构造函数
const chi = new Per() // 实例对象

chi.__proto__ ===  Per.prototype // 获取对象的原型 也是就构造函数的prototype属性
Per.prototype.constructor === Per // constructor属性 获取当前原型关联的构造函数

Экземпляры и прототипы

  • Когда атрибут экземпляра не может быть найден, он будет искать атрибут прототипа, связанного с объектом, и продолжит поиск.
  • Эта цепочка отношений между экземпляром и архетипом, образующая原型链
    function Foo() {}
    Foo.prototype.name = 'tom'
    const foo = new Foo()
    foo.name = 'Jerry'
    console.log(foo.name); // Jerry
    delete foo.name
    console.log(foo.name); // tom

2. Цепочка прототипов

Сначала покажите знакомую карту сети原型链关系图

Это цепочка отношений между экземпляром и конструктором, прототипом

  • примерprotoуказать на прототип

  • Свойство прототипа конструктора указывает на прототип

  • Свойство конструктора прототипа указывает на конструктор

  • всех конструкторовprotoуказывает на Function.prototype

  • Function.prototype protoуказывает на Object.prototype

  • Object.prototype protoуказывает на ноль

Прототип функционального объекта (Function.prototype) — это машина, ответственная за создание конструктора, включая объект, строку, число, логическое значение, массив и функцию. Затем конструктор используется для создания конкретного экземпляра объекта.

function Foo() {}
// 1. 所有构造函数的 __proto__ 指向 Function.prototype
Foo.__proto__ // ƒ () { [native code] }
Function.__proto__ // ƒ () { [native code] }
Object.__proto__ // ƒ () { [native code] }

// 2. 所有构造函数原型和new Object创造出的实例  __proto__ 指向 Object.prototype
var o = new Object()
o.__proto__ // {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ ...}
Function.prototype.__proto__  // {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ ...}

// 3. Object.prototype 指向null
Object.prototype.__proto__ // null

2. this

  1. При вызове как объектный метод, указывающий на объектobj.b();// указывает на объект
  2. Как вызов метода функции,var b = obj.b; b();// Указываем на глобальное окно (метод функции на самом деле является методом объекта окна, поэтому он такой же, как и выше, кто бы его ни вызывал, указывает на кого)
  3. Вызывается как конструкторvar b = new Per();// это указывает на текущий объект экземпляра
  4. Звоните как звоните и подавайте заявкуobj.b.apply(object, []);// это указывает на текущее указанное значение

кто бы ни звонил


const obj = {a: 1, f: function() {console.log(this, this.a)}}
obj.f(); // 1

const a = 2;
const win = obj.f;
win(); // 2

function Person() {
    this.a = 3;
    this.f = obj.f;
}
const per = new Person()
per.f() // 3

const app = { a: 4 }
obj.f.apply(app); // 4

thisуказать да动态作用域, кто кому звонит,

3. call

  1. Определение: вызов функции или метода с указанным значением this и рядом указанных значений параметров.
  2. Пример:
var obj = {
    value: 1,
}
function foo (name, old) {
    return {
        value:this.value, 
        name, 
        old
    }
} 
foo.call(obj, 'tom', 12); // {value: 1, name: "tom", old: 12}
  1. Требовать:
  • вызов меняет суть этого,
  • функция foo выполняется
  • Поддержка передачи параметров, количество параметров не фиксировано
  • Этот параметр может быть передан null, при передаче null он указывает на окно
  • Функции могут иметь возвращаемые значения
  • Нефункциональная обработка оценки вызова
  1. реализация и идеи
Function.prototype.call1 = function (context, ...args) {
    if(typeof this !== 'function') {throw new Error('Error')} // 非函数调用判断处理
    context = context || window; // 非运算符判定传入参数 null则指向window
    const key = Symbol(); // 利用symbol创建唯一的属性名,防止覆盖原有属性
    context[key] = this; // 把函数作为对象的属性,函数的this就指向了对象
    const result = context[key](...args) // 临时变量赋值为执行对象方法
    delete context[key]; // 删除方法,防止污染对象
    return result // return 临时变量
}
  1. Сценарии применения

Измените этот указатель, в основном, чтобы заимствовать методы или свойства.

    1. Определить тип данных, заимствоватьObjectизtoStringметодObject.prorotype.toString.call()
    1. Подкласс наследует свойства родительского класса,function Chi() {Par.call(this)}

4. apply

  1. Определение: вызов функции или метода с указанным значением this и массивом (массив содержит несколько указанных значений параметров)
  2. Пример:
var obj = {
    value: 1,
}
function foo (name, old) {
    return {
        value:this.value, 
        name, 
        old
    }
} 
foo.apply(obj, ['tom', 12], 24); // {value: 1, name: "tom", old: 12}
  1. Требовать:
  • вызов меняет суть этого,
  • функция foo выполняется
  • Поддерживает передачу параметров, второй параметр является массивом, следующие параметры недействительны, а количество параметров в массиве не фиксировано.
  • Этот параметр может быть передан null, при передаче null он указывает на окно
  • Функции могут иметь возвращаемые значения
  • Нефункциональная обработка оценки вызова
  1. реализация и идеи
Function.prototype.apply1 = function (context, args) { // 与call实现的唯一区别,此处不用解构
    if(typeof this !== 'function') {throw new Error('Error')} // 非函数调用判断处理
    context = context || window; // 非运算符判定传入参数 null则指向window
    const key = Symbol(); // 利用symbol创建唯一的属性名,防止覆盖原有属性
    context[key] = this; // 把函数作为对象的属性,函数的this就指向了对象
    const result = context[key](...args) // 临时变量赋值为执行对象方法
    delete context[key]; // 删除方法,防止污染对象
    return result // return 临时变量
}
  1. Сценарии применения
  • то же, что звонить

5. bind

  1. определение
    • Метод bind создает новую функцию,
    • Когда вызывается новая функция, первым параметром является this во время выполнения,
    • Следующие параметры будут переданы в качестве его параметров перед передачей фактических параметров.
  2. Пример
const obj = {
    value: 1
};
function foo(name, old) {
    return {
        value: this.value,
        name,
        old
    }
}
const bindFoo = foo.bind(obj, 'tom'); 
bindFoo(12); // {value: 1, name: "tom", old: 12}
  1. Требовать
    • Возвращает функцию с this в качестве первого параметра при выполнении
    • Вы можете передать часть параметров при привязке, а затем передать другую часть параметров при выполнении функции
    • Функция привязки используется как конструктор, это недопустимо, но параметры допустимы,
    • И в качестве конструктора прототип должен указывать на прототип связанной функции, чтобы экземпляр мог наследовать значение в прототипе.
    • Обработка суждения о привязке нефункционального вызова
  2. реализация и идеи
Function.prototype.bind1 = function (context, ...args) {
    if(typeof this !== 'function') {throw new Error('Error')} // 非函数调用判断处理
    const self = this; // 保存当前执行环境的this
    const Foo = function() {} // 保存原函数原型
    const res = function (...args2) { // 创建一个函数并返回
        return self.call( // 函数内返回
            this instanceof res ? this : context, // 作为构造函数时,this指向实例,:作为普通函数this正常指向传入的context
             ...args, ...args2) // 两次传入的参数都作为参数返回
    }
    Foo.prototype = this.prototype; // 利用空函数中转,保证在新函数原型改变时bind函数原型不被污染
    res.prorotype = new Foo();
    return res;
}

6. new

посмотриnewЧто может пример

  • Доступные свойства конструктора
  • Доступ к свойствам прототипа
  • Если конструктор имеет возвращаемое значение, он возвращает объект, а экземпляр может получить доступ только к свойствам возвращаемого объекта, это недопустимо.
  • Возвращает значение базового типа, нормальная обработка, это допустимо
function Persion(name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype.sayName = function () {
    console.log(this.name)
}
var per = new Person('tom', 10)
per.name // 10 可访问构造函数的属性
per.sayName // tom 访问prototype的属性

новая реализация

var person = factory(Foo)

function factory() { // new是关键字,无法覆盖,函数替代
    var obj = {}; // 新建对象obj
    var con = [].shift.call(arguments); // 取出构造函数
    obj._proto_ = con.prototype; // obj原型指向构造函数原型
    var res = con.apply(obj, arguments); // 构造函数this指向obj
    return typeof(res) === 'object' ? ret : obj;
}

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

  1. Цепное наследование прототипов и прототипное наследование
  • Невозможно передать параметры родительскому классу, только совместно использовать свойства
  1. Заимствование конструкторов и паразитическое наследование
  • Метод не находится на прототипе и должен каждый раз создаваться заново.
  1. наследование композиции
  • Хотя две вышеупомянутые проблемы решены, но отец вызывается дважды,
  • Одни и те же свойства будут использоваться в экземпляре и прототипе.
  1. Наследование паразитарного состава
  • Текущее лучшее решение

Реализация наследования паразитной композиции

function P (name) { this.name = name; } // 父类上绑定属性动态传参
P.prototype.sayName = function() { // 父类原型上绑定方法
    console.log(111)
}
function F(name) { // 子类函数里,父类函数利用`call`函数this指向子类,传参并执行
    P.call(this, name)
}
const p = Object.create(P.prototype) // Object.create 不会继承构造函数多余的属性和方法
p.constructor = F; // constructor属性丢失,重新指向
F.prototype = p; // 子类原型 指向 中转对象

const c = new F('tom'); // 子类实例 化
c.name // tom
c.sayName() // 111 

3. JS-данные

1. Тип данных

JS делится на базовые типы и ссылочные типы.

  • основной тип:Boolean Undefined String Number Null Symbol
  • Тип ссылки:Object Funciton ArrayЖдать

2. Хранение данных (глубокое и поверхностное копирование)

js типы данных делятся на базовые типы и ссылочные типы.

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

Два объекта указывают на один и тот же адрес, изменение одного повлияет на другой

Специальные методы объекта массива (глубокое копирование одного уровня)

  1. obj2 = Object.assign({}, obj1)

  2. arr2 = [].concat(arr1)

  3. arr2 = arr1.slice(0)

  4. arr2 = Array.form(arr1)

  5. arr2 = [...arr1];

Вышеупомянутые методы могут глубоко копировать только один слой

JSON.parse(JSON.stringify(obj)) (многослойный)Вместо копирования объекта копирование строки откроет новый адрес памяти и прервет связь указателя со ссылочным объектом.

недостаток

  1. объект времени => строковая форма
  2. RegExp, Error => просто получить пустой объект
  3. функция,неопределенная => потеряна
  4. NaN, Infinity и -Infinity => сериализовать в ноль
  5. Объект генерируется конструктором => конструктор, который отбрасывает объект
  6. Глубокое копирование невозможно при наличии циклических ссылок

Вручную внедрить глубокое копирование (многослойное)

    function Judgetype(e) {
        return Object.prototype.toString.call(e).slice(8, -1).toLowerCase();
    }
    function Loop(param) {
        let target = null;
        if(Judgetype(param) === 'array') {
            target = [];
            for(let key of param.keys()){
                target[key] = Deep(param[key]);
            }
        } else {
            target = {};
            Object.keys(obj).forEach((val) => {
                target[key] = Deep(param[key]);
            })
        }
        return target;
    }
    function Deep(param) {
        //基本数据类型
        if(param === null || (typeof param !== 'object' && typeof param !== 'function')) {
            return param;
        }
        //函数
        if(typeof param === 'function') {   
            return new Function('return ' + param.toString())();
        }
        return Loop(param);
    }

3. Оценка типа данных (оценка типа, равенство и соответствие, неявное преобразование, равенство двух объектов)

  1. Типовое суждение

typeof

  • 无法区分 object, null 和 array
  • Возвращает правильные результаты для примитивных типов, кроме null.
  • Для ссылочных типов, кроме функции, все возвращаемые типы объектов
typeof(1) // number
typeof('tom') // string
typeof(undefined) // undefined
typeof(null) // object
typeof(true) // boolean
typeof(Symbol(1)) // symbol

typeof({a: 1}) // object
typeof(function () {}) // function
typeof([]) // object

instanceof

  • Определить, относится ли экземпляр к определенному типу
[] instanceof Array; // true
{} instanceof Object;// true

var a  = function (){}
a instanceof Function // true


Object.prototype.toString.call

  • Текущее лучшее решение

  • toString() — это метод-прототип Object, вызов которого по умолчанию возвращает [[Class]] текущего объекта. Это внутреннее свойство в формате [object Xxx] , где Xxx — тип объекта.

  • Для объектов Object вызов toString() напрямую возвращает [object Object]. Для других объектов его необходимо вызывать через call/apply, чтобы вернуть правильную информацию о типе.

  • 这里就是call的应用场景,巧妙利用call借用Object的方法实现类型的判断

function T(e) {
    return Object.prototype.toString.call(e).slice(8, -1).toLowerCase();
}
T(1) // number
T('a') // string
T(null) // null
T(undefined) // undefined
T(true) // boolean
T(Symbol(1)) // symbol

T({a: 1}) // object
T(function() {}) // function
T([]) // array
T(new Date()) // date
T(/at/) // RegExp
  1. равные и конгруэнтные
  • ==Нестрогое сравнение допускает преобразование типов
  • ===Строгое сравнение не допускает преобразования типов

Адрес хранится в стеке ссылочного типа данных, а содержимое хранится в куче.Даже если содержимое одинаковое, адрес отличается, поэтому они не равны.

const a = {c: 1}
const b = {c: 1}
a === b // false
  1. неявное преобразование
  • ==Могут быть преобразования типов не только при сравнении на равенство, но и при выполнении операций, которые мы называем неявными преобразованиями.

Логическое сравнение, сначала поворачивайте числа

true == 2 // false
||
1 == 2
// if(X)
var X = 10
if(X) // true
10 ==> true

// if(X == true)
if(X == true) // false
10 == true
|| 
10 == 1

Сравнивать числа со строками,字符串Перемена数字

0 == '' // true
||
0 == 0

1 == '1' // true
||
1 == 1


Сравнение равенства между объектными типами и примитивными типами


[2] == 2 // true
|| valueOf() // 调用valueOf() 取自身值
[2] == 2
|| toString() // 调用toString() 转字符串
"2" == 2
|| Number() // // 数字和字符串做比较,`字符串`转`数字`
2 == 2

резюме

js использует определенные операторы для преобразования типов, обычно +, ==

  1. во время операции
  • Дополнение существует строка и преобразует ее в строку
  • Умножить - Разделить - Вычесть Строки преобразуются в числа
  1. При сравнении на равенство
  • Логическое сравнение, сначала поворачивайте числа
  • Сравнивать числа со строками,字符串Перемена数字
  • Сравнение типов объектов для преобразования исходного типа

Внеклассные вопросы Осознайте a == 1 && a == 2 && a == 3

const a = {
  i: 1,
  toString: function () {
    return a.i++;
  }
}
a == 1 && a == 2 && a == 3
  1. Определить, равны ли два объекта
  • Адрес хранится в стеке ссылочного типа данных, а содержимое хранится в куче, даже если содержимое такое же, а адрес другой, поэтому===Эти двое все еще не равны при оценке
  • Но как мы можем судить об их равенстве, когда содержание справочных данных одинаково?
// 判断两者非对象返回
// 判断长度是否一致
// 判断key值是否相同
// 判断相应的key值里的对应的值是否相同

这里仅仅考虑 对象的值为object,array,number,undefined,null,string,boolean

关于一些特殊类型 `function date RegExp` 暂不考虑
function Judgetype(e) {
    return Object.prototype.toString.call(e).slice(8, -1).toLowerCase();
}
function Diff(s1, s2) {
    const j1 = Judgetype(s1);
    const j2 = Judgetype(s2);
    if(j1 !== j2){
        return false;
    }
    if(j1 === 'object') {
        if(Object.keys(s1).length !== Object.keys(s2).length){
            return false;
        }
        s1[Symbol.iterator] = function* (){
            let keys = Object.keys( this )
            for(let i = 0, l = keys.length; i < l; i++){
                yield {
                    key: keys[i],
                    value: this[keys[i]]
                };
            }
        }
        for(let {key, value} of s1){
            if(!Diff(s1[key], s2[key])) {
                return false
            }
        }
        return true
    } else if(j1 === 'array') {
        if(s1.length !== s2.length) {
            return false
        }
        for(let key of s1.keys()){
            if(!Diff(s1[key], s2[key])) {
                return false
            }
        }
        return true
    } else return s1 === s2
}

Diff( {a: 1, b: 2}, {a: 1, b: 3}) // false
Diff( {a: 1, b: [1,2]}, {a: 1, b: [1,3]}) // false

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

  • Объект для обхода должен самостоятельно добавлять итератор, что более хлопотно
    for(var key in s1) {
        if(!s1.hasOwnProperty(key)) {
            if(!Diff(s1[key], s2[key])) {
                return false
            }
        }
    }

4. Манипуляции с данными (обход массива, обход объекта)

1. Обход массива

`最普通 for循环` // 较为麻烦
for(let i = 0,len = arr.length; i < len; i++) {
    console.log(i, arr[i]);
}

`forEach` 无法 break return

`for in` 不适合遍历数组,

`for...in` 语句在w3c定义用于遍历数组或者对象的属性
1. index索引为字符串型数字,不能直接进行几何运算
2. 遍历顺序有可能不是按照实际数组的内部顺序
3. 使用for in会遍历数组所有的可枚举属性,包括原型

`for of` 无法获取下标

// for of 兼容1
for(let [index,elem] of new Map( arr.map( ( item, i ) => [ i, item ] ) )){
  console.log(index);
  console.log(elem);
}

// for of 兼容2
let arr = [1,2,3,4,5];
for(let key of arr.keys()){
    console.log(key, arr[key]);
}
for(let val of arr.values()){
    console.log(val);
}
for(let [key, val] of arr.entries()){
    console.log(key, val);
}

2. 

2. Обход объекта

  1. for in

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

var obj = {a:1, b: 2}
obj.__proto__.c = 3;
Object.prototype.d = 4
for(let val in obj) {
    console.log(val) // a,b,c,d
}
// 优化
for(let val in obj) {
    if(obj.hasOwnProperty(val)) { // 判断属性是存在于当前对象实例本身,而非原型上
        console.log(val) // a,b
    }   
}
  1. object.keys
var obj = { a:1, b: 2 }
Object.keys(obj).forEach((val) => {
    console.log(val, obj[val]); 
    // a 1
    // b 2
})
  1. for of
  • for-of можно использовать только для типов данных, которые предоставляют интерфейс Iterator.
  • Такие типы, как Array, предоставляются по умолчанию.
  • Мы можем добавить к объекту свойство Symbol.iterator
var obj = { a:1, b: 2 }
obj[Symbol.iterator] = function* (){
    let keys = Object.keys( this )
    for(let i = 0, l = keys.length; i < l; i++){
        yield {
            key: keys[i],
            value: this[keys[i]]
        };
    }
}

for(let {key, value} of obj){
    console.log( key, value );
    // a 1
    // b 2
}

5. Расчет данных (ошибка расчета)

  1. 0.1 + 0.2 = 0.30000000000000004
  • Все числа будут преобразованы в двоичные и рассчитаны побитно,
  • Десятичный, двоичный нельзя разделить на два, он будет бесконечно зацикливаться
  • js данные хранят 64-битные числа двойной точности с плавающей запятой, которые здесь повторяться не будут, лишнее будет перехвачено (из-за этого тоже большие ошибки вычисления числа и ограничения)
  • После добавления и обратного конвертирования будут ошибки
  1. Так как же сделать точный расчет?
  • Для десятичных дробей, у которых сами десятичные дроби не превышают количество цифр, хранящихся в js, они могут быть одновременно преобразованы в целые числа, а затем после вычисления преобразованы в десятичные дроби.
function getLen(n) {
    const str = n + '';
    const s1 = str.indexOf('.')
    if(s1) {
        return str.length - s1 - 1
    } else {
        return 0
    }  
}
function add(n1, n2) {
    const s1 = getLen(n1)
    const s2 = getLen(n2)
    const max = Math.max(s1, s2)
    return (n1 * Math.pow(10, max) + n2 * Math.pow(10, max)) / Math.pow(10, max)
}
add(11.2, 2.11) // 13.31
  • Для тех, которые превышают количество цифр хранения, преобразуйте его в массив, добавьте его побитно в обратном порядке, а если оно больше 10, склеивайте строки, чтобы получить значение
function add(a, b) {
    let i = a.length - 1;
    let j = b.length - 1;
    let carry = 0;
    let ret = '';
    while(i>=0|| j>=0) {
        let x = 0;
        let y = 0;
        let sum;
        if(i >= 0) {
            x = a[i] - '0';
            i--
        }
        if(j >=0) {
            y = b[j] - '0';
            j--;
        }
        sum = x + y + carry;
        if(sum >= 10) {
            carry = 1;
            sum -= 10;
        } else {
            carry = 0
        }
        ret = sum + ret;
    }
    if(carry) {
        ret = carry + ret;
    }
    return ret;
}
add('999999999999999999999999999999999999999999999999999999999999999', '1') 
//  1000000000000000000000000000000000000000000000000000000000000000

4. JS-приложение

1. Защита от сотрясения

Сцены:

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

определение:

  • Выполненные n секунд после того, как событие срабатывает, и событие срабатывает в течение n секунд события, в зависимости от того, что

Реализовать идею:

  1. Выполнение и сброс таймеров
  2. применить изменения к этому пункту
  3. применить наследование параметров
function debounce(func, wait) {
    var timeout;
    return function () {
        var context = this;
        var args = arguments;
        clearTimeout(timeout)
        timeout = setTimeout(function(){
            func.apply(context, args)
        }, wait);
    }
}

2. Дросселирование

Сцены:

  • Некоторые события могут запускаться реже.
  • Например, при ленивой загрузке нужно следить за положением полосы прокрутки вычислений, но ее не нужно срабатывать каждый раз при скольжении, что позволяет снизить частоту вычислений без траты ресурсов;

определение:

  • Непрерывно запускайте событие и выполняйте его только один раз в течение указанного времени.
  1. Способ 1: Отметка времени
  • Реализовать идею:
    • Время триггера теперь принимает текущую отметку времени минус отметку времени флага (начальное значение равно 0).
    • Если оно больше указанного времени, выполнить, и флаг обновится до текущего времени,
    • Если оно меньше указанного времени, оно не будет выполнено
function foo(func, wait) {
    var context, args;
    var flag = 0;
    return function () {
        var now = +new Date();
        context = this;
        args = arguments;
        if(now - flag > 0) {
            func.apply(context, args);
            flag = now; 
        }
    }
}
  1. Способ 2: Таймер
  • Реализовать идею:
    • Определить, есть ли таймер в данный момент,
    • Если нет, определите таймер, запустите его в указанное время и очистите таймер.
    • не выполнять
function foo(func,   wait) {
    var context, args;
    var timeout;
    return function() {
        if(!timeout){
            setTimeout(()=>{
                timeout = null;
                func.apply(context, args);
            }, wait) 
        }
    }
}

3. Карри

  1. Определение: Преобразует функцию, которая принимает несколько параметров, в функцию, которая принимает один параметр, и возвращает новую функцию, которая принимает оставшиеся параметры и возвращает результат.
  2. Функции:参数复用,提前返回,延迟执行
  3. Процесс реализации
  • Создайте функцию, используйте команду «Применить» и повторно передайте объединенные параметры каррированной функции.
  • Используйте сокращение для перебора всех элементов в массиве и создания окончательного возвращаемого значения.
function add(...args) {
	var fn = function(...args1) {
        return add.apply(this, [...args, ...args1]);
    }
    fn.toString = function() {
        return args.reduce(function(a, b) {
            return a + b;
        })
    }
    return fn;
}
add(1)(2)(3).toString(); // 6