Мы не читаем API, мы просто реализуем API

внешний интерфейс опрос
Мы не читаем API, мы просто реализуем API

Многие студенты, которые только что вошли в индустрию, говорили мне: «Что делать, если я не могу вспомнить многие API в JavaScript? Всегда тупо непонятно тот метод и тот метод массива, что делать? Способ манипулирования DOM сегодня помнят, а завтра забывают, это действительно расстраивает!"

Некоторые разработчики даже жалуются мне при обсуждении интервью: "Интервьюеры всегда запутались в использовании API, и даже порядок параметров некоторых методов jQuery должен быть четко объяснен!"

На мой взгляд, для метода многократного использования каждый должен иметь "механическую память" и уметь записывать ее от руки. Некоторые API, которые, кажется, никогда не запоминаются, просто недостаточно используются.

Как интервьюер, я никогда не заставляю разработчиков точно «декламировать» API. Вместо этого мне нравится смотреть на собеседника под другим углом:"Поскольку я не могу вспомнить, как его использовать, я расскажу вам, как его использовать, и вы сможете реализовать его!«Внедрение API может не только проверить понимание API интервьюируемым, но и отразить мышление разработчика в области программирования и способность кодировать. Для мотивированных фронтенд-инженеров имитация и внедрение некоторых классических методов должны быть «повседневными», что является относительно базовыми требованиями. .

**В этом разделе, основываясь на известных мне вопросах интервью и моем опыте интервьюера, я выбираю несколько типичных API и освещаю некоторые моменты знаний и основы программирования на JavaScript, реализуя их в разной степени и разными способами. **Изучая содержание этого раздела, я надеюсь, вы сможете не только понять смысл кода, но и научиться делать выводы из одного экземпляра.

Соответствующие точки знаний по темам API следующие:

目录

реализация смещения jQuery

Эта тема развилась из вопросов интервью определенного отдела в Toutiao сегодня. В то время интервьюер спросил: «Как получить расстояние любого элемента в документе от документаdocumentрасстояние от вершины? "

Учащиеся, знакомые с jQuery, должныoffsetМетод не чужой, он возвращает или задает смещение (положение) совпавшего элемента относительно документа. Объект, возвращаемый этим методом, содержит два целочисленных свойства:topа такжеleftВозьмите пиксель. Если вы можете использовать jQuery, мы можем напрямую адаптировать API для получения результата. но,Если реализовано в собственном JavaScript, то есть вручную реализовать jQueryoffsetМетод, как начать?

Есть две основные идеи:

  • Реализовано рекурсивно
  • пройти черезgetBoundingClientRectРеализация API

рекурсивная реализация

Мы вернемся к источнику, проехавшей целевой элемент, родительский узел элемента целевого элемента, родительский узел родительского узла ... и накапливаться эти пройденные узлы относительно их ближайших узлов предков (иpositionатрибут неstatic) смещение, доdocument, сложите, чтобы получить результат.

Среди них нам нужно использовать JavaScriptoffsetTopдля доступа к границе на узле DOM, который относительно ближе всего к себе, иpositionзначение неstaticВертикальное смещение элемента-предка. Конкретная реализация:

const offset = ele => {
    let result = {
        top: 0,
        left: 0
    }

    // 当前 DOM 节点的 display === 'none' 时, 直接返回 {top: 0, left: 0}
    if (window.getComputedStyle(ele)['display'] === 'none') {
        return result
    }
 
    let position

    const getOffset = (node, init) => {
        if (node.nodeType !== 1) {
            return
        }

        position = window.getComputedStyle(node)['position']
 
        if (typeof(init) === 'undefined' && position === 'static') {
            getOffset(node.parentNode)
            return
        }

        result.top = node.offsetTop + result.top - node.scrollTop
        result.left = node.offsetLeft + result.left - node.scrollLeft
 
        if (position === 'fixed') {
            return
        }
 
        getOffset(node.parentNode)
    }
 
    getOffset(ele, true)
 
    return result
}

Приведенный выше код не сложен для понимания и реализован с использованием рекурсии. если узелnode.nodeTypeтип неElement(1), то выпрыгнуть; еслиpositionсобственностьstatic, он не включается в расчет, а вводится рекурсия к следующему узлу (его родительскому узлу). Если соответствующее имуществоdisplayсобственностьnone, он должен возвращать 0 непосредственно в качестве результата.

Эта реализация является хорошей проверкой элементарного использования разработчиком рекурсии и владения методами JavaScript.

