JS-операция «Узнать из исходного кода» в исходном коде Vue

JavaScript
JS-операция «Узнать из исходного кода» в исходном коде Vue

В этой статье мы не собираемся анализировать принцип работы исходного кода Vue, а просто исследуем эти достойные операции в инструментальных функциях Vue.

Конечная цель: увеличить очки знаний из функций полезности

1. Ряд суждений о текущем окружении

1.1inBrowser: проверьте, является ли текущая среда хостинга браузером.

// 通过判断 `window` 对象是否存在即可
export const inBrowser = typeof window !== 'undefined'

1.2 hasProto: проверьте, может ли текущая среда использовать__proto__Атрибуты

// 一个对象的 __proto__ 属性指向了其构造函数的原型
// 从一个空的对象字面量开始沿着原型链逐级检查。
export const hasProto = '__proto__' in {}

2. user AgentСерия операций над константами

2.1 Получить, когда браузерuser Agent

// toLowerCase目的是 为了后续的各种环境检测
export const UA = inBrowser && window.navigator.userAgent.toLowerCase()

2.2 Оценка браузера IE

export const isIE = UA && /msie|trident/.test(UA)

Разбор: используйте регулярку, чтобы сопоставить, содержит ли ПА'msie'или'trident'Эти две строки могут определить, является ли это браузером IE.

источник:Internet Explorer User Agent Strings

Multi-highlight

2.3 IE9| Edge | Chrome

export const isIE9 = UA && UA.indexOf('msie 9.0') > 0
export const isEdge = UA && UA.indexOf('edge/') > 0
export const isChrome = UA && /chrome\/\d+/.test(UA) && !isEdge

3.1 isReserved

// charCodeAt() 方法可返回指定位置的字符的 Unicode 编码
export function isReserved (str: string): boolean {
  const c = (str + '').charCodeAt(0)
  return c === 0x24 || c === 0x5F
}

unicode0x24а также0x5Fв сравнении с.

Как фронтенд, который хочет перейти на средний и продвинутый уровень,charCodeAtРазличные волшебные применения метода все еще должны быть известны (все виды вопросов алгоритма интервью).

3.1.2 JavascriptПромежуточный алгоритмcharCodeAt

Найдите пропущенную букву из переданной последовательности букв и верните ее. Например: FearNotLetter("abce") должен возвращать "d".

function fearNotLetter(str) {
  //将字符串转为ASCII码,并存入数组
  let arr=[];
  for(let i=0; i<str.length; i++){
    arr.push(str.charCodeAt(i));
  }
  for(let j=1; j<arr.length; j++){
    let num=arr[j]-arr[j-1];
    //判断后一项减前一项是否为1,若不为1,则缺失该字符的前一项
    if(num!=1){
      //将缺失字符ASCII转为字符并返回 
      return String.fromCharCode(arr[j]-1); 
    }
  }
  return undefined;
}
fearNotLetter("abce") // "d"

3.2 camelize: дефис в верблюжьем регистре

const camelizeRE = /-(\w)/g
export const camelize = cached((str: string): string => {
  return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : '')
})

Разобрать:Определите регулярное выражение:/-(\w)/g, который используется для глобального сопоставления дефиса и символа после дефиса в строке. В случае захвата персонаж начнет сtoUpperCaseзамена в верхнем регистре, в противном случае с''заменять. Такие как:camelize('aa-bb') // aaBb

3.3 toString: преобразовать значение данной переменной в строку типа и вернуть

export function toString (val: any): string {
  return val == null
    ? ''
    : typeof val === 'object'
      ? JSON.stringify(val, null, 2)
      : String(val)
}

Разобрать:существуетVueВ нем много таких расширенных оберток, которые сильно уменьшают сложность нашего кода. Но здесь мы хотим узнать следующее.多重三元运算符Применение

3.3.1 Множественные тернарные операторы

export function toString (val: any): string {
  return val == null
    ? ''
    : typeof val === 'object'
      ? JSON.stringify(val, null, 2)
      : String(val)
}

解析:

export function toString (val: any): string {
  return 当变量值为 null 时
    ? 返回空字符串
    : 否则,判断当变量类型为 object时
      ? 返回 JSON.stringify(val, null, 2)
      : 否则 String(val)
}

