«Intermediate and Advanced Front-End Interview» JavaScript Рукописный код Invincible Cheats

JavaScript опрос
«Intermediate and Advanced Front-End Interview» JavaScript Рукописный код Invincible Cheats

Рукописная навигация по пути

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-кода, но в реальном программировании это не рекомендуется.

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

Три реализации JSON.parse

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состояние неизменно.
  1. должен иметь один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. Ставьте лайк, чтобы больше людей увидело этот контент
  2. Обратите внимание на паблик «Учитель фронтенд-убеждения», и время от времени делитесь оригинальными знаниями.
  3. Также смотрите другие статьи

Вы также можете прийти ко мнеGitHubПолучите исходные файлы всех статей в блоге:

Руководство по убеждению:GitHub.com/Roger-Hi RO/…