• Vue.js源码解析:Vue.js 常用工具函数 util.js


    源码地址:https://github.com/vuejs/vue/blob/2.6/src/shared/util.js

    1. 基本判断

    1.1 isUndef 是否未定义

    export function isUndef(v: any): boolean % checks {
      return v === undefined || v === null
    }
    
    • 1
    • 2
    • 3

    判断变量是否未定义,也就是判断是否是定义了未赋值,或者赋值null的情况

    1.2 isDef 是否定义

    export function isDef(v: any): boolean % checks {
        return v !== undefined && v !== null
    }
    
    • 1
    • 2
    • 3

    补充:详解undefined 与 null 的区别
    undefined :未定义的值,属于原始状态。
    (1)声明一个变量,但没有赋值
    (2)访问对象上不存在的属性
    (3)函数定义了形参,但未传递实参
    (4)函数没有返回值,即void状态,默认返回undefined
    null:空值,属于空对象,非原始状态。
    (1) 作为函数的参数,表示该函数的参数不是对象。
    (2) 作为对象原型链的终点。

    1.3 isTrue 是否为True

    export function isTrue(v: any): boolean % checks {
        return v === true
    }
    
    • 1
    • 2
    • 3

    判断值是否为True,很简单

    1.4 isFalse 是否为False

    export function isFalse(v: any): boolean % checks {
        return v === false
    }
    
    • 1
    • 2
    • 3

    判断值是否为False,很简单

    1.5 emptyObject 创建冻结的空对象

    export const emptyObject = Object.freeze({})
    
    • 1

    Object.freeze() 方法可以冻结一个对象。 MDN解释
    被冻结的对象不能增删改它的属性、同时不能修改已有属性的值。
    扩展:
    **Object.isFrozen() **方法判断一个对象是否被冻结。
    **Object.seal() **方法封闭一个对象,阻止添加新属性并将所有现有属性标记为不可配置。当前属性的值只要原来是可写的就可以改变。
    Object.isSealed() 方法判断一个对象是否被密封。

    1.6 isRegExp 是否为RegExp对象

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

    1.7 isPromise 是否为promise对象

    export function isPromise(val: any): boolean {
        return (
            isDef(val) &&
            typeof val.then === 'function' &&
            typeof val.catch === 'function'
        )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2. 数据类型判断和获取

    2.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'
        )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    原始类型:String、Number、Boolean、Symbol
    一般情况下:
    typeof 判断原始类型,
    instanceof 判断对象类型

    扩展:为什么不能用typeof判断所有类型
    (1)typeof 对 原始数据类型(null除外)都可以显示为正确的类型
    (2)typeof 对 对象 (函数除外),都会显示object
    示例:
    typeof [1, 2, 4] == ‘object’; // true
    typeof new Date() === ‘object’; // true
    typeof /regex/ === ‘object’; // true
    typeof null === ‘object’; // true

    2.2 isObject 是否为对象

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

    由于 typeof null === ‘object’,所以判断是否为对象时要加上非null的条件。
    正常情况下,如2.1扩展所说:typeof 对对象(函数除外),都会显示Object。

    2.3 isPlainObject 是否为纯粹的对象

     const _toString = Object.prototype.toString
     export function isPlainObject(obj: any): boolean {
         return _toString.call(obj) === '[object Object]'
     }
    
    • 1
    • 2
    • 3
    • 4

    我们常用isObject来判断是否为对象并不准确,例如:isObject([]) 结果就为true。
    因此,如果想判断是否为真正的对象,可以用isPlainObject,例如:使用isPlainObject判断,则 isPlainObject([]) 为false。

    2.4 toRawType 获取数据的原始类型

     const _toString = Object.prototype.toString
     
     export function toRawType(value: any): string {
         return _toString.call(value).slice(8, -1)
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    例如:
    toRawType(1)
    toRawType({})

    3. 数据类型转换

    3.1 toString 将各种类型的值序列化为string类型

    export function toString(val: any): string {
        return val == null ?
            '' :
            Array.isArray(val) || (isPlainObject(val) && val.toString === _toString) ?
            JSON.stringify(val, null, 2) :
            String(val)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    将数据转换为string类型,如果是数组或者对象用JSON.stringify做转换。

    3.2 toNumber 转为数字

    export function toNumber(val: string): number | string {
        const n = parseFloat(val)
        return isNaN(n) ? val : n
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    string类型的数字可以转换为number类型,但是string类型的字符串则还是返回字符串。

    4. 其他

    4.1 isValidArrayIndex 是否有效的数组索引

    export function isValidArrayIndex(val: any): boolean {
        const n = parseFloat(String(val))
        return n >= 0 && Math.floor(n) === n && isFinite(val)
    }
    
    • 1
    • 2
    • 3
    • 4

    4.2 makeMap 创建字典。返回函数检测key是否存在于map

    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    4.2.1 isBuiltInTag 检测tag是否为内置tag

    export const isBuiltInTag = makeMap('slot,component', true)
    
    • 1

    4.2.2 isReservedAttribute 检查属性是否为vue保留属性

    export const isReservedAttribute = makeMap('key,ref,slot,slot-scope,is')
    
    • 1

    4.3 remove 数组中删除某一项

    export function remove(arr: Array < any > , item: any): Array < any > | void {
        if (arr.length) {
            const index = arr.indexOf(item)
            if (index > -1) {
                return arr.splice(index, 1)
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    4.3 hasOwn 验证是否为对象本身的属性,而非原型链上的

    const hasOwnProperty = Object.prototype.hasOwnProperty
    export function hasOwn(obj: Object | Array < * > , key: string): boolean {
        return hasOwnProperty.call(obj, key)
    }
    
    • 1
    • 2
    • 3
    • 4

    4.4 cached 缓存函数

    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)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    利用闭包特性,创建缓存函数。

    4.5 camelize 横线形式’-'转驼峰

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

    4.6 hyphenate 驼峰转横线形式’-’

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

    4.7 capitalize 首字母大写

    export const capitalize = cached((str: string): string => {
        return str.charAt(0).toUpperCase() + str.slice(1)
    })
    
    • 1
    • 2
    • 3

    4.8 bind 函数兼容

    var bind = Function.prototype.bind
      ? nativeBind
      : polyfillBind;
    
    • 1
    • 2
    • 3

    简单来说就是兼容了老版本浏览器不支持原生的 bind 函数。同时兼容写法,对参数的多少做出了判断,使用call和apply实现,

    4.8.1 polyfillBind 实现bind

    function polyfillBind(fn: Function, ctx: Object): Function {
        function boundFn(a) {
            const l = arguments.length
            return l ?
                l > 1 ?
                fn.apply(ctx, arguments) :
                fn.call(ctx, a) :
                fn.call(ctx)
        }
    
        boundFn._length = fn.length
        return boundFn
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    4.8.2 nativeBind 原生bind

    function nativeBind(fn: Function, ctx: Object): Function {
        return fn.bind(ctx)
    }
    
    • 1
    • 2
    • 3

    4.9 extend 对象属性合并

    export function extend(to: Object, _from: ? Object): Object {
        for (const key in _from) {
            to[key] = _from[key]
        }
        return to
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    示例:
    const data = { name: ‘李四’ };
    const data2 = extend(data, { mp: ‘AAAA’, name: ‘王五’ });
    console.log(data); // { name: “王五”, mp: “AAAA” }
    console.log(data2); // { name: “王五”, mp: “AAAA” }

    4.10 toArray 类数组转为真实数组

    export function toArray(list: any, start ? : number): Array < any > {
        start = start || 0
        let i = list.length - start
        const ret: Array < any > = new Array(i)
        while (i--) {
            ret[i] = list[i + start]
        }
        return ret
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    示例:
    toArray(1,2,3,4,5) // 输出:[1,2,3,4,5]

    4.11 toObject 将对象数组中的对象提取到一个对象里面

    export function toObject(arr: Array < any > ): Object {
        const res = {}
        for (let i = 0; i < arr.length; i++) {
            if (arr[i]) {
                extend(res, arr[i])
            }
        }
        return res
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    示例:
    toObject([{a:2,c:3},{a:1,b:2}]) // {a: 1, c: 3, b: 2}

    4.12 looseEqual 判断两个内容是否相同,宽松相等

    export function looseEqual(a: any, b: any): boolean {
        if (a === b) return true
        const isObjectA = isObject(a)
        const isObjectB = isObject(b)
        if (isObjectA && isObjectB) {
            try {
                const isArrayA = Array.isArray(a)
                const isArrayB = Array.isArray(b)
                if (isArrayA && isArrayB) {
                    return a.length === b.length && a.every((e, i) => {
                        return looseEqual(e, b[i])
                    })
                } else if (a instanceof Date && b instanceof Date) {
                    return a.getTime() === b.getTime()
                } else if (!isArrayA && !isArrayB) {
                    const keysA = Object.keys(a)
                    const keysB = Object.keys(b)
                    return keysA.length === keysB.length && keysA.every(key => {
                        return looseEqual(a[key], b[key])
                    })
                } else {
                    /* istanbul ignore next */
                    return false
                }
            } catch (e) {
                /* istanbul ignore next */
                return false
            }
        } else if (!isObjectA && !isObjectB) {
            return String(a) === String(b)
        } else {
            return false
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    js数据类型中:数组、对象等是引用类型,所以两个内容看起来相等,严格相等都是不相等。
    该函数的作用就是:对数组、日期、对象进行递归比对。如果内容完全相等则宽松相等。

    示例:
    looseEqual({a:‘2’},{a:‘2’}) // true

    4.13 looseIndexOf 返回数组元素的下标

    export function looseIndexOf(arr: Array < mixed > , val: mixed): number {
        for (let i = 0; i < arr.length; i++) {
            if (looseEqual(arr[i], val)) return i
        }
        return -1
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    基于looseEqual函数,该函数的作用是找到并返回数组元素(第一次出现)的Index。
    原生的 indexOf 是严格相等。

    4.14 once 函数执行一次

    export function once(fn: Function): Function {
        let called = false
        return function() {
            if (!called) {
                called = true
                fn.apply(this, arguments)
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    利用闭包,实现函数只执行一次。
    扩展:闭包
    由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数"。在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
    阮一峰:学习Javascript闭包(Closure)

    5. 源码(中文备注)

    /* @flow */
    
    /**
     * 创建空对象
     */
    export const emptyObject = Object.freeze({})
    
    // These helpers produce better VM code in JS engines due to their
    // explicitness and function inlining.
    
    /**
     * 是否未定义
     * @param {*} v 
     */
    export function isUndef(v: any): boolean % checks {
        return v === undefined || v === null
    }
    
    /**
     * 是否定义
     * @param {*} v 
     */
    
    export function isDef(v: any): boolean % checks {
        return v !== undefined && v !== null
    }
    
    /**
     * 是否True
     * @param {*} v 
     */
    export function isTrue(v: any): boolean % checks {
        return v === true
    }
    
    /**
     * 是否False
     * @param {*} v 
     */
    export function isFalse(v: any): boolean % checks {
        return v === false
    }
    
    /**
     * 判断对象是否为原始类型(Check if value is primitive. )
     */
    export function isPrimitive(value: any): boolean % checks {
        return (
            typeof value === 'string' ||
            typeof value === 'number' ||
            // $flow-disable-line
            typeof value === 'symbol' ||
            typeof value === 'boolean'
        )
    }
    
    /**
     * 判断是否为对象:1. 知道原始类型 2. 符合JSON类型
     * Quick object check - this is primarily used to tell
     * Objects from primitive values when we know the value
     * is a JSON-compliant type.
     */
    export function isObject(obj: mixed): boolean % checks {
        return obj !== null && typeof obj === 'object'
    }
    
    
    /**
     * 获取值的原始类型
     * Get the raw type string of a value, e.g., [object Object].
     */
    const _toString = Object.prototype.toString
    
    export function toRawType(value: any): string {
        return _toString.call(value).slice(8, -1)
    }
    
    /**
     * 是否为纯粹的对象
     * Strict object type check. Only returns true
     * for plain JavaScript objects.
     */
    export function isPlainObject(obj: any): boolean {
        return _toString.call(obj) === '[object Object]'
    }
    
    /**
     * 是否为RegExp对象
     * @param {*} v 
     * @returns 
     */
    export function isRegExp(v: any): boolean {
        return _toString.call(v) === '[object RegExp]'
    }
    
    /**
     * 是否有效的数组索引
     * Check if val is a valid array index.
     */
    export function isValidArrayIndex(val: any): boolean {
        const n = parseFloat(String(val))
        return n >= 0 && Math.floor(n) === n && isFinite(val)
    }
    
    /**
     * 是否为promise对象
     * @param {*} val 
     * @returns 
     */
    export function isPromise(val: any): boolean {
        return (
            isDef(val) &&
            typeof val.then === 'function' &&
            typeof val.catch === 'function'
        )
    }
    
    /**
     * 将各种类型的值序列化为string类型
     * Convert a value to a string that is actually rendered.
     */
    export function toString(val: any): string {
        return val == null ?
            '' :
            Array.isArray(val) || (isPlainObject(val) && val.toString === _toString) ?
            JSON.stringify(val, null, 2) :
            String(val)
    }
    
    /**
     * 转为数字
     * Convert an input value to a number for persistence.
     * If the conversion fails, return original string.
     */
    export function toNumber(val: string): number | string {
        const n = parseFloat(val)
        return isNaN(n) ? val : n
    }
    
    /**
     * 创建字典。返回函数检测key是否存在于map
     * Make a map and return a function for checking if a key is in that map.
     * 
     */
    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]
    }
    
    /**
     * 检测tag是否为内置tag,比如slot,component
     * Check if a tag is a built-in tag.
     */
    export const isBuiltInTag = makeMap('slot,component', true)
    
    /**
     * 检查属性是否为vue保留属性
     * Check if an attribute is a reserved attribute.
     */
    export const isReservedAttribute = makeMap('key,ref,slot,slot-scope,is')
    
    /**
     * 数组中删除某一项
     * Remove an item from an array.
     */
    export function remove(arr: Array < any > , item: any): Array < any > | void {
        if (arr.length) {
            const index = arr.indexOf(item)
            if (index > -1) {
                return arr.splice(index, 1)
            }
        }
    }
    
    /**
     * 验证是否为对象本身的属性,而非原型链上的
     * Check whether an object has the property.
     */
    const hasOwnProperty = Object.prototype.hasOwnProperty
    export function hasOwn(obj: Object | Array < * > , key: string): boolean {
        return hasOwnProperty.call(obj, key)
    }
    
    /**
     * 创建缓存函数
     * 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)
    }
    
    /**
     * 横线形式'-'转驼峰
     * Camelize a hyphen-delimited string.
     */
    const camelizeRE = /-(\w)/g
    export const camelize = cached((str: string): string => {
        return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : '')
    })
    
    /**
     * 首字母大写
     * Capitalize a string.
     */
    export const capitalize = cached((str: string): string => {
        return str.charAt(0).toUpperCase() + str.slice(1)
    })
    
    /**
     * 驼峰转横线形式'-'
     * Hyphenate a camelCase string.
     */
    const hyphenateRE = /\B([A-Z])/g
    export const hyphenate = cached((str: string): string => {
        return str.replace(hyphenateRE, '-$1').toLowerCase()
    })
    
    /**
     * Simple bind polyfill for environments that do not support it,
     * e.g., PhantomJS 1.x. Technically, we don't need this anymore
     * since native bind is now performant enough in most browsers.
     * But removing it would mean breaking code that was able to run in
     * PhantomJS 1.x, so this must be kept for backward compatibility.
     */
    
    /* istanbul ignore next */
    /**
     * 实现bind
     * @param {*} fn 
     * @param {*} ctx 
     * @returns 
     */
    function polyfillBind(fn: Function, ctx: Object): Function {
        function boundFn(a) {
            const l = arguments.length
            return l ?
                l > 1 ?
                fn.apply(ctx, arguments) :
                fn.call(ctx, a) :
                fn.call(ctx)
        }
    
        boundFn._length = fn.length
        return boundFn
    }
    
    /**
     * 原生bind
     * @param {*} fn 
     * @param {*} ctx 
     * @returns 
     */
    function nativeBind(fn: Function, ctx: Object): Function {
        return fn.bind(ctx)
    }
    
    /**
     * 兼容判断
     */
    export const bind = Function.prototype.bind ?
        nativeBind :
        polyfillBind
    
    /**
     * 类数组转为真实数组
     * Convert an Array-like object to a real Array.
     */
    export function toArray(list: any, start ? : number): Array < any > {
        start = start || 0
        let i = list.length - start
        const ret: Array < any > = new Array(i)
        while (i--) {
            ret[i] = list[i + start]
        }
        return ret
    }
    
    /**
     * 扩展集成
     * Mix properties into target object.
     */
    export function extend(to: Object, _from: ? Object): Object {
        for (const key in _from) {
            to[key] = _from[key]
        }
        return to
    }
    
    /**
     * 将对象数组中的对象提取到一个对象里面
     * Merge an Array of Objects into a single Object.
     * 
     */
    export function toObject(arr: Array < any > ): Object {
        const res = {}
        for (let i = 0; i < arr.length; i++) {
            if (arr[i]) {
                extend(res, arr[i])
            }
        }
        return res
    }
    
    /* eslint-disable no-unused-vars */
    
    /**
     * Perform no operation.
     * Stubbing args to make Flow happy without leaving useless transpiled code
     * with ...rest (https://flow.org/blog/2017/05/07/Strict-Function-Call-Arity/).
     */
    export function noop(a ? : any, b ? : any, c ? : any) {}
    
    /**
     * Always return false.
     */
    export const no = (a ? : any, b ? : any, c ? : any) => false
    
    /* eslint-enable no-unused-vars */
    
    /**
     * Return the same value.
     */
    export const identity = (_: any) => _
    
    /**
     * Generate a string containing static keys from compiler modules.
     */
    export function genStaticKeys(modules: Array < ModuleOptions > ): string {
        return modules.reduce((keys, m) => {
            return keys.concat(m.staticKeys || [])
        }, []).join(',')
    }
    
    /**
     * 判断两个内容是否相同,宽松相等
     * Check if two values are loosely equal - that is,
     * if they are plain objects, do they have the same shape?
     */
    export function looseEqual(a: any, b: any): boolean {
        if (a === b) return true
        const isObjectA = isObject(a)
        const isObjectB = isObject(b)
        if (isObjectA && isObjectB) {
            try {
                const isArrayA = Array.isArray(a)
                const isArrayB = Array.isArray(b)
                if (isArrayA && isArrayB) {
                    return a.length === b.length && a.every((e, i) => {
                        return looseEqual(e, b[i])
                    })
                } else if (a instanceof Date && b instanceof Date) {
                    return a.getTime() === b.getTime()
                } else if (!isArrayA && !isArrayB) {
                    const keysA = Object.keys(a)
                    const keysB = Object.keys(b)
                    return keysA.length === keysB.length && keysA.every(key => {
                        return looseEqual(a[key], b[key])
                    })
                } else {
                    /* istanbul ignore next */
                    return false
                }
            } catch (e) {
                /* istanbul ignore next */
                return false
            }
        } else if (!isObjectA && !isObjectB) {
            return String(a) === String(b)
        } else {
            return false
        }
    }
    
    /**
     * 返回数组元素的下标
     * Return the first index at which a loosely equal value can be
     * found in the array (if value is a plain object, the array must
     * contain an object of the same shape), or -1 if it is not present.
     */
    export function looseIndexOf(arr: Array < mixed > , val: mixed): number {
        for (let i = 0; i < arr.length; i++) {
            if (looseEqual(arr[i], val)) return i
        }
        return -1
    }
    
    /**
     * 函数执行一次
     * Ensure a function is called only once.
     */
    export function once(fn: Function): Function {
        let called = false
        return function() {
            if (!called) {
                called = true
                fn.apply(this, arguments)
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350
    • 351
    • 352
    • 353
    • 354
    • 355
    • 356
    • 357
    • 358
    • 359
    • 360
    • 361
    • 362
    • 363
    • 364
    • 365
    • 366
    • 367
    • 368
    • 369
    • 370
    • 371
    • 372
    • 373
    • 374
    • 375
    • 376
    • 377
    • 378
    • 379
    • 380
    • 381
    • 382
    • 383
    • 384
    • 385
    • 386
    • 387
    • 388
    • 389
    • 390
    • 391
    • 392
    • 393
    • 394
    • 395
    • 396
    • 397
    • 398
    • 399
    • 400
    • 401
    • 402
    • 403
    • 404
    • 405
    • 406
    • 407
    • 408
    • 409
    • 410
    • 411
    • 412
  • 相关阅读:
    Echarts实现半圆形饼图,Echarts实现扇形图
    STM32:GPIO控制LED流水灯(主函数代码部分+代码解释)
    【MATLAB】关系运算与逻辑运算
    【算法专题--链表】合并两个有序链表--高频面试题(图文详解,小白一看就会!!)
    LVS的认识与快速上手
    10.20 platform总线驱动
    Mysql——》计算日期差
    添加阿里云maven镜像
    Figma数值输入框支持拖拽调整功能实现
    计算机组成原理习题help
  • 原文地址:https://blog.csdn.net/yi_zongjishi/article/details/125561138