В исходном коде vue есть много подобных операций. НапримерmergeHook

3.3.2 mergeHook: объединить параметры жизненного цикла

function mergeHook (
  parentVal: ?Array<Function>,
  childVal: ?Function | ?Array<Function>
): ?Array<Function> {
  return childVal
    ? parentVal
      ? parentVal.concat(childVal)
      : Array.isArray(childVal)
        ? childVal
        : [childVal]
    : parentVal
}

Здесь нам все равноmergeHookВ исходном коде это делается (по сути, заключается в том, чтобы определить, соответствует ли компонент имени отца и жизненному циклу функции хука, затем путемconcatсливаться

3.4 capitalize: Сделать первый символ заглавным

// 忽略cached
export const capitalize = cached((str: string): string => {
  return str.charAt(0).toUpperCase() + str.slice(1)
})

Разобрать: str.charAt(0)Получатьstrпервый термин, используяtoUpperCase()преобразовать в верхний регистр,str.slice(1)Отрезать первый элементstrчасть.

3.5 hyphenate: верблюжий регистр до дефиса

const hyphenateRE = /\B([A-Z])/g
export const hyphenate = cached((str: string): string => {
  return str.replace(hyphenateRE, '-$1').toLowerCase()
})

Разобрать:а такжеcamelizeНапротив. Метод реализации также заключается в использовании обычных,/\B([A-Z])/gИспользуется для сопоставления заглавных букв в строке с последующей заменой.

4. Тип суждения

4.1 isPrimitive: определить, является ли переменная типом прототипа.

export function isPrimitive (value: any): boolean %checks {
  return (
    typeof value === 'string' ||
    typeof value === 'number' ||
    // $flow-disable-line
    typeof value === 'symbol' ||
    typeof value === 'boolean'
  )
}

Разобрать:Это очень просто, но мы часто игнорируемsymbolЭтот тип (хотя и совершенно неиспользуемый).

4.2 isRegExp: определяет, является ли переменная обычным объектом.

// 使用 Object.prototype.toString 与 '[object RegExp]' 做全等对比。

export function isRegExp (v: any): boolean {
  return _toString.call(v) === '[object RegExp]'
}

Это также самый точный метод оценки типа, вVueТо же самое верно для других видов

4.3 isValidArrayIndex: определить, содержит ли переменная допустимый индекс массива

export function isValidArrayIndex (val: any): boolean {
  const n = parseFloat(String(val))
  // n >= 0 && Math.floor(n) === n 保证了索引是一个大于等于 0 的整数
  return n >= 0 && Math.floor(n) === n && isFinite(val)
}

isFiniteМетод проверяет значение своего аргумента. если параметрNaN, положительная бесконечность или отрицательная бесконечность, вернетfalse, другие возвратыtrue.

Расширение:Синтаксис: isFinite()

4.4 isObject: различать объекты и примитивные значения

export function isObject (obj: mixed): boolean %checks {
  return obj !== null && typeof obj === 'object'
}

5. Операция закрытия в Vue

5.1 makeMap(): определяет, содержится ли переменная во входящей строке

export function makeMap (
  str: string,
  expectsLowerCase?: boolean
): (key: string) => true | void {
  const map = Object.create(null)
  const list: Array<string> = str.split(',')
  for (let i = 0; i < list.length; i++) {
    map[list[i]] = true
  }
  return expectsLowerCase
    ? val => map[val.toLowerCase()]
    : val => map[val]
}
  1. определить объектmap
  2. Будуstrразделить на массивы и сохранить вlistв переменной
  3. траверсlist,а такжеlistэлементы в качествеmapизkey, установите его наtrue
  4. возвращает функцию, и еслиexpectsLowerCaseдляtrueслова, строчныеmap[key]:

Проиллюстрируем на примере:

let isMyName = makeMap('前端劝退师,帅比',true); 
//设定一个检测是否为我的名字的方法,第二个参数不区分大小写
isMyName('前端劝退师')  // true
isMyName('帅比')  // true
isMyName('丑逼')  // false

VueПодобных судов в Китае много, и они очень практичны.

5.1.1 isHTMLTag | isSVG | isReservedAttr

Эти три функцииmakeMapГенерируется для определения того, является ли атрибут (тег) зарезервированным атрибутом (тегом).

export const isHTMLTag = makeMap(
  'html,body,base,head,link,meta,style,title,' +
  'address,article,aside,footer,header,h1,h2,h3,h4,h5,h6,hgroup,nav,section,' +
  'div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,' +
  'a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,rtc,ruby,' +
  's,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,' +
  'embed,object,param,source,canvas,script,noscript,del,ins,' +
  'caption,col,colgroup,table,thead,tbody,td,th,tr,' +
  'button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,' +
  'output,progress,select,textarea,' +
  'details,dialog,menu,menuitem,summary,' +
  'content,element,shadow,template,blockquote,iframe,tfoot'
)
export const isSVG = makeMap(
  'svg,animate,circle,clippath,cursor,defs,desc,ellipse,filter,font-face,' +
  'foreignObject,g,glyph,image,line,marker,mask,missing-glyph,path,pattern,' +
  'polygon,polyline,rect,switch,symbol,text,textpath,tspan,use,view',
  true
)
// web平台的保留属性有 style 和 class
export const isReservedAttr = makeMap('style,class')

5.2 once: функция, которая вызывается только один раз

export function once (fn: Function): Function {
  let called = false
  return function () {
    if (!called) {
      called = true
      fn.apply(this, arguments)
    }
  }
}

Разобрать:кcalledв качестве идентификатора обратного вызова. При вызове этой функцииcalledЕсли идентификатор изменен, следующий вызов будет недействительным. Также типичный звонок закрытия.

5.3 cache: создать функцию кеша

/**
 * Create a cached version of a pure function.
 */
export function cached<F: Function> (fn: F): F {
  const cache = Object.create(null)
  return (function cachedFn (str: string) {
    const hit = cache[str]
    return hit || (cache[str] = fn(str))
  }: any)
}

Разобрать:Комментарии здесь объяснили эффект.

const cache = Object.create(null)Создать чистую функцию, чтобы предотвратить изменение(纯函数的特性:输入不变则输出不变).

В Vue нужно переводить много одинаковых строк, и если перевод каждый раз выполнять заново, это вызовет много ненужных накладных расходов.cacheЭта функция может прочитать кеш, если кеша нет, сохранить его в кеше и, наконец, прочитать.

6. Несколько типов суждений о конгруэнтности

looseEqual: проверить, равны ли два значения

export function looseEqual (a: any, b: any): boolean {
  // 当 a === b 时,返回true
  if (a === b) return true
  // 否则进入isObject判断
  const isObjectA = isObject(a)
  const isObjectB = isObject(b)
  // 判断是否都为Object类型
  if (isObjectA && isObjectB) {
    try {
      // 调用 Array.isArray() 方法,再次进行判断
      // isObject 不能区分是真数组还是对象(typeof)
      const isArrayA = Array.isArray(a)
      const isArrayB = Array.isArray(b)
      // 判断是否都为数组
      if (isArrayA && isArrayB) {
        // 对比a、bs数组的长度
        return a.length === b.length && a.every((e, i) => {
          // 调用 looseEqual 进入递归
          return looseEqual(e, b[i])
        })
      } else if (!isArrayA && !isArrayB) {
        // 均不为数组,获取a、b对象的key集合
        const keysA = Object.keys(a)
        const keysB = Object.keys(b)
        // 对比a、b对象的key集合长度
        return keysA.length === keysB.length && keysA.every(key => {
          //长度相等,则调用 looseEqual 进入递归
          return looseEqual(a[key], b[key])
        })
      } else {
        // 如果a、b中一个是数组,一个是对象,直接返回 false
        /* istanbul ignore next */
        return false
      }
    } catch (e) {
      /* istanbul ignore next */
      return false
    }
  } else if (!isObjectA && !isObjectB) {
    return String(a) === String(b)
  } else {
    return false
  }
}

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

Различные суждения о типах + рекурсия

В этой статье давайте поговорим о некоторых функциях инструментов в Vue.Есть много способов изучить исходный код Vue. Пройди мимо и поставь палец вверх, бро.

Сборник авторских статей