前端阿瓜每周速记(2020 第 34 周)

353 阅读8分钟

本文首发在我的博客:前端阿瓜每周速记(2020 第 34 周)

本瓜准备开始写这样的一个系列:《前端阿瓜每周速记》

用于记录每周或亲身经历、或道听途说、或意外所感的有关前端技术二三。

坚持一周一更!以期同各位相好们,哦,不,同各位同好们分享交流 ~

  • 为什么是速记呢?

毕竟不是全职写文,工作生活之余,遇到自己想写的,又不想随便水一水、或只做一个搬运工,往往需要查阅大量相关知识来吸收、总结、抽离、创新,时间上太紧,难成好作。

想去探索的点却又繁多、冗杂,食之不易,弃之可惜!遂速记之,抛砖引玉,期待日后涓涓细流有幸汇聚成河!

  • 撰文不易 点赞鼓励

本系列不会影响长篇输出,如“前端书签整理”、“TS 学习”、“Vue3 源码”等等,关注不迷路!

值传递、引用传递、解释型、编译型

我知道你知道,JS 值有两大类型:基本类型和引用类型。基本类型按值传递,引用类型按引用传递。非常优秀,背的简直不要太熟,但你有想过为什么要这么划分吗?

这些变量保存在哪里?内存中?

内存的分配策略是如何的呢?

为什么是内存,不是 CPU?不是外存?本瓜在面试中被问过,面试官多半是后端或架构师

不要方,抱紧我。芜湖起飞!🚀

内存分配

程序运行时的内存分配的策略有三种:

  1. 静态存储。
  2. 栈式存储。
  3. 堆式存储。

静态存储分配:是指在编译时就能确定每个数据目标在运行时刻的存储空间需求,因而在编译时就可以给他们分配固定的内存空间.这种分配策略要求程序代码中不允许有可变数据结构(比如可变数组)的存在,也不允许有嵌套或者递归的结构出现,因为它们都会导致编译程序无法计算准确的存储空间需求.

栈式存储分配:也可称为动态存储分配,是由一个类似于堆栈的运行栈来实现的.和静态存储分配相反,在栈式存储方案中,程序对数据区的需求在编译时是完全未知的,只有到运行的时候才能够知道,但是规定在运行中进入一个程序模块时,必须知道该程序模块所需的数据区大小才能够为其分配内存.和我们在数据结构所熟知的栈一样,栈式存储分配按照先进后出的原则进行分配。

堆式存储分配:则专门负责在编译时或运行时模块入口处都无法确定存储要求的数据结构的内存分配,比如可变长度串和对象实例.堆由大片的可利用块或空闲块组成,堆中的内存可以按照任意顺序分配和释放.

JS 是脚本语言,是解释型语言,即运行时翻译。不清楚解释型语言编译型语言的参见这篇。简单来说:解释型语言是边运行边翻译,编译型语言是翻译完后再运行。

那么可推断出内存分配策略采用的是栈式存储堆式存储。(实际上也确实如此)

基础类型因为大小固定,采用栈式存储。定义时,即系统自动分配,可直接访问,遵循后进先出原则。

引用类型因大小不固定,采用堆式存储

JS 不允许直接访问堆内存中的位置,因此我们不能直接操作对象的堆内存空间。

操作对象时,实质上操作的是这个对象的引用,可理解为:在栈内存中的一个指针,指向堆内存的某个地址。

  • 堆栈存储的优缺对比

栈内存优缺:

优: 大小固定有限制,存取快,销毁快。

缺:不够灵活。编译时确定大小,运行时进行分配。

堆内存优缺:

优:编译器不必知道要从堆里分配多少存储空间,保存数据更灵活。

缺:运行时请求操作系统分配给自己内存,分配和销毁都要占用时间,效率非常低。

简单做了个图:

共享传递

来看一道题(来自京东面试):

function changeObjProperty(o) {
  o.siteUrl = "http://www.baidu.com"
  o = {}
  o.siteUrl = "http://www.google.com"
} 
let webSite = {};
changeObjProperty(webSite);
console.log(webSite.siteUrl);

实际上,o 与后传进来的 webSite 的指针相同,o 是 webSite 的指针的副本,修改 o.siteUrl 会改变原指针,而直接修改 o ,不会改变原指针。这种传递值的方式叫做共享传递