Далее пойдем другим путем и воспользуемся относительно новым API:getBoundingClientRectреализовать jQueryoffsetметод.

getBoundingClientRectметод

getBoundingClientRectМетод используется для описания определенного положения элемента.Следующие четыре свойства этого положения относятся к положению верхнего левого угла окна просмотра. Выполните этот метод на узле, и его возвращаемое значение будетDOMRectтип объекта. Этот объект представляет собой прямоугольную коробку, содержащую:left,top,rightа такжеbottomи другие свойства только для чтения.

图示

Пожалуйста, обратитесь к реализации:

const offset = ele => {
    let result = {
        top: 0,
        left: 0
    }
    // 当前为 IE11 以下,直接返回 {top: 0, left: 0}
    if (!ele.getClientRects().length) {
        return result
    }

    // 当前 DOM 节点的 display === 'none' 时,直接返回 {top: 0, left: 0}
    if (window.getComputedStyle(ele)['display'] === 'none') {
        return result
    }

    result = ele.getBoundingClientRect()
    var docElement = ele.ownerDocument.documentElement

    return {
        top: result.top + window.pageYOffset - docElement.clientTop,
        left: result.left + window.pageXOffset - docElement.clientLeft
    }
}

Обратите внимание на следующие детали:

  • node.ownerDocument.documentElementИспользование может быть незнакомым для всех.ownerDocumentЯвляется свойством узла DOM, возвращает верхний слой текущего узла.documentобъект.ownerDocumentэто документ,documentElementявляется корневым узлом. Фактически,ownerDocumentНиже находятся 2 узла:

    • <!DocType>
    • documentElement

    docElement.clientTop,clientTopширина верхней границы элемента, не включая верхнее поле или отступы.

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

Как видно из этого заголовка, такая реализация имеет больше смысла, чем изучение API «механического запоминания». С точки зрения интервьюера, я часто даю интервьюеру (разработчику) соответствующие советы по методу, чтобы помочь ему дать окончательное решение.

Соответствующая реализация метода уменьшения массива

Метод массива очень важен: **поскольку массивы — это данные, данные — это состояние, а состояние отражает представление. **Мы не можем быть незнакомы с работой массивов, среди которыхreduceМетод должен быть более привычным. Я думаю, что этот метод является хорошим воплощением «функциональной» концепции, а также одним из самых популярных направлений исследования.

мы знаемreduceЭтот метод был представлен ES5, и английское объяснение сокращения переводится как «уменьшать, сжимать, восстанавливать и ослаблять». MDN прямо описывает метод как:

The reduce method applies a function against an accumulator and each value of the array (from left-to-right) to reduce it to a single value.

Синтаксис его использования:

arr.reduce(callback[, initialValue])

Здесь мы кратко представляем его.

  • reduceпервый параметрcallbackявляется ядром, он «складывает» каждый элемент массива, и его последнее возвращаемое значение будет использоваться какreduceОкончательное возвращаемое значение метода. Он содержит 4 параметра:
    • previousValue  означает «последний раз»callbackвозвращаемое значение функции
    • currentValueЭлемент обрабатывается в массиве обхода
    • currentIndex  необязательный, означаетcurrentValueСоответствующий индекс в массиве. если предусмотреноinitialValue, начальный номер индекса равен 0, иначе он равен 1
    • array  по желанию, звонитеreduce()массив
  • initialValueнеобязательно, как первый вызовcallbackпервый параметр времени. если не предусмотреноinitialValue, то первый элемент массива будетcallbackпервый параметр .

reduceвыполнитьrunPromiseInSequence

мы смотрим на этотипичное приложение: запустить промисы по порядку:

const runPromiseInSequence = (array, value) => array.reduce(
    (promiseChain, currentFunction) => promiseChain.then(currentFunction),
    Promise.resolve(value)
)

runPromiseInSequenceМетод будет вызываться массивом, который возвращает промис для каждого элемента, и каждый промис в массиве будет выполняться по очереди.Пожалуйста, поймите внимательно. Если вы находите это неясным, вы можете обратиться к примеру:

const f1 = () => new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log('p1 running')
        resolve(1)
    }, 1000)
})

const f2 = () => new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log('p2 running')
        resolve(2)
    }, 1000)
})


const array = [f1, f2]

const runPromiseInSequence = (array, value) => array.reduce(
    (promiseChain, currentFunction) => promiseChain.then(currentFunction),
    Promise.resolve(value)
)

runPromiseInSequence(array, 'init')

Результат выполнения следующий:

代码执行

уменьшить трубы

reduceдругойтипичное приложениеВы можете обратиться к функциональному методуpipeРеализация:pipe(f, g, h)это каррированная функция, которая возвращает новую функцию, которая завершит(...args) => h(g(f(...args)))вызов. которыйpipeФункция, возвращаемая методом, получит параметр, который передается вpipeПервый параметр метода для его вызова.

const pipe = (...functions) => input => functions.reduce(
    (acc, fn) => fn(acc),
    input
)

Опыт тщательноrunPromiseInSequenceа такжеpipeэти два метода, ониreduceТиповые сценарии приложений.

реализоватьreduce

Так как же нам добитьсяreduceШерстяная ткань? Обратитесь к полифиллу из MDN:

if (!Array.prototype.reduce) {
  Object.defineProperty(Array.prototype, 'reduce', {
    value: function(callback /*, initialValue*/) {
      if (this === null) {
        throw new TypeError( 'Array.prototype.reduce ' + 
          'called on null or undefined' )
      }
      if (typeof callback !== 'function') {
        throw new TypeError( callback +
          ' is not a function')
      }
	
      var o = Object(this)
	
      var len = o.length >>> 0
	
      var k = 0
      var value
	
      if (arguments.length >= 2) {
        value = arguments[1]
      } else {
        while (k < len && !(k in o)) {
          k++
        }
	
        if (k >= len) {
          throw new TypeError( 'Reduce of empty array ' +
            'with no initial value' )
        }
        value = o[k++]
      }
	
      while (k < len) {
        if (k in o) {
          value = callback(value, o[k], k, o)
        }
    
        k++
      }
	
      return value
    }
  })
}

используется в приведенном выше кодеvalueв качестве начального значения и передатьwhileпетля, накапливаться и рассчитать в свою очередьvalueрезультат и вывод. Но по сравнению с приведенной выше реализацией MDN, моя личная предпочтительная реализация:

Array.prototype.reduce = Array.prototype.reduce || function(func, initialValue) {
    var arr = this
    var base = typeof initialValue === 'undefined' ? arr[0] : initialValue
    var startPoint = typeof initialValue === 'undefined' ? 1 : 0
    arr.slice(startPoint)
        .forEach(function(val, index) {
            base = func(base, val, index + startPoint, arr)
        })
    return base
}

Основной принцип заключается в использованииforEachзаменитьwhileРеализовано накопление результатов, они по сути одинаковые.

Я также посмотрел на pollyfill в ES5-shim, который точно такой же, как и в приведенной выше идее. Единственное отличие: я использовалforEachИтеративный, в то время как ES5-shim использует простойforцикл. На самом деле, если бы мы были немного более сообразительными, мы бы указали, что массивforEachМетоды также являются новыми для ES5. Поэтому, используя один из API ES5 (forEach), чтобы реализовать еще один API ES5 (reduce), что на самом деле не имеет смысла — pollyfill здесь — смоделированная схема отката на случай несовместимости с ES5. Здесь не так много нужно исследовать, потому что основная цель состоит в том, чтобы надеяться, что читателиreduceИмейте полное понимание.

Знать сокращение только через исходный код модуля Koa

Понимая и осознаваяreduceметод, у нас уже есть более глубокое понимание этого. Наконец, давайте посмотрим на другойreduceПример использования — через исходный код KoaonlyМодуль, углубить впечатление:

var o = {
    a: 'a',
    b: 'b',
    c: 'c'
}
only(o, ['a','b'])   // {a: 'a',  b: 'b'}

Этот метод возвращает новый объект с указанными свойствами фильтра. ​ только реализация модуля:

var only = function(obj, keys){
    obj = obj || {}
    if ('string' == typeof keys) keys = keys.split(/ +/)
    return keys.reduce(function(ret, key) {
        if (null == obj[key]) return ret
        ret[key] = obj[key]
        return ret
    }, {})
}

крошечныйreduceЕсть много мест, которые стоит обдумать и изучить в отношении его производных сцен. Чтобы сделать выводы из других фактов, жизненное обучение и применение являются ключом к технологическому прогрессу.

Несколько схем, реализованных композицией

Функциональная концепция - теперь эта древняя концепция в области фронтальной "везде". Функциональные идеи стоят обучения, одна деталь: составляют из-за его гениальной конструкции широко используются. Для его реализации, от ориентированного на процесс стиля для достижения функциональных, разных стилей, стоит изучить. Среди интервью интервьюер также часто требуется для достиженияcomposeметод, давайте посмотрим, чтоcompose.

