精华内容
下载资源
问答
  • 在ARM汇编数据处理指令中经常会使用到常数,而ARM汇编中规定使用常数必须立即数。ARM立即数的是由一个8位常数循环右移...为什么会有立即数这样规定呢,这由于所有ARM指令是精简指令集,指令长度固定都...

    在ARM汇编的数据处理指令中经常会使用到常数,而ARM汇编中规定使用的常数必

    须是立即数。ARM立即数的是由一个8位的常数循环右移偶数位得到的,其中循环右移

    的位数由一个4位2进制的两倍表示,公式如下:

    immediate=immed_8&

    简单的说一个常数如果可以由一个8位的常数循环移位偶数位得到,那么就是立即数。

    为什么会有立即数这样的规定呢,这是由于所有的ARM指令是精简指令集,指令长度固定都是32位,对于ARM数据处理指令自然也是一样。数据处理指令大致可包含3类,数据传送指令、数据算术逻辑运算指令和数据比较指令。在一条ARM数据处理指令中,除了要包含处理的数据值外,还要标识ARM命令名称,控制位,寄存器等其他信息。这样在一条ARM数据处理指令中,能用于表示要处理的数据值的位数只能小于32位。

    ARM在指令格式中设定,只能用指令机器码32位中的低12位来表示要操作的常数。ARM处理器是按32位来处理数据的,ARM处理器处理的数据是32位,如果简单的用这12位来表示,显然范围太小了,为了扩展到32位,因此使用了构造的方法,在12位中用8位表示基本数据值,用4位表示位移值,通过用8位基本数据值往右循环移动4位位移值*2次,来表示要操作的常数。这里要强调终的循环次数是4位位移值乘以2得到的,所以得到的终循环次数肯定是一个偶数,为什么要乘以2呢,实质还是因为范围不够,4位表示位移次数,大才15次,加上8位数据还是不够32位,这样只能通过ALU的内部结构设计将4位位移次数乘以2,这样就能用12位表示32位常数了。

    通过循环偶数位得的到操作数,扩大了操作数的范围,但也带来了问题,并不是每个数据都能通过8位基本数据循环移动偶数为得到,如果你在ARM数据处理指令中使用的操作数,不是立即数,比如MOV R1,#0x12345678,编译器就会报错,所以我们在使用前必须进行判断,这也是很多ARM相关求职笔试中常考的一道题目。

    那怎样怎么快速判断一个数是否是立即数,对于简单的数字我们可以直接判断,比如小于255的数字肯定是立即数。对相对复杂的数字进行判断就需要先把它转换为2进制形式,然后根据定义进行判断了。我这里总结了个比较快速的方法:

    1、把数据转换成二进制形式,从低位到高位写成4位1组的形式,高位一组不够四位的,在高位前面补0。

    2、数1的个数,如果大于8个肯定不是立即数,如果小于等于8进行下面步骤。

    3、如果数据中间有连续的大于等于24个0,循环左移4的倍数,使高位全为0。

    4、找到高位的1,去掉前面大偶数个0。

    5、找到低位的1,去掉后面大偶数个0。

    6、数剩下的位数,如果小于等于8位,那么这个数就是立即数,反之就不是立即数。

    针对可能现的情况,我举5个典型例子:

    (1)0x4FF (2)0x122 (3)0x234 (4)0xF000000F (5)0x8000007F

    例1: 0x4FF

    第一步:0100 1111 1111

    第二步:其中1的个数是9个,大于8个,判定不是立即数

    例2: 0x122

    第一步: 0001 0010 0010

    第二步: 其中1的个数4个,小于8,继续

    第三步: 其中没有连续大于等于24个0,继续

    第四部: xx01 0010 0010 (高位前面有3个0,大偶数2,去掉2个0)

    第五步: xx10 0011 0010 (低位后面只有1个0,大偶数0)

    第六部: 剩下10 0011 0010 共10位,大于8,判定0x122不是立即数

    例3: 0x234

    第一步: 0010 0011 0100

    第二步: 其中1的个数4个,小于8,继续

    第三步: 其中没有连续大于等于24个0,继续

    第四部: xx10 0011 0100

    第五步: xx10 0011 01xx

    第六部: 剩下10 0011 01 共8位,等于8,判定0x234是立即数

    例4: 0xF000000F

    第一步: 1111 0000 0000 0000 0000 0000 0000 1111

    第二步: 其中1的个数8个,没有大于8,继续

    第三步: 其中有连续24个0,循环左移4位,使高位全为0

    0000 0000 0000 0000 0000 0000 0000 1111 1111

    第四部: xxxx xxxx xxxx xxxx xxxx xxxx xxxx 1111 1111

    第五步: xxxx xxxx xxxx xxxx xxxx xxxx xxxx 1111 1111

    第六部: 剩下1111 1111共8位,等于8,判定0xF000000F是立即数

    例5: 0x8000007F

    第一步: 1000 0000 0000 0000 0000 0000 0111 1111

    第二步: 其中1的个数8个,没有大于8,继续

    第三步: 其中有连续24个0,循环左移4位,使高位全为0

    0000 0000 0000 0000 0000 0000 0111 1111 1000

    第四部: xxxx xxxx xxxx xxxx xxxx xxxx 0111 1111 10xx

    第五步: xxxx xxxx xxxx xxxx xxxx xxxx 0111 1111 10xx

    第六部: 剩下0111 1111 10共10位,等于8,判定0x7000008F是立即数

    问题还没有结束,我们在ARM汇编中如何规避立即数这个问题呢,其实可以使用ARM汇编LDR伪指令,例如直接把MOV指令变为, LDR R1,=0x12345678这样编译器就不会报错了。但这种方法也有弊端会增加开销和影响执行效率。同时ARM汇编中还有有效数的概念,比如 MOV R1,#0xFFFFFFFF 指令中 0xFFFFFFFF 不是立即数,但是是有效数,编译器自动把原指令变换为 MVN R1,#0,也不会报错。有效数判定:原数是立即数或者原数反码是立即数。

    展开全文
  • 程序局部变量存在于栈(.stack)中,全局变量存在于静态区(.bss,.data)中,动态申请数据存在于堆(heap)使用...在C语言的循环中,无论何种循环结构,都会在循环中指明退出条件,这个条件会一个逻辑运算...

    程序的局部变量存在于栈(.stack)中,全局变量存在于静态区(.bss,.data)中,动态申请数据存在于堆(heap)使用(malloc函数申请内存空间)中
    预处理指令#define声明一个常树,用以表明1年中有多少秒

    #define TIME (365x24x60x60)s

    c语言中的死循环
    在C语言的循环中,无论是何种循环结构,都会在循环中指明退出条件,这个条件会是一个逻辑运算表达式。 要做死循环,只需要把对应的退出条件置为恒为真即可。
    以下根据三种循环模式分别描述:
    1、 while循环。
    while循环的判断条件是第一行while后面空格的内容,所以写成
    while(1)
    {
    //循环体
    }
    即可成为一个死循环。这个也是C语言中死循环最常见的模式。
    2、 for循环。
    for循环的判断条件,是for后面括号中的第二条语句。由于for的特殊性,以下两种方式,均可以构建死循环。
    (1) for(expr1; 1; expr2)
    {
    //循环体
    }
    判断条件处写为1, 代表恒为真,与1中while的类似。
    (2) for(expr1; ; expr2)
    由于for允许括号中的三个表达式为空,所以判断部分留空,同样达到死循环的效果。
    3、 do-while循环。
    do-while循环的判断条件为结尾while后的括号中内容。与1类似,do-while死循环可以写作:
    do
    {
    //循环体
    }while(1);
    以上为三种死循环的构建,不过需要说明的是,死循环尽量少用。如果一定要用死循环,也要在循环体内设置退出条件(break)。

    关键字static、Const、Volatile的作用是什么
    在C语言中,关键字static有三个明显的作用:
    1). 在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。
    2). 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。
    3). 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。
    大多数应试者能正确回答第一部分,一部分能正确回答第二部分,同是很少的人能懂得第三部分。这是一个应试者的严重的缺点,因为他显然不懂得本地化数
    据和代码范围的好处和重要性。
    我只要一听到被面试者说:“const意味着常数”,我就知道我正在和一个业余者打交道。去年Dan Saks已经在他的文章里完全概括了const的所有用法,因此ESP(译者:Embedded Systems Programming)的每一位读者应该非常熟悉const能做什么和不能做什么.如果你从没有读到那篇文章,只要能说出const意味着“只读”就可以了。尽管这个答案不是完全的答案,但我接受它作为一个正确的答案。(如果你想知道更详细的答案,仔细读一下Saks的文章吧。)如果应试者能正确回答这个问题,我将问他一个附加的问题:下面的声明都是什么意思?
    const int a;
    int const a;
    const int *a;
    int * const a;
    int const * a const;
    前两个的作用是一样,a是一个常整型数。第三个意味着a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。第四个意思a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。如果应试者能正确回答这些问题,那么他就给我留下了一个好印象。顺带提一句,也许你可能会问,即使不用关键字 const,也还是能很容易写出功能正确的程序,那么我为什么还要如此看重关键字const呢?我也如下的几下理由:
    2). 通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。
    3). 合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少bug的出现。
    一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:
    1). 并行设备的硬件寄存器(如:状态寄存器)
    2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
    3). 多线程应用中被几个任务共享的变量
    假设被面试者正确地回答了这是问题(嗯,怀疑这否会是这样),我将稍微深究一下,看一下这家伙是不是直正懂得volatile完全的重要性。

    int square(volatile int *ptr)
    {
        return *ptr * *ptr;
    }

    **
    下面是答案:
    1). 是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。
    2). 是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。
    3). 这段代码的有个恶作剧。这段代码的目的是用来返指针*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码:

    int square(volatile int *ptr)
    {
        int a,b;
        a = *ptr;
        b = *ptr;
        return a * b;
    }

    由于*ptr的值可能被意想不到地该变,因此a和b可能是不同的。结果,这段代码可能返不是你所期望的平方值!

    展开全文
  • 看门狗,又叫 watchdog timer,一个定时器电路, 一般有一个输入,叫喂狗,一个输出到MCU的RST端,MCU正常工作的时候,每隔一端时间输出一个... 看门狗的作用就是防止程序发生死循环,或者说程序跑飞。此狗非彼狗工作原理...

    看门狗,又叫 watchdog timer,是一个定时器电路, 一般有一个输入,叫喂狗,一个输出到MCU的RST端,MCU正常工作的时候,每隔一端时间输出一个信号到喂狗端,给 WDT 清零,如果超过规定的时间不喂狗,(一般在程序跑飞时),WDT 定时超过,就会给出一个复位信号到MCU,是MCU复位. 防止MCU死机. 看门狗的作用就是防止程序发生死循环,或者说程序跑飞。

    4feaf676f3d0c30594c450dbbab64a6d.png

    此狗非彼狗

    工作原理:在系统运行以后也就启动了看门狗的计数器,看门狗就开始自动计数,如果到了一定的时间还不去清看门狗,那么看门狗计数器就会溢出从而引起看门狗中断,造成系统复位。所以在使用有看门狗的芯片时要注意清看门狗。

    硬件看门狗是利用了一个定时器,来监控主程序的运行,也就是说在主程序的运行过程中,我们要在定时时间到之前对定时器进行复位如果出现死循环,或者说PC指针不能回来。那么定时时间到后就会使单片机复位。常用的WDT芯片如MAX813 ,5045, IMP 813等,价格4~10元不等.

    3b5de8f3fdb4a953c0d784643c4aee67.png

    软件看门狗技术的原理和这差不多,只不过是用软件的方法实现,我们还是以51系列来讲,我们知道在51单片机中有两个定时器,我们就可以用这两个定时器来对主程序的运行进行监控。我们可以对T0设定一定的定时时间,当产生定时中断的时候对一个变量进行赋值,而这个变量在主程序运行的开始已经有了一个初值,在这里我们要设定的定时值要小于主程序的运行时间,这样在主程序的尾部对变量的值进行判断,如果值发生了预期的变化,就说明T0中断正常,如果没有发生变化则使程序复位。对于T1我们用来监控主程序的运行,我们给T1设定一定的定时时间,在主程序中对其进行复位,如果不能在一定的时间里对其进行复位,T1 的定时中断就会使单片机复位。在这里T1的定时时间要设的大于主程序的运行时间,给主程序留有一定的的裕量。而T1的中断正常与否我们再由T0定时中断子程序来监视。这样就构成了一个循环,T0监视T1,T1监视主程序,主程序又来监视T0,从而保证系统的稳定运行。

    51 系列有专门的看门狗定时器,对系统频率进行分频计数,定时器溢出时,将引起复位.看门狗可设定溢出率,也可单独用来作为定时器使用.

    凌阳61的看门狗比较单一,一个是时间单一,第二是功能在实际的使用中只需在循环当中加入清狗的指令就OK了。

    C8051Fxxx单片机内部也有一个21位的使用系统时钟的定时器,该定时器检测对其控制寄存器的两次特定写操作的时间间隔。如果这个时间间隔超过了编程的极限值,将产生一个WDT复位。

    看门狗使用注意:大多数51 系列单片机都有看门狗,当看门狗没有被定时清零时,将引起复位。这可防止程序跑飞。设计者必须清楚看门狗的溢出时间以决定在合适的时候,清看门狗。清理看门狗也不能太过频繁否则会造成资源浪费。程序正常运行时,软件每隔一定的时间(小于定时器的溢出周期)给定时器置数,即可预防溢出中断而引起的误复位。

    看门狗运用:看门狗是恢复系统的正常运行及有效的监视管理器(具有锁定光驱,锁定任何指定程序的作用,可用在家庭中防止小孩无节制地玩游戏、上网、看录像)等具有很好的应用价值.

    e4256e82adc7647de922f306475ab0e0.png

    系统软件"看门狗"的设计思路:

    1.看门狗定时器T0的设置。在初始化程序块中设置T0的工作方式,并开启中断和计数功能。系统Fosc=12 MHz,T0为16位计数器,最大计数值为(2的10次方)-1=65 535,T0输入计数频率是.Fosc/12,溢出周期为(65 535+1)/1=65 536(μs)。

    2.计算主控程序循环一次的耗时。考虑系统各功能模块及其循环次数,本系统主控制程序的运行时间约为16.6 ms。系统设置"看门狗"定时器T0定时30 ms(T0的初值为65 536-30 000=35 536)。主控程序的每次循环都将刷新T0的初值。如程序进入"死循环"而T0的初值在30 ms内未被刷新,这时"看门狗"定时器T0将溢出并申请中断。

    3.设计T0溢出所对应的中断服务程序。此子程序只须一条指令,即在T0对应的中断向量地址(000BH)写入"无条件转移"命令,把计算机拖回整个程序的第一行,对单片机重新进行初始化并获得正确的执行顺序。

    展开全文
  • 而通常在我们使用v-for这个指令的时候,Vue会要求你给循环列表的每一项添加唯一的key,那么这个key在渲染列表时究竟起到了什么作用呢?在解释这一点之前,你最好已经了解Vue的diff算法的具体原理是什么。Vue2更新...

    Vue2+采用diff算法来进行新旧vnode的对比从而更新DOM节点。而通常在我们使用v-for这个指令的时候,Vue会要求你给循环列表的每一项添加唯一的key,那么这个key在渲染列表时究竟起到了什么作用呢?

    在解释这一点之前,你最好已经了解Vue的diff算法的具体原理是什么。

    Vue2更新真实DOM的操作主要是两种:创建新DOM节点并移除旧DOM节点和更新已存在的DOM节点,这两种方式里创建新DOM节点的开销肯定是远大于更新或移动已有的DOM节点,所以在diff中逻辑都是为了减少新的创建而更多的去复用已有DOM节点来完成DOM的更新。

    在新旧vnode的diff过程中,key是判断两个节点是否为同一节点的首要条件:

    // 参见Vue2源码 core/vdom/patch.js

    function sameVnode (a, b){

    return (

    a.key === b.key && (

    (

    a.tag === b.tag &&

    a.isComment === b.isComment &&

    isDef(a.data) === isDef(b.data) &&

    sameInputType(a, b)

    ) || (

    isTrue(a.isAsyncPlaceholder) &&

    a.asyncFactory === b.asyncFactory &&

    isUndef(b.asyncFactory.error)

    )

    )

    )

    }

    复制代码

    值得注意的是,如果新旧vnode的key值都未定义的话那么两个key都为undefined,a.key === b.key 是成立的

    接下来是在updateChildren方法中,这个方法会对新旧vnode进行diff,然后将比对出的结果用来更新真实的DOM

    // 参见Vue2源码 core/vdom/patch.js

    function updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly){

    ...

    while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {

    if (isUndef(oldStartVnode)) {

    ...

    } else if (isUndef(oldEndVnode)) {

    ...

    } else if (sameVnode(oldStartVnode, newStartVnode)) {

    ...

    } else if (sameVnode(oldEndVnode, newEndVnode)) {

    ...

    } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right

    ...

    } else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved left

    ...

    } else {

    if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)

    idxInOld = isDef(newStartVnode.key)

    ? oldKeyToIdx[newStartVnode.key]

    : findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)

    if (isUndef(idxInOld)) { // New element

    createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)

    } else {

    vnodeToMove = oldCh[idxInOld]

    if (sameVnode(vnodeToMove, newStartVnode)) {

    patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)

    oldCh[idxInOld] = undefined

    canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm)

    } else {

    // same key but different element. treat as new element

    createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)

    }

    }

    newStartVnode = newCh[++newStartIdx]

    }

    }

    ...

    }

    复制代码

    设置key的可以在diff中更快速的找到对应节点,提高diff速度

    在updateChildren方法的while循环中,如果头尾交叉对比没有结果,即oldStartVnode存在且oldEndVnode存在且新旧children首尾四个vnode互不相同的条件下,会根据newStartVnode的key去对比oldCh数组中的key,从而找到相应oldVnode

    首先通过createKeyToOldIdx方法创建一个关于oldCh的map

    if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)

    function createKeyToOldIdx (children, beginIdx, endIdx){

    let i, key

    const map = {}

    for (i = beginIdx; i <= endIdx; ++i) {

    key = children[i].key

    if (isDef(key)) map[key] = i

    }

    return map

    }

    复制代码

    这个map中将所有定义了key的oldVnode在数组中的index值作为键值,它的key作为键名存储起来,然后赋给oldKeyToIdx

    idxInOld = isDef(newStartVnode.key) ? oldKeyToIdx[newStartVnode.key] : findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)

    function findIdxInOld (node, oldCh, start, end){

    for (let i = start; i < end; i++) {

    const c = oldCh[i]

    if (isDef(c) && sameVnode(node, c)) return i

    }

    }

    复制代码

    如果newStartVnode的key存在的话,就去oldKeyToIdx中寻找相同key所对应的index值,这样就能拿到跟newStartVnode的key相同的oldVnode在oldCh数组中的index,即得到了与newStartVnode对应的oldVnode。如果找不到的话,那么idxInOld就为undefined。

    而如果newStartVnode并没有设置key,则通过findIdxInOld方法遍历oldCh来获取与newStartVnode互为sameVnode的oldVnode,返回这个oldVnode在oldCh数组的index。(前面介绍过,Vue在更新真实DOM时倾向于真实DOM节点的复用,所以在这里还是会选择去找对应的oldVnode,来更新已有的DOM节点)

    这时候设置key的好处就显而易见了,有key存在时我们可以通过map映射快速定位到对应的oldVnode然后进行patch,没有key值时我们需要遍历这个oldCh数组然后去一一进行比较,相比之下肯定是key存在时diff更高效。

    接下来就是更新DOM的过程,如果oldCh[idxInOld]存在且与newStartVnode互为sameVnode存在则先更新再移动,否则创建新的element

    if (isUndef(idxInOld)) { // New element

    createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)

    } else {

    vnodeToMove = oldCh[idxInOld]

    if (sameVnode(vnodeToMove, newStartVnode)) {

    patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)

    oldCh[idxInOld] = undefined

    canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm)

    } else {

    // same key but different element. treat as new element

    createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)

    }

    }

    复制代码

    那么设置key值就一定能提高diff效率吗?

    答案是否定的

    `

    {{ i }}
    `

    // 如果我们的数组是这样的

    [1, 2, 3, 4, 5]

    // 它的渲染结果是这样的

    `

    1
    ` // key: undefined

    `

    2
    ` // key: undefined

    `

    3
    ` // key: undefined

    `

    4
    ` // key: undefined

    `

    5
    ` // key: undefined

    // 将它打乱

    [4, 1, 3, 5, 2]

    // 渲染结果是这样的 期间只发生了DOM节点的文本内容的更新

    `

    4
    ` // key: undefined

    `

    1
    ` // key: undefined

    `

    3
    ` // key: undefined

    `

    5
    ` // key: undefined

    `

    2
    ` // key: undefined

    // 如果我们给这个数组每一项都设置了唯一的key

    [{id: 'A', value: 1}, {id: 'B', value: 2}, {id: 'C', value: 3}, {id: 'D', value: 4}, {id: 'E', value: 5}]

    // 它的渲染结果应该是这样的

    `

    1
    ` // key: A

    `

    2
    ` // key: B

    `

    3
    ` // key: C

    `

    4
    ` // key: D

    `

    5
    ` // key: E

    // 将它打乱

    [{id: 'D', value: 4}, {id: 'A', value: 1}, {id: 'C', value: 3}, {id: 'E', value: 5}, {id: 'B', value: 2}]

    // 渲染结果是这样的 期间只发生了DOM节点的移动

    `

    4
    ` // key: D

    `

    1
    ` // key: A

    `

    3
    ` // key: C

    `

    5
    ` // key: E

    `

    2
    ` // key: B

    复制代码

    我们给数组设置了key之后数组的diff效率真的变高了吗?

    并没有,因为在简单模板的数组渲染中,新旧节点的key都为undefined,根据sameVnode的判断条件,这些新旧节点的key、tag等属性全部相同,所以在sameVnode(oldStartVnode, newStartVnode)这一步的时候就已经判定为对应的节点(不再执行头尾交叉对比),然后直接进行patchVnode,根本没有走后面的那些else。每一次循环新旧节点都是相对应的,只需要更新其内的文本内容就可以完成DOM更新,这种原地复用的效率无疑是最高的。

    而当我们设置了key之后,则会根据头尾交叉对比结果去执行下面的if else,进行判断之后还需要执行insertBefore等方法移动真实DOM的节点的位置或者进行DOM节点的添加和删除,这样的查找复用开销肯定要比不带key直接原地复用的开销要高。

    Vue文档中对此也进行了说明:

    当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用“就地复用”策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。

    这个默认的模式是高效的,但是只适用于不依赖子组件状态或临时 DOM 状态 (例如:表单输入值) 的列表渲染输出。

    建议尽可能在使用 v-for 时提供 key,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。

    所以,简单列表的渲染可以不使用key或者用数组的index作为key(效果等同于不带key),这种模式下性能最高,但是并不能准确的更新列表项的状态。一旦你需要保存列表项的状态,那么就需要用使用唯一的key用来准确的定位每一个列表项以及复用其自身的状态,而大部分情况下列表组件都有自己的状态。

    总结

    key在列表渲染中的作用是:在复杂的列表渲染中快速准确的找到与newVnode相对应的oldVnode,提升diff效率

    展开全文
  • 而通常在我们使用v-for这个指令的时候,Vue会要求你给循环列表的每一项添加唯一的key,那么这个key在渲染列表时究竟起到了什么作用呢?在解释这一点之前,你最好已经了解Vue的diff算法的具体原理是什么。Vue2更新...
  • 循环的优化

    2011-01-14 21:46:00
    其实在嵌入式开发中,这样优化有必要,因为在程序运行时,循环代码执行时间在程序总执行时间中占了很大比重,因而循环代码优化对程序速率提高有很大作用。在数据处理过程中,循环体用得较多,处理量较...
  • 雨课堂: 循环结构可以使用Python语言中( )语句...答:ABC双列直插式器件的封装以下的哪一项:答:DIP中国大学MOOC: 保存正在执行指令的寄存器_____。答:IR会计核算流程正确的答:确认→计量→记录→报告在Wind...
  • 而通常在我们使用v-for这个指令的时候,Vue会要求你给循环列表的每一项添加唯一的key,那么这个key在渲染列表时究竟起到了什么作用呢? 在解释这一点之前,你最好已经了解Vue的diff算法的具体原理是什么。 Vue2更新...
  • 而通常在我们使用v-for这个指令的时候,Vue会要求你给循环列表的每一项添加唯一的key,那么这个key在渲染列表时究竟起到了什么作用呢? 在解释这一点之前,你最好已经了解Vue的diff算法的具体原理是什么。 Vue2更新...
  • 需要注意的,如果执行的 native 方法,那么程序计数器记录的 undefined 地址,只有执行的 Java 代码时程序计数器记录的才下一条指令的地址。 所以,程序计数器私有主要为了线程切换后能恢复到正确的执行...
  • 什么是循环展开? 循环展开,英文中称Loop unwinding或loop unrolling,一种牺牲程序的尺寸来加快程序的执行速度的优化...我们直接以实际代码向大家展示循环展开的作用,首先看未经过循环展开优化的代码: #includ
  • GPU的作用及工作原理

    2021-01-19 21:29:38
    因需要发送指令(instruction)运行,解码(decode)和执行指令(execute)过程必不可少,而着色器代码中判断语句和循环语句也会引起执行次序小问题等等。这意味着着色器已经能成为一个小型计算引擎去执行任何...
  • 因需要发送指令(instruction)运行,解码(decode)和执行指令(execute)过程必不可少,而着色器代码中判断语句和循环语句也会引起执行次序小问题等等。这意味着着色器核心已经能成为一个小型计算引擎去执行...
  • 文章目录Vue 是什么指令v-cloakv-textv-htmlv-prev-once双向数据绑定v-modelmvvmv-onv-on事件函数中传入参数事件修饰符按键修饰符自定义按键修饰符别名v-bind绑定对象绑定class绑定对象和绑定数组 区别绑定style...
  • 关于前者,没弄明白是什么意思,就知道跟atomic是差不多的作用(但既然作用差不多那这个功能到底与之有什么区别呢?); 关于后者,按我的理解,使用后一条指令之后循环就只能按顺序进行了,那样的话在这里做的并行...
  • 汇编4--[BX]和loop指令

    2017-02-10 23:05:12
    [bx]的作用就是代替汇编指令中要用到的像[0]这样的内存单元,在使用前先向bx传送值。上面的汇编指令可以改写成 mov bx,0 mov ax,[bx].4.2loop指令 作用相当于c语言中的while,让某一个语句循环执行,在汇编语言中
  • 问:Vue实例的作用范围是什么呢? 答:Vue会管理el选项命中的元素及其内部的后代元素。 问:是否可以使用其他的选择器? 答:可以使用其他的选择器,但是建议使用ID选择器。 问:是否可以设置其他的dom元素呢? 答:...
  • 答:一直为1,需要赋值为0才会变化问:这个死循环指令在做项目时候有什么作用啊?答:当要与初始化程序隔开时问:进入死循环,这个中断程序怎么返回呢?答:有相应返回指令,也可以用跳转指令问:中断程序里面...
  • 相信你们也遇到过,当将两者同时都写在一个标签里面时候,就会出现红线警告,原因是什么呢?怎么去解决呢?下面本文就这个问题进行解释。 一、作用 v-if 指令用于在满足条件下渲染内容。这块内容只会在表达式为...
  • 课程重点:1.NOP的在分析数据中的作用2.NOP的时候所要注意的地方3.为何找喊话内容会找到结构呢,因为我们查找的控件中的值.4.通过结构我们可以遍历所有的控件.5.读取以及访问的区别6.调用CALL时,如果参数的数量正确...
  • //i++ 起延时作用 ds=1;i++;i++; dat=ds; i=8;while(i>0)i--; return (dat); } /////////////////////////////////18B20读1个字节//////////////////////////////////// uchar tempread(void) { ...
  • 14.3 是什么使类成为集合:IEnumerable 392 14.3.1 foreach和数组 392 14.3.2 foreach和IEnumerable 393 14.3.3 foreach循环内不要修改集合 396 14.4 标准查询操作符 397 14.4.1 使用Where()来...
  • 字节码解释器通过改变程序计数器来依次读取指令,从而实现代码流程控制,如:顺序执行、选择、循环、异常处理。 在多线程情况下,程序计数器用于记录当前线程执行位置,从而当线程被切换回来时候能够知道该...
  • 6.keep-alive的作用是什么? 答:keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。 7.如何获取dom? 答:ref=“domName” 用法:this.$refs.domName 8.说出几种vue当中的指令和它的...
  • 对于goto心得

    2007-08-31 08:55:00
    goto语句,在我所受的教育中,都不提倡的。应为,它很容易打破程序的可阅读性。所以我 也一直没有用goto。...跳转指令在汇编里有着很大的作用。我们常用的条件跳转,符合什么条件时候就跳到那一段。这样
  • 可以看作是当前线程所执行的字节码的行号指示器,通过程序计数器知道当前线程接下来要执行什么指令,比如分支、循环、跳转、异常处理等等;程序计数器的作用是给线程用的,所以它是线程私有的。该内存区域...
  • 27、GC是什么? 为什么要有GC?  GC是垃圾收集意思(Gabage Collection),内存处理是编程人员容易出现问题地方,忘记或者错误内存回收会导致程序或系统不稳定甚至崩溃,Java提供GC功能可以自动监测对象...
  • 可以说,这些答案都从某个侧面回答了“C++是什么”,但是又都不全面。  在这里,我不能、也不会给你所谓完美答案。在这里,我能够做到,就是给你描绘美好C++世界,讲述其中各色公民、风俗见闻,为各位...
  • 1.4 新64位机上64位类型是什么? 指针声明 1.5 这样声明有什么问题?char*p1,p2;我在使用p2时候报错了。 1.6 我想声明一个指针,并为它分配一些空间,但却不行。这样代码有什么问题?char*p;*p=...

空空如也

空空如也

1 2 3 4 5 ... 8
收藏数 149
精华内容 59
关键字:

循环指令的作用是什么