zoukankan      html  css  js  c++  java
  • 前端冷知识

    1、看起来一样的字符串却不相等

    第一次遇到这种情况的同学可能会一脸懵,其实这主要是字符编码不同造成的
    (这种情况可能发生在字符串来自不同地方的时候,如 文件的名称,html中的textContent,js等)

    通过 encodeURIComponent 将两个字符串进行编码,你会发现编码结果不同:
    console.log(str1, str2, encodeURIComponent(str1), encodeURIComponent(str2), str1 === str2);

    经过测试,可以使用正则去掉空白类字符(也许尽管看起来没有空白字符)再比较,或者使用 localeCompare 比较两个字符串:str1.localeCompare(str2)===0

    2、Storage的页面共享问题

    通常,用户登录后会将token等登录信息缓存在sessionStorage中,这样用户关闭网页清理token,而刷新网页可以不用登录。似乎很完美。但:(注:以下新页面均是同源页面)

    window.open()  打开新的页面;a标签 跳转新页面;ctrl + 鼠标右键点击 打开新页面。
    这三种情况,新页面会复制已有的sessionStorage——但仅仅是复制,如果原页面sessionStorage变了,新页面是不会同步变的。那么,如果用户在原页面退出了登录,新页面却还保留了token(甚至有可能是合法的token),是不是很糟糕!

    鼠标右键点击连接,弹出上下文菜单,再点击“新窗口(或标签页)打开链接”,打开新页面; 复制原页面网址,打开一个新的空的标签页,然后粘贴此网址,打开新页面。
    这两种情况,sessionStorage不会被复制。也就是你需要再新页面重新登录!
    另外Electron 将前端代码打包成桌面应用,window.open打开新标签页,也不会被复制。这是更糟糕的情况。

    采用 localStorage,这是所有页面共享的,无论以何种方式打开的不同页面,无论以何种方式修改了localStorage,所有页面都会同步变化。
    你可测试,几乎所有的知名网站 用户登录信息都是所有页面同步共享的(这个页面切换用户了,另一个页面也会变化),所以不要再用sessionStorage保存用户信息了!!

    注意localStorage和sessionStorage的这个区别,将有助于你更好的做决策。

    3、Map 和 Object互转问题

    Map类型 和 普通Object 类型,都是键值对的形式,他们之间需要互转的场景也很多。如果你还在用for循环互转,那么这些技巧能帮你简化代码:

    • 键值对entry,实际上就是一个  [ key , value ] 这样的数组
    • new Map( iterableEntries ) 可以直接创建一个map,传入的参数是每个元素都是entry的可迭代对象,如数组,set,其他map 等。
    • Object.fromEntries( iterableEntries ) 可以直接创建一个对象。传入的参数和上面的完全一致
    • Object.entries(obj)  和 map.entries() 返回都是可迭代对象(每个元素都是entry的

    于是就可以很优雅的实现两者的互转:

    // map 转 obj:
    const obj = Object.fromEntries(map);
    const obj = Object.fromEntries(map.entries())
    // obj 转 map: const map = new Map(Object.entries(obj));

    以及更多其他的互转:

    // 数组 转 map或obj
    const arr = [['a',1],['b',2],['c',3]]
    const map = new Map(arr)
    const obj = Object.fromEntries(arr)
    
    // for of 循环的解构赋值
    for(const [k,v] of map){
        console.log(k,v)
    }

    4、window.parent

    当前窗口的父窗口对象(如果没有父窗口,就是自身。)

    window.parent === window 表明当前窗口不是在 iframe object frame这些标签下【参考】,这在避免自己的网站被恶意引用(如劫持类的攻击)时很有用。

    5、多重循环中的label

    在循环语句中,有两个重要的控制语句 break 和 continue,表示 退出当前循环 和 跳过本次循环进行下一个。

    在多重循环中,使用label 控制非常方便。如果你还在用一些中间flag来传递控制,知道这个技巧尤为重要。举个例子,还好感受一下吧:

    loop1:
    for (i = 0; i < 3; i++) {     
       loop2:
       for (j = 0; j < 3; j++) {  
          if (i == 1 && j == 1) {
             break loop1;       // 这里直接退出外层循环   
             // continue loop1;   // 这里退出内层循环,继续外层的下一条循环
          }
          console.log("i = " + i + ", j = " + j);
       }
    }

    6、try 语句块中的异步

    直接上代码:

    try {
        Promise.reject(new Error('test error'))
    } catch (err) {
        console.log('catch eror:' + err.message)
    } finally {
        console.log('finally')
    }

    这段代码,只会打印出 finally,然后提示异常未捕获。这里的try语句只能捕获同步的异常

    (async function () {
        try {
            await Promise.reject(new Error('test error'))
        } catch (err) {
            console.log('catch eror:' + err.message)
        } finally {
            console.log('finally')
        }
    })()

    这段代码能和你期望的一样:先打印出catch eror:test error,再打印出 finally。能成功捕获异步异常!

    7、?? 和 ?.

    这两个都是判断一个变量是否存在,是否为nullish(null或undefined),但结果是相反的。??是nullish时为true,?.不是nullish时才为true:

     ?.  又叫可选链操作符;   ??  控制合并运算符;   ??=  逻辑空赋值。示例:

    console.log(obj?.a)     // obj不是nullish时 打印obj.a,否则打印undefined
    obj.dog?.['name']    // dog属性存在的话,取其name(注意写法,不是obj.dog?['name'], ?.是一体的),[]里面是表达式,表达式的结果作为key的name。
    // 如果dog不存在,后面的表达式将不会计算,类似于if else 直接跳过!
    
    obj.add?.(1,2,3)    // 有add这个方法的,就调用这个方法。注意 如果存在add,但add不是方法,这会报错。()表示作为方法来调用它。
    
    // ?.不能用于赋值   obj?.name = 'Jack'  这是错误的!
    let foo = null ?? 'default string';    // ?? 则表示是nullish时,取后面的结果否则取前面的结果!
    const baz = 0 ?? 42;     // 结果0  (0 不是 nullish)
    console.log( B() ?? C() );    // B()返回的不是undefined或null, C方法将不会调用!
    (null || undefined ) ?? "foo"; // ||或&&或关系比较 都不能与??直接连用,需要借助括号     
           
    a.duration ??= 10;    // a.duration是nullish时才赋值,等价于
    a.duration??(a.duration=10) 

    在复杂表达式中为了避免空值引起的异常,基本都用&&或||的技巧,但这任然很麻烦,而且无法很好的规避0,false,NaN这些合法的值,现在有了?和?? 让一起再次简单了起来! 

    高级示例:

    // 当变量age存在时(存在包括0,NaN的字符串),调用验证函数validateAge:
    age?.[validateAge()]
    
    // 当age不存在时,弹出提示:
    age??alert("年龄不存在")

     8、基础类型添加属性的问题

    基础类型如:number、string、boolean、bigint、symbol

    通过new创建的Number、String、Boolean 是对象,所以可以添加任意的属性。

    let a = 5
    a.name="a"   // 严格模式下报错,非严格模式下静默失败!
    console.log(a,a.name)

    更多冷知识,正在收录中……

  • 相关阅读:
    Linux下编译LibCURL
    Linux下编译OpenSSL
    Linux下编译UnixODBC
    Linux下编译Boost
    MySQL存储引擎【InnoDB、MyISAM、Memory】
    MySQL数据库MyISAM和InnoDB存储引擎的比较
    MySQL存储引擎MyISAM与InnoDB的优劣
    14款经典的MySQL客户端软件
    MySQL 数据类型
    MySQL数据库的基本数据类型
  • 原文地址:https://www.cnblogs.com/zhwc-5w4/p/13087007.html
Copyright © 2011-2022 走看看