composeНа самом деле, как упоминалось ранееpipeТо же самое — выполнить ряд задач (методов) неопределенной длины, таких как:

let funcs = [fn1, fn2, fn3, fn4]
let composeFunc = compose(...funcs)

воплощать в жизнь:

composeFunc(args)

эквивалентно:

fn1(fn2(fn3(fn4(args))))

В заключениеcomposeКлючевые моменты метода:

  • composeАргумент представляет собой массив функций, и возвращаемое значение также является функцией.
  • composeПараметры могут быть любой длины, все параметры являются функциями, а направление выполнения — справа налево, поэтому исходная функция должна располагаться справа от параметров.
  • composeФункция, возвращаемая после выполнения, может получать параметры, и этот параметр будет использоваться как параметр исходной функции, поэтому параметры исходной функции являются многомерными, и возвращаемый результат исходной функции будет использоваться как параметр следующей функция и так далее. Поэтому, кроме исходной функции, полученное значение остальных функций унарно.

Мы обнаружили, что на самом делеcomposeа такжеpipeРазница только в порядке вызова:

// compose
fn1(fn2(fn3(fn4(args))))
	
// pipe
fn4(fn3(fn2(fn1(args))))

Теперь, как мы ранее достиглиpipeМетоды те же, так что еще анализировать? Читайте дальше, чтобы узнать, какие еще цветы можно разыграть.

composeСамая простая реализация ориентирована на процедуры:

const compose = function(...args) {
    let length = args.length
    let count = length - 1
    let result
    return function f1 (...arg1) {
        result = args[count].apply(this, arg1)
        if (count <= 0) {
            count = length - 1
            return result
        }
        count--
        return f1.call(null, result)
    }
}

Здесь главное использоватьЗакрытие, используйте переменную закрытия для сохранения результатаresultИ длина массива функций и индекс обхода, и использование рекурсивной идеи для накопления результатов. Общая реализация соответствует обычному процессуально-ориентированному мышлению и несложна для понимания.

Умные учащиеся могут также понять, что использование вышеупомянутогоreduceметод, должен уметьфункциональныйрешить проблему:

const reduceFunc = (f, g) => (...arg) => g.call(this, f.apply(this, arg))
const compose = (...args) => args.reverse().reduce(reduceFunc, args.shift())

Благодаря предыдущему исследованию в сочетании сcall,applyметод, такую ​​реализацию нетрудно понять.

Мы продолжаем развивать идеи, «поскольку это включает конкатенацию и управление потоком», то мы также можем использовать Promise для достижения:

const compose = (...args) => {
    let init = args.pop()
    return (...arg) => 
    args.reverse().reduce((sequence, func) => 
      sequence.then(result => func.call(null, result))
    , Promise.resolve(init.apply(null, arg)))
}

В этой реализации используется функция Promise: сначалаPromise.resolve(init.apply(null, arg))начать логику, начатьresolveЗначение — это возвращаемое значение после того, как последняя функция получает параметр, и функции выполняются последовательно. потому чтоpromise.then()по-прежнему возвращает значение Promise, поэтомуreduceОн может быть выполнен полностью в соответствии с экземпляром Promise.

Поскольку это можно реализовать с помощью Promise, тоgeneratorКонечно, это должно быть возможно. Вот вопрос для вас, чтобы подумать. Заинтересованные студенты могут попробовать его. Добро пожаловать, чтобы обсудить в области комментариев.

Наконец, давайте взглянем на реализацию хорошо известного в сообществе lodash и Redux.

лодаш версия

// lodash 版本
var compose = function(funcs) {
    var length = funcs.length
    var index = length
    while (index--) {
        if (typeof funcs[index] !== 'function') {
            throw new TypeError('Expected a function');
        }
    }
    return function(...args) {
        var index = 0
        var result = length ? funcs.reverse()[index].apply(this, args) : args[0]
        while (++index < length) {
            result = funcs[index].call(this, result)
        }
        return result
    }
}

Версия lodash больше похожа на нашу первую реализацию и проще для понимания.

Редукс-версия

// Redux 版本
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)))
}

Короче говоря, по-прежнему в полной мере использовать массивreduceметод.

Функциональные концепции действительно несколько абстрактны и требуют, чтобы разработчики обдумывали и отлаживали их вручную. Как только у вас появится прозрение, вы обязательно почувствуете в нем элегантность и простоту.

Расширенная реализация применения и привязки

