Введение
В этой статье кратко изложены основные сведения, такие как типы js и написанные от руки функции. Цель состоит в том, чтобы лучше обобщить систему знаний js и помочь вам лучше освоить язык js. Количество слов может быть немного большим, но, пожалуйста, усваивайте медленно. Пожалуйста. укажите на плохое написание или не туда, автору удобно доработать
Подходя к собеседованию, я хочу систематически пройтись по полученным ранее знаниям, и они глубоко отложились в моем сознании, и у меня есть эта статья почти в 10 000 слов.
1.js тип
Прежде всего, сейчас существуют следующие типы js:
基本类型
: String , Number , null , undefined , Boolean , Symbol , Bigint引用类型
: object , function
(1) Разница между базовым типом и типом приложения
基本数据类型是按值访问的,因为可以直接操作保存在变量中的实际值。示例:
var a = 10;
var b = a;
b = 15;
console.log(a)//输出10,这说明b只是保存了a复制的一个副本。所以,b的改变,对a没有影响。
如图:
javascript的引用数据类型是保存在堆内存中的对象。实例:
var obj1 = {};
var obj2 = obj1;
obj2.name = "小白";
console.log(obj1.name); // 小白
如图:
(2) Тип преобразования
-
1. 8 можно преобразовать значение false:
NaN,'',0,-0,null,undefined,0n,false
-
2.Number([value]) преобразует другие типы данных в числовой тип моего числа
- Преобразование строки в число: пустая строка равна 0, если в строке появляется какой-либо недопустимый числовой символ, результатом будет NaN
- Преобразование логического значения в число: true=>1 false=>0
- null=>0 undefined=>NaN
- символ не может быть преобразован в число, сообщается об ошибке
- bigInt можно преобразовать в число
- Тип ссылки (объект или функция)
- Сначала получите значение свойства [Symbol.toPrimitive]
- Если такого атрибута нет, во-вторых, получить его valueOf
- Если исходного значения по-прежнему нет, преобразуйте его в строку toString, а затем преобразуйте в число Число
-
3.parseInt/parseFloat([значение]) Преобразование других типов данных в числовые типы
- Необходимо убедиться, что [value] является строкой, если нет, сначала неявно преобразовать ее в строку [value].toString()
- Начинайте поиск с первого символа в левой части строки вправо, и конвертируйте найденные допустимые числовые символы в числа (если встречается недопустимый числовой символ, поиск будет остановлен, и независимо от того, есть ли допустимые номера позади, больше не будут искать)
- Если ни один из допустимых числовых символов не найден, результатом будет NaN.
(3) Определение типа
Для определения типа ознакомьтесь с моей предыдущей статьей:js четыре типа методов обнаружения
(4) Тест вопросов
- 1. вычисление parseInt
Прежде всего, этот вопрос проверяет синтаксис parseInt, а именно parseInt ([строка], [основание] необязательный), первый параметр — это значение для анализа. Если аргумент не является строкой, преобразуйте его в строку (используя абстрактную операцию ToString). Пробелы в начале строки игнорируются. Основание второго параметра представляет собой основание.
let arr = [27.2,0,'0013','14px',123];
arr = arr.map(parseInt);
console.log(arr);
//这里的map函数中的结构为
item index
27.2 0
0 1
'0013' 2
'14px' 3
123 4
由于radix中只接受2-16进制,因此进制为1的直接转化为NaN,
进制运算举例:
parseInt('14px',3),首先取出14,然后计算3进制,由于1和4中,4不满足3进制,因此只有1,所以得出1*3^0
因此结果为[27, NaN, 1, 1, 27]
2. О замыканиях
- (1) О закрытиях
Что касается замыканий, согласно 3-му изданию Красной книги: Замыкание — это функция, которая имеет доступ к переменной в области видимости другой функции.
总结:
• Выполнение функции сформирует частный контекст.Если некоторый контент в контексте (обычно относится к адресу кучи памяти) занят чем-то вне контекста (например, переменными, привязками событий), текущий контекст не может быть извлечен из стека. (определяется механизмом сборки мусора браузера GC) > => Механизм закрытия: сформировать контекст, который не освобождается; • Защита: защита частных переменных в частном контексте и во внешнем мире от влияния друг на друга> • Сохранить: если контекст не освобожден, частные переменные и значения в контексте будут сохранены и могут использоваться подчиненными контекстами. • Недостатки: Если вы используете много замыканий, память стека будет слишком велика, рендеринг страниц будет медленным, а производительность пострадает, поэтому вам действительно нужно разумно применять замыкания в проекте, некоторые коды приведут к переполнению стека. или утечки памяти, эти операции — все, что требует нашего внимания.
- (2) Использование замыканий
1. Решить проблему с петлей
用var
var li = document.querySelectorAll("li");
for(var i=0;i<li.length;i++){
li[i].onclick= function(){
console.log(i);//显示结果都为3
}
}
使用闭包
for(var i=0;i<li.length;i++){
(function(i){
li[i].onclick= function(){
console.log(i) //输出0,1,2
}
})(i)
}
使用let
for(let i=0;i<li.length;i++){
li[i].onclick= function(){
console.log(i);//显示结果为0,1,2
}
}
2.составить функцию
Когда мы встречаем const fn1 = x => x * 2, fn2 = x => x + 3, fn3 = x => x-1, вы хотите подключиться к той же функции обработки данных, что и конвейер, а затем позволить данным через результаты конца канала, если не через обработку функции компоновки, это fn3 (fn2 (fn1 (x))), это выглядит очень непривлекательно. Следовательно, мы можем построить композицию функции, которая принимает произвольную функцию от множества параметров (эти функции принимают только параметр)
function compose(...args){
return function(x){
let len = args.length;
if(args == 0) return x;
if(len == 1) return args[0](x);
return args.reduceRight((pre,cur)=>{
return cur(pre)
},x)
}
}
const fn4 = compose(fn1,fn2,fn3);
console.log(fn4(5))
redux中compose源码:
function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg
}
if (funcs.length === 1) {
return funcs[0]
}
return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
3. Понимание цепочки прототипов
О прототипе:
- 1. Все ссылочные типы имеют свойство __proto__ (неявный прототип), а значением свойства является обычный объект
- 2. Все функции имеют свойство прототип (prototype), а значением свойства является обычный объект
- 3. Свойство __proto__ всех ссылочных типов указывает на прототип своего конструктора
如
function Person(name){this.name = name}
let p1 = new Person("小白");
console.dir(p1)
console.log(p1.__proto__ == Person.prototype)//true
console.log(Person.prototype.constructor == Person)//true
由此我们可以通过类型.prototype去看他们的API,可以非常清楚的学习到方法的使用,这个可以作为当你忘记api时及时查找的方法
Про цепочку прототипов: при обращении к свойству объекта сначала будет искать свойство самого объекта, если не найдет, то пойдет к своему __proto__ неявному прототипу, чтобы найти его, то есть прототип своего Если он не найден, то он будет искаться в __proto__ прототипа конструктора, так что послойный поиск сформирует структуру цепочки, которую мы называем цепочкой прототипов.
看Person函数的原型链:
Из рисунка известно, что экземпляр person найдет Person.prototype через атрибут __proto__, а затем будет искать от Person.prototype.__proto__ до нуля, таким образом формируя очевидную длинную цепочку.
4. Рукописные функции
(1) новый
new原理:
- 1. Новое ключевое слово сначала создаст пустой объект
- 2. Указать объект-прототип этого пустого объекта на свойство прототипа конструктора, тем самым наследуя методы прототипа
- 3. Укажите this на этот пустой объект и выполните код в конструкторе, чтобы получить приватные свойства
- 4. Если конструктор возвращает объект res, вернуть возвращаемое значение res, если возвращаемое значение не является объектом, вернуть созданный объект
ES5写法
function _new(target){
var obj = {},
params = [].splice.call(arguments,1),
result;
obj.__proto__ = target.prototype;
result = target.apply(obj,params);
if(result!=null && /(function|object)/.test(typeof result)){
return result;
}
return obj;
}
ES6写法
function _new(target,...params){
let obj = Object.create(target.prototype),
result = target.call(obj,...params);
if(result!=null && /^(function|object)$/.test(typeof result)){
return result;
}
return obj;
}
(2) звонок
原理
- Установите атрибут для CONTEXT (имя атрибута должно быть как можно более уникальным, чтобы избежать изменения структуры в объекте по умолчанию с помощью атрибутов, которые мы установили сами, например, это может быть реализовано на основе Symbol, или может быть создано имя временной метки) , значением атрибута должна быть функция, которую мы хотим выполнить (то есть THIS, THIS в CALL — это функция, с которой мы хотим работать)
- Затем получите доступ к методу выполнения на основе члена CONTEXT.XXX(), вы можете выполнить функцию и изменить ЭТО внутри (вы также можете передать информацию в PARAMS этой функции); все обработано, не забудьте установить КОНТЕКСТ Удалите этот атрибут (вы сами не добавили его раньше, нам нужно удалить его после добавления)
Function.prototype.call = function(context,...params){
let key = Symbol('key'),//设置唯一值
result;
!/^(object|function)$/.test(typeof context) ? context = Object(context) :null;
context !=null ? null : context = window;//如果context为null或者undefined,直接赋值为window
context[key] = this;
result = context[key](...params);//返回值
delete context[key];
return result;
}
(3) применить
原理:
- В основном то же самое, что и вызов, но следующие параметры должны быть массивами
Function.prototype.apply = function(context,params = []){
let key = Symbol('key'),
result;
!/^(object|function)$/.test(typeof context) ? context = Object(context) :null;
context !=null ? null : context = window;
context[key] = this;
result = context[key](...params);
delete context[key];
return result;
}
(4) связать
原理:
- Функция bind() создает новую функцию (называемую функцией связывания) с тем же телом функции (атрибут call, встроенный в спецификацию ECMAScript 5), что и вызываемая функция (целевая функция функции связывания).
- Когда целевая функция вызывается, это значение привязывается к первому параметру bind() , который не может быть переопределен. Когда вызывается функция связывания, bind() также принимает параметры по умолчанию, предоставленные исходной функции.
- Связанная функция также может создавать объекты с помощью оператора new: он ведет себя как исходная функция в качестве конструктора. Предоставленное это значение игнорируется, а аргументы во время вызова передаются фиктивной функции.
Function.prototype.bind = function(context,...params){
let self = this;
return funtion(...innerArgs){
params = params.concat(...innerArgs);
return self.call(context,...params);
}
}
(5) Защита от сотрясений
思路:
- После завершения текущего клика мы ждем такое долгое время, чтобы увидеть, сработает ли второй раз.Если второй раз не срабатывает, это нечастая операция.Мы непосредственно выполняем функцию func, которую мы хотим выполнить : если второй раз срабатывает раз, то предыдущие не учитываются, а текущий начнет ждать...
/*
防抖:
@params:
func[function]:最后要触发执行的函数
wait[number]:频繁设定的界限
immediate[boolean]:默认多次操作,我们识别的是最后一次,但是immediate=true,让其识别第一次
@return
可以被调用执行的函数
*/
function debounce(func,wait = 300,immediate = false){
let timer = null;
return function anonymous(...params){
let now = immediate && !timer;
//每次点击都把之前设置的定时器清除掉
clearInterval(timer)
//重新设置一个新的定时器监听wait事件内是否触发第二次
timer = setTimeout(() => {
timer = null;//垃圾回收机制
//wait这么久的等待中,没有触发第二次
!immediate ? func.call(this,...params) : null;
}, wait);
//如果是立即执行
now ? func.call(this,...params) : null;
}
}
(6) Дросселирование
思路:
- Регулировка функции: при частой работе может срабатывать несколько раз, но частота срабатывания задается сама
/*
@params:
func[function]:最后要触发执行的函数
wait[number]:触发的频率
@return
可以被调用执行的函数
*/
function throttle(func,wait = 300){
let timer = null,
previous = 0;//记录上一次操作时间
return function anonymouse(...params){
let now = new Date(),//记录当前时间
remaining = wait - (now - previous);//记录还差多久达到我们一次触发的频率
if(remaining <= 0){
//两次操作的间隔时间已经超过wait了
window.clearInterval(timer);
timer = null;
previous = now;
func.call(this,...params);
}else if(!timer){
//两次操作的间隔时间还不符合触发的频率
timer = setTimeout(() => {
timer = null;
previous = new Date();
func.call(this,...params);
}, remaining);
}
}
}
(7) Поверхностное копирование
数组的浅拷贝:
- 1. Метод среза массива
let arr = [1,2,3]
let newArr = arr.slice();
- 2.concat
let arr = [1,2,3]
let newArr = arr.concat();
对象浅拷贝
[注意]这里的toType请参考我写的jquery工具方法,链接:
метод инструмента jquery
function clone(obj){
var type = toType(obj);
if(/^(boolean|number|string|null|undefined|bigInt|symbol)$/.test(type)) return obj;
if(/^(regExp|date)$/.test(type)) return new obj.constructor(obj);
if(/^(function)$/.test(obj)) return function(){
obj()
};
if(/^error$/.test(type))return new obj.constructor(obj.message);
var target = {},
keys = this.getOwnProperty(obj);
Array.isArray(target) ? target = [] : null;
keys.forEach(item=>{
target[item] = obj[item]
})
return target
}
(8) Глубокое копирование
- Используйте JSON.stringify+JSON.parse
[注意]这个方法有缺陷:
- Обычный будет рассматриваться как пустой объект
- Те, у кого значения функции/символа/неопределенного свойства напрямую убиваются
- BigInt пока не может с этим справиться, он сообщит об ошибке
- Объект даты становится строкой
let obj = {
a:1,
b:/^$/,
c:undefined,
d:new Date()
}
console.log(JSON.parse(JSON.stringify(obj)));
如图:
手写深克隆
function deepClone(obj,cache = new Set()){//深克隆 cache处理self属性,防止死递归
//只有数组和对象我们再处理深克隆,其余的按照浅克隆
let type = toType(obj);
if(!/^(array|object)$/.test(type)) return this.clone(obj);
if(cache.has(obj)) return obj;
cache.add(obj);
let keys = this.getOwnProperty(obj),
clone = {};
type === "array" ? clone = [] : null;
keys.forEach(key=>{
clone[key] = this.deepClone(obj[key],cache)
})
return clone
}
(9) экземпляр
function _instanceof(L,R){
// 验证如果为基本数据类型,就直接返回false
const baseType = ['string', 'number','boolean','undefined','symbol']
if(baseType.includes(typeof(L))) { return false }
let RP = R.prototype; //取 R 的显示原型
L = L.__proto__; //取 L 的隐式原型
while(true){ // 无线循环的写法(也可以使 for(;;) )
if(L === null){ //找到最顶层
return false;
}
if(L === RP){ //严格相等
return true;
}
L = L.__proto__; //没找到继续向上一层原型链查找
}
}
(10) Сглаживание массива
flat方法实现
let arr = [1,2,[3,4,[5,[6]]]]
console.log(arr.flat(Infinity))//flat参数为指定要提取嵌套数组的结构深度,默认值为 1
reduce实现
function fn(arr){
return arr.reduce((prev,cur)=>{
return prev.concat(Array.isArray(cur)?fn(cur):cur)
},[])
}
5. Говоря о событиях js
Что касается событий, мы думаем, что события — это поведение, которое может быть обнаружено с помощью Javascript.С точки зрения непрофессионала, когда пользователь взаимодействует с веб-страницей, интерпретатор создает реагирующий объект события для описания информации о событии.
【注意:事件并不是你自己添加才有的,而是浏览器自带的】
(1).Привязка события
- Определяйте события непосредственно в html
<button onclick="console.log(1)">点我</button>
缺点:违反了内容与行为相分离的原则,在html中写js代码,强耦合,不利于复用代码。不推荐
- Событие уровня DOM0
let btn = document.querySelector(".btn");
btn.onclick = function(){
console.log(1);
}
缺点:解决了强耦合性,但只能给一个事件对象绑定一个事件类型。 同样的事件类型,绑定两次,以 后一次 为准。
-События уровня DOM2
同样的事件类型,绑定多次,每个都会执行,可以设置时捕获还是冒泡。
let btn = document.querySelector(".btn");
btn.addEventListener("click",function(){
console.log(1);
},false)//默认值为false,可不写,false表示冒泡
Пузырь и захват
事件冒泡
То есть событие вначале принимается наиболее специфичным элементом, а затем распространяется на менее специфичные узлы,事件捕获
Идея состоит в том, что менее конкретные узлы DOM должны получать события ранее, в то время как большинство специфических узлов должны получать события последнего
事件冒泡
<div class="my">click me</div>
如果你点击了页面中的<div>元素,那么这个click事件会按照如下顺序传播:
1.<div>
2.<body>
3.<html>
4.document
事件捕获
<div class="my">click me</div>
如果你点击了页面中的<div>元素,那么这个click事件会按照如下顺序传播:
1.document
2.<html>
3.<body>
4.<div>
поток событий
Поток событий состоит из трех этапов: этап захвата события, этап захвата, этап всплытия события.
以前面的html为例,如图所示:
7. Окончательное резюме
Наконец-то я закончил писать эту статью.Хотя писать статью немного утомительно, печатать по одному слову, да и ягодицы немного болят, но наконец-то я могу закончить статью, а у меня большой опыт. Когда я писал эту статью, я действительно пересматривал знания, которые не усвоил крепко, и мне не хотелось их писать. Изначально, когда писал, хотел добавить знания про http, но обнаружил, что мои знания http недостаточно сильны сейчас, поэтому не буду безобразничать.Автор теперь готов восполнить эти знания, а я буду почувствуй это позже.Чуть не напиши статью про http независимость! Наконец, спасибо вам, читатели, я надеюсь, что вы можете извлечь что-то из этой статьи, и вы можете оставить комментарий ниже и обсудить знания вместе.
如果你喜欢该文章,请各位师哥美女动动你的拇指点个赞
Использованная литература: