while
即先检测退出条件,再执行循环体内的代码。。while语句代码有可能不执行的情况。
for
无法通过while循环实现的逻辑,同样无法使用for循环实现。for循环只是将循环相关的代码封装在了一起而已。 一般i使用let声明 ,尽量避免使用var(可联系上文var声明关键字理解)
层次for循环使用就是递归以及双重for循环、
[for循环打印案例]((68条消息) js for循环创造小星星⭐⭐⭐_秃头男神的博客-CSDN博客)
for-in
严格的迭代语句,用于枚举对象中的非符号键属性。 for in循环里属性变量尽量使用const
for-of
严格的迭代语句,用于遍历可迭代对象的元素
无法直接遍历对象。只能用于遍历数组中的对象
break和continue语句
break用于立即退出循环,强制执行循环后的下一条语句。
之所以为4,是因为当i等于5时break语句会导致循环退出,该次循环不会执行递增num的代码。
continue语句也用于立即退出循环,但会再次从循环顶部开始执行。
当i等于5时 continue会退出 执行下一次迭代 重新从顶部开始。最终因为continue使其少递增一次,所以为8
with
不推荐使用 影响性能且难于调试 用于多个相同api调用方法
switch
适合处理多个if else判断条件情况下使用
为避免不必要条件判断,最好每个条件后面加break。如果需要匹配多个条件那么推荐写个注释表明故意忽略break
条件判断中能够使用表达式,可以在判断中加入更多逻辑。
函数
函数对于任何语言都是核心组件。可封装语句,然后再任何地方、时间执行。
只要遇到return 语句,函数立即停止执行并退出
变量 作用域 内存
原始值与引用值
动态属性
原始值类型不可以添加属性,引用值可随意添加属性。
复制值
原始值类型复制过去,相当于复制一份全新空间的值。 引用值类型复制过去,其实就是将一个空间的指针(指向这个空间)
传递值
原始值传递是没有什么需要注意的,主要是对象传值 需要注意。
上图,创建了一个对象,并把它保存在标量person中。然后将其作为参数传给函数方法,并被复制到参数a中。在函数方法中a和person都指向一个对象。即使对象是按值传进函数,a也会通过引用访问对象。当函数内部给obj设置name属性时,函数外部的对象也会反映这个变化,因为obj指向的对象保存在全局作用域的堆内存上。(不是固定的 是可以被修改的 是会变得 是会导致bug原因)
上图与上上图区别变化是set函数方法内多了两行代码,将obj重新定义为一个有着不同name的新对象。当person传入set方法时,其name属性被设置成了‘che’,然后变量a被设置为一个新对象且name属性被设置为‘hu’。如果person是按引用传递的,那么打印的应该是‘hu’而不是‘che’。可是当访问时,a的值还是‘che’,这表明函数中参数的值改变之后,原始的引用依然没变。当obj在函数内部重写时,它变成了一个指向本地对象的指针。而那个本地对象在函数执行结束时就被销毁了、
函数的参数就是局部变量
确定类型
执行上下文与作用域
变量或函数的上下文决定它们可以访问哪些数据,以及它们的行为。每个上下文都有一个关联的变量对象,而这个上下文中定义的所有变量和函数都存在于这个对象上。虽然无法通过代码访问变量对象,但后台处理数据会用到它。
全局上下文是最外层的上下文。在浏览器中,全局上下文就是window对象,所有通过var定义的全局对象和函数都会成为window对象的属性和方法(和原型一样原理 [参照这个)](方法原型对象使用 - 掘金 (juejin.cn)) 。 如下图
上下文在其代所有代码都执行完毕后会被销毁,包括定义在它上面的所有变量和函数(全局上下文在应用程序退出前才会被销毁,比如关闭网页或退出浏览器)。
每个函数调用都有自己上下文,当代码执行流进入函数时,函数的上下文被推到一个上下文栈上。在函数执行完后,上下文栈会弹出该函数上下文(即销毁),将控制权返还给之前的执行上下文。
上下文中的代码在执行的时候,会创建变量的一个作用域链。这个作用域链决定了各级上下文中的代码在访问变量和函数时的顺序。代码正在执行的上下文的变量对象始终位于作用域链的最前端。如果上下文是函数,则其活动对象用作变量对象。活动对象最初只有一个定义变量:arguments。(全局上下文中没有这个变量。)作用域链中的下一个变量对象来自包含上下文,再下一个对象来自再下一个包含上下文。以此类推直至全局上下文;全局上下文的变量对象始终是作用域链的最后一个变量对象。
代码执行时的标识符解析是通过沿作用域链逐级搜索标识符名称完成的。搜索过程始终从作用域链的最前端开始,然后逐级往后,直到找到标识符。(如果没有找到标识符,那么通常会报错。)
如上图,函数changeColor的作用域链包括两个对象:一个是它自己的变量对象(就是定义arguments对象的那个),另一个是全局上下文的变量对象。这个函数内部之所以能够访问变量color,就是因为可以在作用域链中找到它。
局部作用域中定义的变量可用于在局部上下文中替换全局变量。
以上代码涉及3个上下文:全局上下文、changeColor()的局部上下文和swapColors()的局部上下文。全局上下文中有一个变量color和一个函数changeColor()。changeColor()的局部上下文中有一个变量anotherColor和一个函数swapColors(),但在这里可以访问全局上下文中的变量color。swapColors()的局部上下文忠有一个变量tempColor,只能在这个上下文中访问到。全局上下文和changeColor()的局部上下文都无法访问到tempColor。而在swapColors()中则可以访问另外两个上下文中的变量,因为它们都是父上下文。下图展示上图的作用域链
矩形表示不同的上下文。内部上下文可以通过作用域链访问外部上下文中的一切,但外 部上下文无法访问内部上下文中的任何东西。上下文之间的连接是线性的、有序的。每个上下文都可以到上一级上下文中去搜索变量和函数,但任何上下文都不能到下一级上下文中去搜索。swapColors()局部上下文的作用域链中有3 个对象:swapColors()的变量对象、changeColor()的变量对象和全局变量对象。swapColors()的局部上下文首先从自己的变量对象开始搜索变量和函数,搜不到就去搜索上一级变量对象。changeColor()上下文的作用域链中只有2 个对象:它自己的变量对象和全局变量对象。因此,它不能访问swapColors()的上下文。
作用域链增强
执行上下文主要有全局上下文和函数上下文两种。有其他方式增强作用域链。 try/catch语句catch块以及with语句. 会在作用域链前端添加一个新的变量对象(临时添加上下文)。对with语句来说,会想作用域链前端添加指定的对象;对catch语句而言,则会创建一个新的变量对象,这个变量对象会包含要抛出的错误对象的声明。
这里,with 语句将location 对象作为上下文,因此location 会被添加到作用域链前端。 buildUrl()函数中定义了一个变量qs。当with 语句中的代码引用变量href 时,实际上引用的是 location.href,也就是自己变量对象的属性。在引用qs 时,引用的则是定义在buildUrl()中的那 个变量,它定义在函数上下文的变量对象上。而在with 语句中使用var 声明的变量url 会成为函数 上下文的一部分,可以作为函数的值被返回;但像这里使用let 声明的变量url,因为被限制在块级作 用域(稍后介绍),所以在with 块之外没有定义。
标记清除
当变量进入上下文,比如在函数内部声明一个变量时,这个变量会被加上存在于上下文中的标记。而在上下文中的变量,逻辑上讲,永远不应该释放它们的内存,因为只要上下文中的代码在执行,就有可能用到它们。当变量离开上下文时,也会被加上离开上下文的标记。
给变量添加标记的方式有很多。比如,当变量进入上下文时,反转某一位;或者可以维护“在上下文中”和“不在上下文中”两个变量列表,可以把变量从一个列表转移到另一个列表。标记过程的实现并不重要,关键是策略。
垃圾回收程序运行的时候,会标记内存中存储的所有变量(标记方法有很多种)。它会将所有在上下文中的变量,以及在上下文中的变量引用的变量的标记去掉。在此之后再被加上标记的变量就是待删除的,原因是任何字啊上下文中的变量都访问不到它们了。随后垃圾回收程序做一次内存清理,销毁带标记的所有值并收回它们的内存。
引用计数
不常用。思路是对每个值都记录它被引用的次数。生命变量并给他赋一个引用值时,这个值的引用数为1。如果同一个值又被赋给另一个变量,那么引用数加1.类似,如果保存对该引用的变量被其他值给覆盖了,那么引用数减1.当一个值得引用数为0时,就说明没办法再访问到这个值,因此可以安全收回其内存。垃圾回收程序下次运行的时候就会释放引用数为0的内存。
内存管理
通过const和let声明提升性能 let和const都以块(非函数)为作用域,相对于使用var,使用这两个新关键字可能会更早的让垃圾回收程序接入,尽早回收应该回收的内存。在块作用域比函数作用域更早终止的情况下,这就有可能发生。
隐藏类和删除操作 v8引擎。运行期间,v8会将创建的对象与隐藏类关联起来,以跟踪它们的属性特征。能够共享相同隐藏类的对象性能会更好,v8基于此优化,不一定总能做到。
v8会在后台配置,让这两个类实例共享相同的隐藏类,因为这两个实例共享同一个构造函数和原型。假设后面添加一行代码
此时两个Article实例就会对应两个不同的隐藏类。根据这种操作的频率和隐藏类的大小,这有可能对性能产生明显影响。解决方案就是避免javascript的“先创建再补充”式的动态属性赋值,并在构造函数中一次性声明所有属性,如下
这样,两个实例基本上一样了,因此可以共享一个隐藏类,从而带来潜在的性能提升。 如果使用delete关键字会导致生成相同的隐藏类片段。
在代码结束后,即使两个实例使用了同一个构造函数,它们也不再共享一个隐藏类。动态删除属性与动态添加属性导致的后果一样。最佳实践是把不想要的属性设置为null。这样可以保持隐藏类不变和继续共享,同时也能达到删除引用值供垃圾回收程序回收的效果。
内存泄漏
javascript中的内存泄漏大部分是由不合理的引用导致的。
意外全局变量 定时器 回调使用上级上下文的变量。只要定时器一直运行,回调函数中引用的变量就会一直占用内存。 使用闭包也会在不知不觉造成内存泄漏
调用outer()会导致分配name的内存被泄露。以上代码执行后创建了一个内部闭包,只要返回的函数存在就不能清理name,因为闭包一直在引用着他。如果name变量值很大,就是大问题。
静态分配与对象池
压榨浏览器。关键是如何减少浏览器执行垃圾回收的次数。开发者无法直接控制什么时候开始手机垃圾,但可以间接控制触发垃圾回收的条件。理论上,能够合理使用分配的内存,同时避免多余垃圾回收,那就可以保住因释放内存而损失的性能。
浏览器决定何时运行垃圾回收程序的一个标准就是对象更替的速度。如果有很多对象被初始化,然后一下子又都超出了作用域,那么浏览器就会采用更激进的方式调度垃圾回收程序运行,这样当然会影响性能。
调用这个函数时,会在堆上创建一个新对象,然后修改它,最后再把它返回给调用者。如果这个矢量对象的生命周期很短,那么它会很快失去所有对它的引用,成为可以被回收的值。假如这个矢量加法频繁被调用,那么垃圾回收调度程序会发现这里对象更替的速度很快,从而更频繁地安排垃圾回收。该问题解决方案是不要动态创建矢量对象,比如可以修改上面的函数,让他使用一个已有的矢量对象。
上图 这里需要在其他地方实例化矢量参数resultant,但这个函数的行为没有变。
怎么创建矢量不让垃圾回收调度程序盯上呢? 一个策略是适用对象池、在初始化的某一时刻,可以创建一个对象池,用来管理一组可回收的对象。应用层序可以向这个对象池请求一个对象、设置其属性、使用它,然后在操作完成后再把它还给对象池。由于没发生对象初始化,垃圾回收探测就不会发现有对象更替,因此垃圾回收程序就不会那么频繁地运行。
如果对象池只按需分配矢量(在对象不存在时创建新的,在对象存在时复用存在的),那么这个实现本质上是一种贪婪算法,有单调增长但为静态的内存。这个对象池就必须使用某种结构维护所有对象,数组是比较好的选择。不过,使用数组来实现,必须留意不要招致额外垃圾回收
由于javascript数组大小是动态变化的,引擎会删除大小为100的数组,再创建一个新的大小为200的数组。垃圾回收程序会看到这个删除操作,说不定因此很快会跑来收一次垃圾。要避免这种动态分配操作,可以在初始化时就创建一个大小够用的数组,从而避免上图代码先删除再创建的操作。
基本引用类型
引用值(或者对象)是某个特定引用类型的实例。引用类型是把数据和功能组织到一起的结构。虽然从技术上讲
Regexp
[参考]((68条消息) 正则表达式 RegExp【详解】_朝阳39的博客-CSDN博客_regexp)
实例属性
如上图,第一个模式是通过字面量创建,第二个通过RegExp构造函数创建的,两个模式的source和flags属性是相同的。source和flags属性返回的是规范化之后的可以在字面量中使用形式。
regexp实例方法
exec()
regexp构造函数属性
这些属性适用于作用域中的所有正则表达式,而且会根据最后执行的正则表达式操作而变化。可以通过两种不同方式访问。
常用正则
1、检验数字的表达式
1 数字:^[0-9]*$
2 n位的数字:^\d{n}$
3 至少n位的数字:^\d{n,}$
4 m-n位的数字:^\d{m,n}$
5 零和非零开头的数字:^(0|[1-9][0-9]*)$
6 非零开头的最多带两位小数的数字:^([1-9][0-9]*)+(.[0-9]{1,2})?$
7 带1-2位小数的正数或负数:^(-)?\d+(.\d{1,2})?$
8 正数、负数、和小数:^(-|+)?\d+(.\d+)?$
9 有两位小数的正实数:^[0-9]+(.[0-9]{2})?$
10 有1~3位小数的正实数:^[0-9]+(.[0-9]{1,3})?$
11 非零的正整数:^[1-9]\d*$ 或 ^([1-9][0-9]*){1,3}$ 或 ^+?[1-9][0-9]*$
12 非零的负整数:^-[1-9][]0-9"*$ 或 ^-[1-9]\d*$
13 非负整数:^\d+$ 或 ^[1-9]\d*|0$
14 非正整数:^-[1-9]\d*|0$ 或 ^((-\d+)|(0+))$
15 非负浮点数:^\d+(.\d+)?$ 或 ^[1-9]\d*.\d*|0.\d*[1-9]\d*|0?.0+|0$
16 非正浮点数:^((-\d+(.\d+)?)|(0+(.0+)?))$ 或 ^(-([1-9]\d*.\d*|0.\d*[1-9]\d*))|0?.0+|0$
17 正浮点数:^[1-9]\d*.\d*|0.\d*[1-9]\d*$ 或 ^(([0-9]+.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*.[0-9]+)|([0-9]*[1-9][0-9]*))$
18 负浮点数:^-([1-9]\d*.\d*|0.\d*[1-9]\d*)$ 或 ^(-(([0-9]+.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*.[0-9]+)|([0-9]*[1-9][0-9]*)))$
19 浮点数:^(-?\d+)(.\d+)?$ 或 ^-?([1-9]\d*.\d*|0.\d*[1-9]\d*|0?.0+|0)$
2、检验字符的表达式
1 汉字:^[\u4e00-\u9fa5]{0,}$
2 英文和数字:^[A-Za-z0-9]+$ 或 ^[A-Za-z0-9]{4,40}$
3 长度为3-20的所有字符:^.{3,20}$
4 由26个英文字母组成的字符串:^[A-Za-z]+$
5 由26个大写英文字母组成的字符串:^[A-Z]+$
6 由26个小写英文字母组成的字符串:^[a-z]+$
7 由数字和26个英文字母组成的字符串:^[A-Za-z0-9]+$
8 由数字、26个英文字母或者下划线组成的字符串:^\w+$ 或 ^\w{3,20}$
9 中文、英文、数字包括下划线:^[\u4E00-\u9FA5A-Za-z0-9_]+$
10 中文、英文、数字但不包括下划线等符号:^[\u4E00-\u9FA5A-Za-z0-9]+$ 或 ^[\u4E00-\u9FA5A-Za-z0-9]{2,20}$
11 可以输入含有^%&',;=?$"等字符:[^%&',;=?$\x22]+
12 禁止输入含有~的字符:[^~\x22]+