интервью оthisСвязанные с привязкой темы сейчас "захлынули", и в то же времяbindРеализация метода также обсуждается в сообществе. Но многое содержание еще не систематизировано, и есть некоторые недочеты. Вот краткий отрывок из статьи, которую я написал в начале 2017 года.От вопроса на собеседовании до «Возможно, я видел поддельный исходный код»для продвижения обсуждения. В "Все в одной сети"this«На уроке мы познакомилиbindреализация, здесь мы расширяемся дальше.

Я не буду вдаваться в подробности здесьbindЧитатели, не разбирающиеся в использовании функций, могут самостоятельно дополнить базовые знания. Давайте сначала посмотрим на предварительную версию реализации:

Function.prototype.bind = Function.prototype.bind || function (context) {
    var me = this;
    var argsArray = Array.prototype.slice.call(arguments);
    return function () {
        return me.apply(context, argsArray.slice(1))
    }
}

Это ответ общеквалифицированного разработчика, если интервьюируемый может написать сюда, дайте ему 60 баллов.

Кратко прочитайте:

Основной принцип использованияapplyимитироватьbind. внутри функцииthisпросто нужно связатьthisфункция или исходная функция. последнее использованиеapplyсделать параметры(context) связать и вернуть.

При этом измените первый параметр (context), в качестве параметров по умолчанию, предоставляемых исходной функции, которая также является основой для базового «каррирования».

В приведенной выше реализации список параметров, который мы возвращаем, содержит:argsArray.slice(1),Проблема в том, что функция предустановленного параметра теряется.

Представьте, что в функции привязки мы возвращаемся, если мы хотим реализовать предустановленные параметры (например,bindдостигнуто), столкнувшись с неловкой ситуацией. «Идеальный способ» на самом деле «карри»:

Function.prototype.bind = Function.prototype.bind || function (context) {
    var me = this;
    var args = Array.prototype.slice.call(arguments, 1);
    return function () {
        var innerArgs = Array.prototype.slice.call(arguments);
        var finalArgs = args.concat(innerArgs);
        return me.apply(context, finalArgs);
    }
}

Но продолжая исследовать, отметимbindВ методе:bindЕсли возвращаемая функция используется как конструктор, она сопоставляется сnewЕсли появляется ключевое слово, наша привязкаthisнужно "не обращать внимания"thisДля привязки к экземпляру. То есть,newоператор выше, чемbindпривязка, реализация, совместимая с этим случаем:

Function.prototype.bind = Function.prototype.bind || function (context) {
    var me = this;
    var args = Array.prototype.slice.call(arguments, 1);
    var F = function () {};
    F.prototype = this.prototype;
    var bound = function () {
        var innerArgs = Array.prototype.slice.call(arguments);
        var finalArgs = args.concat(innerArgs);
        return me.apply(this instanceof F ? this : context || this, finalArgs);
    }
    bound.prototype = new F();
    return bound;
}

Если вы думаете, что это конец, я на самом деле скажу вам, что оргазм вот-вот произойдет. Раньше я думал, что описанный выше метод идеален, пока не посмотрел исходный код es5-shim (который был соответствующим образом удален):

function bind(that) {
    var target = this;
    if (!isCallable(target)) {
        throw new TypeError('Function.prototype.bind called on incompatible ' + target);
    }
    var args = array_slice.call(arguments, 1);
    var bound;
    var binder = function () {
        if (this instanceof bound) {
            var result = target.apply(
                this,
                array_concat.call(args, array_slice.call(arguments))
            );
            if ($Object(result) === result) {
                return result;
            }
            return this;
        } else {
            return target.apply(
                that,
                array_concat.call(args, array_slice.call(arguments))
            );
        }
    };
    var boundLength = max(0, target.length - args.length);
    var boundArgs = [];
    for (var i = 0; i < boundLength; i++) {
        array_push.call(boundArgs, '$' + i);
    }
    bound = Function('binder', 'return function (' + boundArgs.join(',') + '){ return binder.apply(this, arguments); }')(binder);
	
    if (target.prototype) {
        Empty.prototype = target.prototype;
        bound.prototype = new Empty();
        Empty.prototype = null;
    }
    return bound;
}

Какого черта делает реализация es5-shim? Вы можете не знать, на самом деле каждая функция имеетlengthАтрибуты. Да, так же, как массивы и строки. функциональныйlengthАтрибут, используемый для представления количества формальных параметров функции. Что еще более важно, функционалlengthЗначения свойств не переопределяются. Я написал тестовый код для демонстрации:

