Рукописная навигация по пути
- Реализовать новый оператор
- Реализовать JSON.stringify
- Реализовать JSON.parse
- осуществить вызов или подать заявку
- Реализовать функцию.bind
- осуществить наследование
- Реализовать каррирование функции JS
- Напишите обещание от руки (требуется для среднего и продвинутого уровня)
- Отказ от дребезга и дросселирование
- Написанная вручную глубокая копия JS
- Реализовать instanceOf
1. Реализуйтеnew
оператор
Источник: английская версия «JavaScript, которого вы не знаете».
new
Оператор делает следующие вещи:
- Он создает совершенно новый объект.
- это будет выполнено
[[Prototype]]
(то есть,__proto__
)Связь. - это делает
this
Указывает на вновь созданный объект. . - пройти через
new
Каждый созданный объект в конечном итоге будет[[Prototype]]
ссылка на эту функциюprototype
на объекте. - Если функция не возвращает тип объекта
Object
(ВключатьFunctoin, Array, Date, RegExg, Error
),Такnew
Вызов функции в выражении вернет ссылку на объект.
function New(func) {
var res = {};
if (func.prototype !== null) {
res.__proto__ = func.prototype;
}
var ret = func.apply(res, Array.prototype.slice.call(arguments, 1));
if ((typeof ret === "object" || typeof ret === "function") && ret !== null) {
return ret;
}
return res;
}
var obj = New(A, 1, 2);
// equals to
var obj = new A(1, 2);
2. РеализуйтеJSON.stringify
JSON.stringify(value[, replacer [, space]])
:
-
Boolean | Number| String
Типы автоматически преобразуются в соответствующие примитивные значения. -
undefined
, любую функцию иsymbol
, игнорируется (при наличии в значении свойства объекта, не являющегося массивом), или преобразуется вnull
(при наличии в массиве). - Неперечислимые свойства игнорируются
- Если значение свойства объекта косвенным образом указывает на сам объект (циклическая ссылка), свойство также игнорируется.
function jsonStringify(obj) {
let type = typeof obj;
if (type !== "object") {
if (/string|undefined|function/.test(type)) {
obj = '"' + obj + '"';
}
return String(obj);
} else {
let json = []
let arr = Array.isArray(obj)
for (let k in obj) {
let v = obj[k];
let type = typeof v;
if (/string|undefined|function/.test(type)) {
v = '"' + v + '"';
} else if (type === "object") {
v = jsonStringify(v);
}
json.push((arr ? "" : '"' + k + '":') + String(v));
}
return (arr ? "[" : "{") + String(json) + (arr ? "]" : "}")
}
}
jsonStringify({x : 5}) // "{"x":5}"
jsonStringify([1, "false", false]) // "[1,"false",false]"
jsonStringify({b: undefined}) // "{"b":"undefined"}"
3. РеализуйтеJSON.parse
JSON.parse(text[, reviver])
Используется для разбора строк JSON и создания значений или объектов JavaScript, описываемых строками. Предусмотрена дополнительная функция оживления для выполнения преобразований (операций) над результирующим объектом перед возвратом.
3.1 Первый тип: прямой вызов eval
function jsonParse(opt) {
return eval('(' + opt + ')');
}
jsonParse(jsonStringify({x : 5}))
// Object { x: 5}
jsonParse(jsonStringify([1, "false", false]))
// [1, "false", falsr]
jsonParse(jsonStringify({b: undefined}))
// Object { b: "undefined"}
Избегайте использования в ненужных ситуациях
eval
, eval() — опасная функция, код, который он выполняет, имеет права исполнителя. Если строковый код, который вы запускаете с помощью eval(), манипулируется злоумышленником (кто-то с плохими намерениями), вы можете в конечном итоге запустить вредоносный код на компьютере пользователя с разрешениями вашей веб-страницы/расширения.
Он будет выполнять код JS и имеет уязвимость XSS.
Если вы хотите запомнить только этот метод, вам нужно проверить параметр json.
var rx_one = /^[\],:{}\s]*$/;
var rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g;
var rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;
var rx_four = /(?:^|:|,)(?:\s*\[)+/g;
if (
rx_one.test(
json
.replace(rx_two, "@")
.replace(rx_three, "]")
.replace(rx_four, "")
)
) {
var obj = eval("(" +json + ")");
}
3.2 Второй тип: Функция
источникMagic eval() и новая функция()
основной:Function
а такжеeval
Имеет ту же функцию строкового параметра.
var func = new Function(arg1, arg2, ..., functionBody);
В практических приложениях преобразования JSON это все, что нужно.
var jsonStr = '{ "age": 20, "name": "jack" }'
var json = (new Function('return ' + jsonStr))();
eval
а такжеFunction
Оба имеют функцию динамической компиляции js-кода, но в реальном программировании это не рекомендуется.
Вот программирование, ориентированное на интервью, и достаточно написать эти два. Что касается третьего и четвертого типов, то они включают в себя утомительную рекурсию и принципы, связанные с конечным автоматом.
4. Реализуйтеcall
илиapply
Реализовать адаптацию Источник:Углубленный вызов JavaScript и реализация имитационного моделирования #11
call
грамматика:
fun.call(thisArg, arg1, arg2, ...)
, который вызывает функцию с указанным значением this и отдельно предоставленными аргументами (списком аргументов).
apply
грамматика:
func.apply(thisArg, [argsArray])
, вызывая функцию с аргументами, предоставленными в виде массива (или массивоподобного объекта).
4.1 Function.call
Реализовать по рутине
call
основной:
- Сделать функцию свойство объекта
- выполнить и удалить эту функцию
- уточнить
this
в функцию и передать заданные параметры для выполнения функции - Если параметр не передается, точкой по умолчанию является окно
Почему ты говоришь, что это трюк? Потому что на настоящем собеседовании интервьюер любит, чтобы вы постепенно глубоко задумались, а в это время вы можете изменить процедуру и сначала написать простую версию:
4.1.1 Простая версия
var foo = {
value: 1,
bar: function() {
console.log(this.value)
}
}
foo.bar() // 1
4.1.2 Улучшенная версия
Когда у интервьюера появятся дополнительные вопросы, или в этот момент вы можете сделать вид, что думаете об этом. Затем запишите следующую версию:
Function.prototype.call2 = function(content = window) {
content.fn = this;
let args = [...arguments].slice(1);
let result = content.fn(...args);
delete content.fn;
return result;
}
let foo = {
value: 1
}
function bar(name, age) {
console.log(name)
console.log(age)
console.log(this.value);
}
bar.call2(foo, 'black', '18') // black 18 1
4.2 Function.apply
Имитационная реализация
apply()
реализация иcall()
Аналогично, отличается только форма параметра. Вставьте код напрямую:
Function.prototype.apply2 = function(context = window) {
context.fn = this
let result;
// 判断是否有第二个参数
if(arguments[1]) {
result = context.fn(...arguments[1])
} else {
result = context.fn()
}
delete context.fn
return result
}
5. РеализуйтеFunction.bind()
bind()
метод:
Создаст новую функцию. Когда эта новая функция вызывается, первый параметр bind() будет использоваться в качестве ЭТОГО, который она запускает, а последующие параметры последовательности будут включены в качестве ее параметров перед передачей. (из МДН)
также,bind
Реализации должны учитывать влияние на цепочку прототипов после создания экземпляра.
Function.prototype.bind2 = function(content) {
if(typeof this != "function") {
throw Error("not a function")
}
// 若没问参数类型则从这开始写
let fn = this;
let args = [...arguments].slice(1);
let resFn = function() {
return fn.apply(this instanceof resFn ? this : content,args.concat(...arguments) )
}
function tmp() {}
tmp.prototype = this.prototype;
resFn.prototype = new tmp();
return resFn;
}
6. Реализовать наследование
Паразитическая композиционная наследственность
Обычно рекомендуется писать только это, потому что другие методы наследования будут вызывать конструктор родительского класса дважды в одном экземпляре или иметь другие недостатки.
Основная реализация:использовать одинF
Пустой конструктор для замены выполненияParent
этот конструктор.
function Parent(name) {
this.name = name;
}
Parent.prototype.sayName = function() {
console.log('parent name:', this.name);
}
function Child(name, parentName) {
Parent.call(this, parentName);
this.name = name;
}
function create(proto) {
function F(){}
F.prototype = proto;
return new F();
}
Child.prototype = create(Parent.prototype);
Child.prototype.sayName = function() {
console.log('child name:', this.name);
}
Child.prototype.constructor = Child;
var parent = new Parent('father');
parent.sayName(); // parent name: father
var child = new Child('son', 'father');
7. Реализуйте каррирование JS-функции
Что такое карри?В компьютерных науках каррирование — это преобразование функции, которая принимает несколько аргументов, в функцию, которая принимает один аргумент (первый аргумент исходной функции) и возвращает новую функцию, которая принимает оставшиеся аргументы и возвращает результат Технология.
Основными функциями и особенностями каррирования функций являются повторное использование параметров, ранний возврат и отложенное выполнение.
7.1 Общая версия
function curry(fn, args) {
var length = fn.length;
var args = args || [];
return function(){
newArgs = args.concat(Array.prototype.slice.call(arguments));
if (newArgs.length < length) {
return curry.call(this,fn,newArgs);
}else{
return fn.apply(this,newArgs);
}
}
}
function multiFn(a, b, c) {
return a * b * c;
}
var multi = curry(multiFn);
multi(2)(3)(4);
multi(2,3,4);
multi(2)(3,4);
multi(2,3)(4);
7.2 ES6
Сан письмо
const curry = (fn, arr = []) => (...args) => (
arg => arg.length === fn.length
? fn(...arg)
: curry(fn, arg)
)([...arr, ...args])
let curryTest=curry((a,b,c,d)=>a+b+c+d)
curryTest(1,2,3)(4) //返回10
curryTest(1,2)(4)(3) //返回10
curryTest(1,2)(3,4) //返回10
8. Напишите от рукиPromise
(Требуется для среднего и продвинутого уровня)
мы пришлиPromise/A+
Технические характеристики:
- три состояния
pending| fulfilled(resolved) | rejected
- когда в
pending
состояние, может быть переведено вfulfilled(resolved)
илиrejected
условие - когда в
fulfilled(resolved)
статус илиrejected
состояние неизменно.
- должен иметь один
then
выполнять методы асинхронно,then
Принимает два аргумента и должен возвращать обещание:
// onFulfilled 用来接收promise成功的值
// onRejected 用来接收promise失败的原因
promise1=promise.then(onFulfilled, onRejected);
8.1 Promise
Блок-схема анализа
пересматриватьPromise
Применение:
var promise = new Promise((resolve,reject) => {
if (操作成功) {
resolve(value)
} else {
reject(error)
}
})
promise.then(function (value) {
// success
},function (value) {
// failure
})
8.2 Интервью достаточно
источник:Реализуйте обещание, которое полностью соответствует спецификации Promise/A+.
function myPromise(constructor){
let self=this;
self.status="pending" //定义状态改变前的初始状态
self.value=undefined;//定义状态为resolved的时候的状态
self.reason=undefined;//定义状态为rejected的时候的状态
function resolve(value){
//两个==="pending",保证了状态的改变是不可逆的
if(self.status==="pending"){
self.value=value;
self.status="resolved";
}
}
function reject(reason){
//两个==="pending",保证了状态的改变是不可逆的
if(self.status==="pending"){
self.reason=reason;
self.status="rejected";
}
}
//捕获构造异常
try{
constructor(resolve,reject);
}catch(e){
reject(e);
}
}
В то же время необходимоmyPromise
Сцепленные вызовы определены на прототипеthen
метод:
myPromise.prototype.then=function(onFullfilled,onRejected){
let self=this;
switch(self.status){
case "resolved":
onFullfilled(self.value);
break;
case "rejected":
onRejected(self.reason);
break;
default:
}
}
есть тест:
var p=new myPromise(function(resolve,reject){resolve(1)});
p.then(function(x){console.log(x)})
//输出1
8.3 Специальное издание для крупных производителей
Разместите это прямо, эту версию довольно легко понять
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
function Promise(excutor) {
let that = this; // 缓存当前promise实例对象
that.status = PENDING; // 初始状态
that.value = undefined; // fulfilled状态时 返回的信息
that.reason = undefined; // rejected状态时 拒绝的原因
that.onFulfilledCallbacks = []; // 存储fulfilled状态对应的onFulfilled函数
that.onRejectedCallbacks = []; // 存储rejected状态对应的onRejected函数
function resolve(value) { // value成功态时接收的终值
if(value instanceof Promise) {
return value.then(resolve, reject);
}
// 实践中要确保 onFulfilled 和 onRejected 方法异步执行,且应该在 then 方法被调用的那一轮事件循环之后的新执行栈中执行。
setTimeout(() => {
// 调用resolve 回调对应onFulfilled函数
if (that.status === PENDING) {
// 只能由pending状态 => fulfilled状态 (避免调用多次resolve reject)
that.status = FULFILLED;
that.value = value;
that.onFulfilledCallbacks.forEach(cb => cb(that.value));
}
});
}
function reject(reason) { // reason失败态时接收的拒因
setTimeout(() => {
// 调用reject 回调对应onRejected函数
if (that.status === PENDING) {
// 只能由pending状态 => rejected状态 (避免调用多次resolve reject)
that.status = REJECTED;
that.reason = reason;
that.onRejectedCallbacks.forEach(cb => cb(that.reason));
}
});
}
// 捕获在excutor执行器中抛出的异常
// new Promise((resolve, reject) => {
// throw new Error('error in excutor')
// })
try {
excutor(resolve, reject);
} catch (e) {
reject(e);
}
}
Promise.prototype.then = function(onFulfilled, onRejected) {
const that = this;
let newPromise;
// 处理参数默认值 保证参数后续能够继续执行
onFulfilled =
typeof onFulfilled === "function" ? onFulfilled : value => value;
onRejected =
typeof onRejected === "function" ? onRejected : reason => {
throw reason;
};
if (that.status === FULFILLED) { // 成功态
return newPromise = new Promise((resolve, reject) => {
setTimeout(() => {
try{
let x = onFulfilled(that.value);
resolvePromise(newPromise, x, resolve, reject); // 新的promise resolve 上一个onFulfilled的返回值
} catch(e) {
reject(e); // 捕获前面onFulfilled中抛出的异常 then(onFulfilled, onRejected);
}
});
})
}
if (that.status === REJECTED) { // 失败态
return newPromise = new Promise((resolve, reject) => {
setTimeout(() => {
try {
let x = onRejected(that.reason);
resolvePromise(newPromise, x, resolve, reject);
} catch(e) {
reject(e);
}
});
});
}
if (that.status === PENDING) { // 等待态
// 当异步调用resolve/rejected时 将onFulfilled/onRejected收集暂存到集合中
return newPromise = new Promise((resolve, reject) => {
that.onFulfilledCallbacks.push((value) => {
try {
let x = onFulfilled(value);
resolvePromise(newPromise, x, resolve, reject);
} catch(e) {
reject(e);
}
});
that.onRejectedCallbacks.push((reason) => {
try {
let x = onRejected(reason);
resolvePromise(newPromise, x, resolve, reject);
} catch(e) {
reject(e);
}
});
});
}
};
эммм, я лучше послушно отпишусь на продвинутую версию.
9. Стабилизация почерка (Debouncing
) и троттлинг (Throttling
)
scroll
Само событие запускает повторный рендеринг страницы, в то время какscroll
мероприятиеhandler
будет срабатывать с высокой частотой, поэтому событиеhandler
Внутри не должно быть сложных операций, таких какDOM
Действия не должны размещаться в обработчиках событий. Для таких часто возникающих проблем с событиями запуска (таких как страницаscroll
,Экранresize
, прослушивание пользовательского ввода и т. д.), есть два распространенных решения: защита от сотрясений и дросселирование.
9.1 Защита от сотрясений (Debouncing
)выполнить
Типичный пример: ограничение щелчков мышью для срабатывания.
Лучшее объяснение:
Когда происходит событие, обработчик событий ожидает определенное пороговое время, и если по истечении этого периода времени не происходит никаких событий, он обрабатывает последнее событие. Предполагая, что не так хорошо
0.01
Достигнуто указанное время в секундах, и приходит другое событие, тогда предыдущее ожидание недействительно, и нужно снова ждать указанное время.
// 防抖动函数
function debounce(fn,wait=50,immediate) {
let timer;
return function() {
if(immediate) {
fn.apply(this,arguments)
}
if(timer) clearTimeout(timer)
timer = setTimeout(()=> {
fn.apply(this,arguments)
},wait)
}
}
Пример комбинации: антивибрационная катушка
// 简单的防抖动函数
// 实际想绑定在 scroll 事件上的 handler
function realFunc(){
console.log("Success");
}
// 采用了防抖动
window.addEventListener('scroll',debounce(realFunc,500));
// 没采用防抖动
window.addEventListener('scroll',realFunc);
9.2 Дросселирование (Throttling
)выполнить
Можно понять, что событие передается в конвейере, и после добавления этого дросселя скорость потока события замедлится. На самом деле функция этой функции такова, она может ограничивать частоту вызова функции определенным порогом, например 1с, тогда эта функция не будет вызываться дважды в течение 1с.
Простая функция дроссельной заслонки:
function throttle(fn, wait) {
let prev = new Date();
return function() {
const args = arguments;
const now = new Date();
if (now - prev > wait) {
fn.apply(this, args);
prev = new Date();
}
}
9.3 Объединение практики
Режим переключается третьим параметром.
const throttle = function(fn, delay, isDebounce) {
let timer
let lastCall = 0
return function (...args) {
if (isDebounce) {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
fn(...args)
}, delay)
} else {
const now = new Date().getTime()
if (now - lastCall < delay) return
lastCall = now
fn(...args)
}
}
}
10. Напишите глубокую копию JS вручную
Существует самая известная реализация версии для нищих, которая также упоминается в «JavaScript, которого вы не знаете (часть 1)»:
10.1 Издание для нищих
var newObj = JSON.parse( JSON.stringify( someObj ) );
10.2 Достаточно интервью
function deepCopy(obj){
//判断是否是简单数据类型,
if(typeof obj == "object"){
//复杂数据类型
var result = obj.constructor == Array ? [] : {};
for(let i in obj){
result[i] = typeof obj[i] == "object" ? deepCopy(obj[i]) : obj[i];
}
}else {
//简单数据类型 直接 == 赋值
var result = obj;
}
return result;
}
Дискуссии о глубоком копировании идут каждый день, я выложу здесь два, ведь я...
11. Реализуйте одинinstanceOf
function instanceOf(left,right) {
let proto = left.__proto__;
let prototype = right.prototype
while(true) {
if(proto === null) return false
if(proto === prototype) return true
proto = proto.__proto__;
}
}
Прочитав три вещи ❤️
Если вы найдете этот контент вдохновляющим, я хотел бы пригласить вас сделать мне три небольших одолжения:
- Ставьте лайк, чтобы больше людей увидело этот контент
- Обратите внимание на паблик «Учитель фронтенд-убеждения», и время от времени делитесь оригинальными знаниями.
- Также смотрите другие статьи
- Те шаблоны проектирования, которые вы используете непреднамеренно (1) - шаблоны создания
- «Король библиотеки визуализации данных» D3.js Быстрое начало работы с приложением Vue
- Руководство "True® Full Stack Road" для веб-интерфейсной разработки
- «Практика Vue» — плагин Vue CLI за 5 минут
- Вооружите свой интерфейсный проект «Практикой Vue»
- «Intermediate and Advanced Front-End Interview» JavaScript Рукописный код Invincible Cheats
- «Узнайте из исходного кода» ответы на вопросы Vue, которые интервьюеры не знают
- JS-операция «Узнать из исходного кода» в исходном коде Vue
- Правильная позиция для обновления vue-cli3 в проекте "Vue Practice"
- Почему вы до сих пор не можете понять цепочку областей видимости JavaScript?
Вы также можете прийти ко мнеGitHub
Получите исходные файлы всех статей в блоге:
Руководство по убеждению:GitHub.com/Roger-Hi RO/…