Stack Overflow 的解释:对于传递到函数参数的对象类型,如果直接改变了拷贝的引用的指向地址,那是不会影响到原来的那个对象;如果是通过拷贝的引用,去进行内部的值的操作,那么就会改变到原来的对象的。

简单做了个图:

内存管理

几乎所有的语言的内存管理都要进行下面这三个步骤:

  1. 分配你所需要的内存。
  2. 使用分配到的内存(读、写)。
  3. 不需要时将其释放或归还。

为了不让程序员费心分配内存,JavaScript 在定义变量时就完成了内存分配。———— MDN

(os:还用我说什么吗?“JS是最好的语言”打在评论中。)

小结:

这里提到了的内存部分只是冰山一角,深可至 V8 引擎原理、计算机系统原理等。有兴趣的小伙伴可自行探索,CV不是计算机,这才是计算机!

JQuery 1.8.2 XSS 攻击

本司安全组在扫码代码安全时检测出了本瓜所在项目存在 Jqurey 版本过低导致的 XSS 攻击(中危)。SNYK-JS-JQUERY-565129

没道理啊,都是 Vue 项目,哪里来的 JQuery,后来全局搜索发现,还真有!原因是 svn 上后台的老旧模板引用了 JQuery 依赖。(别奇怪,历史遗留问题导致各种情况都会出现。)

自查

不查不知道,一查吓一跳,就连比较新的 JQuery 版本也存在 XSS 漏洞。

例(动动手):

<html>
    <head>
        <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.js"></script>
    </head>
    <body>
        <div id="div"></div>
        <button onclick="test()">xss</button>
        <script>
            function test(){
                    $('#div').html('document.getElementById("div").innerHTML="<style><style /><img src=x onerror=alert(document.cookie)></style>"');
            }
        </script>
    </body>
</html>

不同的版本下,不同的攻击字符串的标签匹配真是五花八门。

JQuery 很多函数/方法都是XSS接收器。所以说在我们用 JQuery 时,必须注意对 DOM 所做的更改以及传递,不要相信用户的任何输入,常升级 JQuery 版本等。(接一个 Vue,渐进式不香吗?后端童鞋学起来!)

更多在 DVWA应用示例,也可借助辅助自查网站 Cross Site Scripting Prevention

类型

回顾三种 XSS 类型:

  1. DOM 型(DOM XSS),即用户输入没有提交到服务器,只是被前端js接收并显示所引起的XSS。

  2. 反射型(Reflected XSS),即用户输入被提交到服务器,服务器将用户输入写入本次请求的response的内引起的XSS。

  3. 存储型(Stored XSS),即用户输入被提交到服务器,服务器将用户输入写入到非本次请求的response的内引起的XSS。

webpack 按需引入、预先加载 import、import ("")

最近在重构个站,重拾 webpack 打包。

按需引入和预先加载

按需引入其实就是“延迟加载”,等到需要用的时候再加载。

预先加载是“提前加载”,把要用的东西先加载准备好。

这二者之前的平衡,决定了 webpack 的打包策略。

import import ("")

前者指的是 ES6 的 import,静态的 import 语句用于导入由另一个模块导出的绑定。一般放在 js 头部。编译时加载

后者指的是 webpack 的import,动态地加载模块。调用 import() 之处,被作为分离的模块起点,意思是,被请求的模块和它引用的所有子模块,会分离到一个单独的 chunk 中。运行时加载

所以,对于按需加载来说:

如果是单页面应用的话,按照路由实现按需加载。监听路由,触发对应的 import('')。

如果是多页面应用的话,在 HtmlWebpackPlugin 中设置 chunks,即可按需加载。

杂问杂记

axios 会手动封装吗?

包含内容:拦截器、错误码、提示、请求类型、请求头、请求取消等。

示例:链接

vuex plugin 了解多少?

核心的 State、Getter、Mutation、Action、Module 用的多,那 Plugin 呢?

解答:

通过 Vuex Plugin 可以轻松实现一些很酷的扩展功能。

例:

  • vuex-persistedstate 实现状态持久化
  • vuex-shared-mutations 同步标签页、窗口
  • vuex-loading 管理多个加载状态

中国有多少市?

你估一下大概有多少地级市呢?如果要用 Echart 绘全国市级别的数据,打算如何处理呢?

后记

哇哈哈,说是速记,此篇也并不是很速记。万事开头难,要求后面越来越精简扼要、快速记录吧!

参考