function test (){}
test.length  // 输出 0
test.hasOwnProperty('length')  // 输出 true
Object.getOwnPropertyDescriptor('test', 'length') 
// 输出:
// configurable: false, 
// enumerable: false,
// value: 4, 
// writable: false 

Сказав это, это легко объяснить: **es5-shim для максимальной совместимости, в том числе для возврата функцийlengthВосстановление свойств. **И если так, как мы это реализовали ранее,lengthЗначение всегда равно нулю. Следовательно, поскольку он не может быть измененlengthЗначение свойства , то всегда можно присвоить значение при инициализации! Так что мы можем пройтиevalа такжеnew Functionспособ динамического определения функций. Но из соображений безопасности в некоторых браузерах используетсяevalилиFunction()Конструкторы выбрасывают исключения. Однако по совпадению эти несовместимые браузеры в основном реализуютbindфункция, эти исключения не будут запущены. В приведенном выше коде сбросьте функцию привязкиlengthАтрибуты:

var boundLength = max(0, target.length - args.length)

Ситуация вызова конструктора, вbinderТакже действительно совместим с:

if (this instanceof bound) { 
    ... // 构造函数调用情况
} else {
    ... // 正常方式调用
}
	
if (target.prototype) {
    Empty.prototype = target.prototype;
    bound.prototype = new Empty();
    // 进行垃圾回收清理
    Empty.prototype = null;
}

Сравнивая несколько версий реализации полифилла, дляbindДолжен иметь более глубокое понимание. Эта серия реализаций эффективно исследует очень важные точки знаний: такие какthisУказание на закрытие JavaScript, прототип и цепочка прототипов, граничный случай программы проектирования и опыт рассмотрения совместимости и другие важные качества.

####Лучший вопрос для интервью

Наконец, во многих интервью в наши дни интервьюерbind«В качестве темы. ** Если бы это был я, я мог бы сейчас избежать этой простой темы «сдачи теста», но проявите изобретательность и позвольте интервьюеру добиться «позвонить / подать заявку». ** Мы часто используемcall/applyРеализация моделированияbind, при этом непосредственно реализуяcall/applyЭто также просто:

Function.prototype.applyFn = function (targetObject, argsArray) {
    if(typeof argsArray === 'undefined' || argsArray === null) {
        argsArray = []
    }
	
    if(typeof targetObject === 'undefined' || targetObject === null){
        targetObject = this
    }
	
    targetObject = new Object(targetObject)
	
    const targetFnKey = 'targetFnKey'
    targetObject[targetFnKey] = this
	
    const result = targetObject[targetFnKey](...argsArray)
    delete targetObject[targetFnKey]
    return result
}

Такой код несложно понять, тело функцииthisуказывает на вызовapplyFnФункция. Чтобы тело функцииthisсвязаны вtargetObjectВыше мы используем метод неявной привязки:targetObject[targetFnKey](...argsArray).

Внимательные читатели обнаружат, что здесь есть проблема: еслиtargetObjectсам объект существуетtargetFnKeyТакие атрибуты используютсяapplyFnфункция, оригиналtargetFnKeyЗначение свойства перезаписывается, а затем удаляется. Решение может использовать ES6Sybmol()для обеспечения уникальных ключей; другим решением является использованиеMath.random()Чтобы получить уникальный ключ, мы не будем повторять их здесь.

Последствия внедрения этих API

Реализация этих API не сложна, но она может должным образом протестировать основы JavaScript разработчика. Основа — это основа, ключ к более глубокому изучению контента и самая важная ссылка на пути к продвижению, которая требует внимания от каждого разработчика. Сегодня, с быстрым развитием и итерацией фронтенд-технологий, в бурной среде различных мнений, таких как «насыщен ли рынок фронтенда», «поиск работы фронтенда чрезвычайно горяч», «фронт-энд». конечный вход прост, а многие люди глупы», особенно важно развитие основных внутренних навыков. Это также является ключом к тому, как далеко и как долго вы можете зайти на переднем крае.

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

обмен акциями

Эта статья из моего курса:Продвинутые базовые знания в области фронтенд-разработкиОдна из основных глав.

Заинтересованные читатели могут:

Нажмите на сторону ПК, чтобы узнать больше «Основные знания для расширенной разработки интерфейсов».

Мобильный Нажмите, чтобы узнать больше:

移动端点击了解更多《前端开发核心知识进阶

Краткое содержание:

image

Happy coding!