js红宝书学习笔记(1-6章)

news2024/9/27 17:29:17

就按照原书中写的章节顺序记笔记了, 还有可能我学过js一段时间了,可能有些对于新手的细节会忽略,但是会尽量写全的~

1.第一章 什么是JavaScript

1.1讲了一些历史,所以我们从1.2开始看

1.2 JavaScript的实现

完整的JaveScript包含三部分:

  • 核心:ECMAScript(下文简写为ES)
  • 文档对象模型:DOM
  • 浏览器对象模型:BOM

三者之间的关系:浏览器是ES实现可能存在的一种宿主环境,也就是说浏览器是ES运行的载体;宿主环境提供ES的基准实现和与环境自身交互必须的扩展,即浏览器提供ES实现的环境和交互的实现条件;扩展(如DOM)使用ES核心类型和语法,提供特定于环境的额外功能,即在ES标准的基础上,实现功能。

ECMAScript

ECMAScript定义的部分:语法、类型、语句、关键字、保留字、操作符、全局对象。js不等于ES,js只是实现ES的一种语言。

ES6-ES10,表示的是五个版本,ES6也可以发布年份命名为ES2015,以此类推。ES6是一次最重要的增强特性。支持了类、模块、迭代器、箭头函数、promise等众多新数据类型。

DOM

DOM文档对象模型:提供与网页内容交互的方法和接口。是一个应用编程接口,用于在HTML中使用扩展的XML。

DOM的升级历程:

  1. DOM Core提供一种映射XML文档,方便访问和操作文档任意部分的方式;DOM HTML拓展了前者,并增加了特定于 HTML 的对象和方法。

  2. 新增了以下模块,以支持新的接口。

    DOM视图:描述追踪文档不同视图(如应用css样式前后的文档)的接口。

    DOM事件:描述事件及事件处理的接口。

    DOM样式:描述处理元素CSS样式的接口。

    DOM遍历和范围:描述遍历和操作DOM树的接口。

  3. 进一步扩展DOM,增加了以统一的方式加载和保存文档的方法(包含在一个叫 DOM Load and Save 的新模块中),还有验证文档的方法(DOM Validation)。

  4. 目前,W3C 不再按照 Level 来维护 DOM 了,而是作为 DOM Living Standard 来维护,其快照称为 DOM4。DOM4 新增的内容包括替代 Mutation Events 的 Mutation Observers。

其他DOM:除了DOM Core和 DOM HTML接口,有些其他语言也发布了自己的 DOM 标准。下面列出的语言 是基于 XML 的,每一种都增加了该语言独有的 DOM 方法和接口:

  • 可伸缩矢量图(SVG,Scalable Vector Graphics)
  • 数学标记语言(MathML,Mathematical Markup Language)
  • 同步多媒体集成语言(SMIL,Synchronized Multimedia Integration Language)

此外,还有一些语言开发了自己的 DOM 实现,比如 Mozilla 的 XML 用户界面语言(XUL,XML User Interface Language)。不过,只有前面列表中的语言是 W3C 推荐标准。

BOM

BOM浏览器对象模型:提供与浏览器交互的方法和接口。总体来说,BOM 主要针对浏览器窗口和子窗口(frame),不过人们通常会把任何特定于浏览器的扩展都归在 BOM 的范畴内。比如:

  • 弹出新浏览器窗口的能力;
  • 移动、缩放和关闭浏览器窗口的能力;
  • navigator 对象,提供关于浏览器的详尽信息;
  • location 对象,提供浏览器加载页面的详尽信息;
  • screen 对象,提供关于用户屏幕分辨率的详尽信息;
  • performance 对象,提供浏览器内存占用、导航行为和时间统计的详尽信息;
  • 对 cookie 的支持;
  • 其他自定义对象,如 XMLHttpRequest 和 IE 的 ActiveXObject。

第2章 HTML中的JavaScript

2.1 script元素

将 JavaScript 插入 HTML 的主要方法是使用<script>元素。<script>元素有下列8个属性。

  • async:可选。表示应该立即开始下载脚本,但不能阻止其他页面动作,比如下载资源或等待其他脚本加载。只对外部脚本文件有效。异步脚本保证会在页面的 load 事件前执行。
  • charset:可选使用 src 属性指定的代码字符集。这个属性很少使用,因为大多数浏览器不在乎它的值。
  • crossorigin:可选。配置相关请求的CORS(跨源资源共享)设置。默认不使用CORScrossorigin= "anonymous"配置文件请求不必设置凭据标志。crossorigin="usecredentials"设置凭据 标志,意味着出站请求会包含凭据。
  • defer:可选。表示脚本可以延迟到文档完全被解析和显示之后再执行。只对外部脚本文件有效。
  • integrity:可选。允许比对接收到的资源和指定的加密签名以验证子资源完整性(SRI, Subresource Integrity)。如果接收到的资源的签名与这个属性指定的签名不匹配,则页面会报错, 脚本不会执行。这个属性可以用于确保内容分发网络(CDN,Content Delivery Network)不会提供恶意内容。
  • language:废弃。最初用于表示代码块中的脚本语言(如"JavaScript"、“JavaScript 1.2” 或"VBScript")。大多数浏览器都会忽略这个属性,不应该再使用它。
  • src:可选。表示包含要执行的代码的外部文件。
  • type:可选。代替 language,表示代码块中脚本语言的内容类型(也称 MIME 类型)。按照惯例,这个值始终都是"text/javascript",尽管"text/javascript"和"text/ecmascript" 都已经废弃了。JavaScript 文件的 MIME 类型通常是"application/x-javascript",不过给 type 属性这个值有可能导致脚本被忽略。在非 IE 的浏览器中有效的其他值还有"application/javascript"和"application/ecmascript"。如果这个值是 module,则代码会被当成 ES6 模块,而且只有这时候代码中才能出现 import 和 export 关键字。

包含在script标签中的代码会被从上到下解析,元素中的代码被计算完成之前,页面的其余内容不会被加载,也不会被显示。没有使用 defer 和 async 属性的script元素也是从上到下解析。

所以把script标签写在body标签内页面内容的后面,防止页面长时间空白,如果要使用defer属性,延迟加载脚本,要记得对于 XHTML 文档,指定 defer 属性时应该写成 defer=“defer”

在使用行内 JavaScript 代码时,要注意代码中不能出现字符串</script>这会被当成标签的结束符,需要使用的话要转义<\/script>。使用外部文件中的JavaScript,必须使用src属性指明地址。

动态加载脚本的方法:通过向DOM中动态添加script元素同样可以加载指定的脚本。只要创建一个script 元素并将其添加到 DOM 即可。但是这种方式默认为异步加载。有些浏览器不支持async,可以使用以下设置为同步加载。

以这种方式获取的资源对浏览器预加载器是不可见的。这会严重影响它们在资源获取队列中的优先级。 在文档头部声明 <link rel="preload" href="gibberish.js">

let script = document.createElement('script');
script.src = 'gibberish.js';
script.async = false; //设置为同步加载
document.head.appendChild(script); 

行内代码与外部文件的加载方式,其中外部文件的优点:

  • 可维护性:用一个目录保存,更容易维护,独立于HTML页面来编辑代码
  • 缓存:重复使用多个js文件时会缓存,只需要下载一次
  • 适应未来:放到外部文件中,不必考虑XHTML的语法(与HTML相同)了

3. 语言基础

语法:很大程度上借鉴了 C 语言和其他类 C 语言,如 Java 和 Perl。

  • 区分大小写:ES 中一切都区分大小写。无论是变量、函数名还是操作符,都区分大小写。

  • 标识符:就是变量、函数、属性或函数参数的名称。标识符可以由一或多个下列字符组成:第一个字符必须是一个字母、下划线(_)或美元符号($);剩下的其他字符可以是字母、下划线、美元符号或数字。标识符中的字母可以是扩展 ASCII(Extended ASCII)中的字母,也可以是 Unicode 的字母字符,如 À 和 Æ(但不推荐使用)。

    关键字、保留字、true、false 和 null 不能作为标识符。

  • 注释://单行 /* 多行 */

  • 严格模式:在函数开头或配置文件中指定"use strict"

  • 语句:分号结尾; 代码块用{}包裹;

关键字和保留字:关键字有特殊用途,比如表示控制语句的开始和结束, 或者执行特定的操作。不能用作标识符或属性名。
在这里插入图片描述

变量:声明:var 和 let const,后两者只能在ES6及以上版本中使用

var:可重复声明 有变量提升 没有块级作用域

let/const: 不可重复声明 没有变量提升 块级作用域。(const为常量,不能修改且声明时必须初始化)

ES6的数据类型:Undefined Boolean String Number Object Function Symbol
  • undefined:声明一个变量但没有初始化,等于赋值undefined。但是当用typeof value 检查一个值的时候,返回值为undefined,并不能确定这个变量未赋值还是变量不存在

  • Null:null类型只有一个值null,表示一个空对象指针,一般用null初始化对象。null派生出了undefined,所以null==undefined

  • Boolean:与其他类型数值的转换:转为false的值: 空字符串, 0,NaN,null,undefined

  • Number:默认为十进制,可以通过0开头转为八进制,但在严格模式下无效。八进制和16进制创建的数值在所有数学操作中都会视为十进制进行操作(Infinity无穷 NaN非数字)

  • String:字符串可以用 ‘xx’ “xx” ``这三种包裹。前两种都是普通字符串,最后一种则称为模板字符串。可以在其中使用变量和常量混合的形式,变量用${key}插值方法使用。如:

    const name = 'liqiang';
    `hello, my name is ${name}`
    
  • Symbol:用Symbol()函数初始化(不需要new关键字)。只要创建 Symbol()实例并将其 用作对象的新属性,就可以保证它不会覆盖已有的对象属性,无论是符号属性还是字符串属性。Symbol具有唯一性。

    一些函数:

  • Object:对象类型, 对象就是一组数据和功能的集合。Object实例都会有的方法和属性:

    • constructor:用于创建当前对象的函数
    • hasOwnProperty(name):判断当前对象实例上是否有name属性(不进行原型链查找)
    • isPrototypeOf(object):判断当前对象是否为另一个对象的原型
    • propertyIsEnumerable(name):判断给定的属性是否可以for-in枚举
    • toLocaleString():返回对象的字符串表示
    • valueOf():返回对象对应的字符串、数值或布尔值表示
操作符
  • 一元操作符:递增:a++; ++a; 递减:a–;–a;
  • 位操作符:按位非~;按位与&; 按位或|;按位异或^;左移<<;有符号右移>>;无符号右移>>>;
  • 布尔操作符:逻辑非!;逻辑与&&;逻辑或||(或有惰性,第一个为真直接返回第一个;第一个不为真才去检验第二个);
  • 乘性操作符:乘法*; 除法/; 取余(模)%;
  • 指数操作符:指数**(等同于Math.pow())
  • 加性操作符:加法+;减法-;
  • 关系操作符:大于>;小于<;小于等于<=;大于等于>=
  • 相等操作符:相等==;全等===(比较数据类型);不等于!=;不全等!==;
  • 条件操作符(三元运算符):条件?a:b;条件为真取a,条件为假取b
  • 赋值操作符:赋值=;
  • 逗号操作符:逗号, 在一条语句中执行多个操作,比如连续声明变量let a=1, b, c;
语句
  • if语句:if(表达式){},表达式为真则执行后面的代码块;if-else;或者 if-else if-else 等都可以

  • do-while语句:do{}while(表达式),执行do中的语句,然后判断表达式,直到表达式值为false跳出执行do中的代码块。代码块中的语句至少执行一次

  • while语句:while(表达式){};表达式的值为真则执行代码块中的语句,先判断再执行,最少0次

  • for(初始;条件;操作){}:可以给初值,条件判断为真执行代码块,然后执行操作;再去判断条件,直到条件不符合跳出循环;

  • for(关键字 in 对象){}:遍历对象的所有属性,对象所有可枚举属性都会返回一次

  • for(关键字 of 数组){}:遍历数组的所有元素

  • 标签语句:start: for (let i=0; i<count; i++){} start是个标签,标签语句的典型应用场景是嵌套循环

  • break和continue语句:break直接关闭循环;continue结束本次循环,开始下次循环

  • with语句:多用于对一个对象反复操作时(严格模式禁用)。在使用with的代码块内部,变量首先会被认为是个局部变量,如果未找到则会搜索with(对象)对象身上是否有同名属性。

    with(location){
    	let qs = search.substring(1);
    	let hostName = hostname; //hostname先被认为是否局部变量,没有则查找location.hostname
    }
    
  • switch语句:switch(检验值){ case value: break;} case的用法类似于if,检验值等于value的时候执行下面的语句,break用于跳出switch结构。

函数

结构:function functionName(参数){语句}

第四章 变量、作用域和内存

原始值和引用值

把一个基本数据类型的值赋值给其他变量的时候,赋的是原始值,就是变量实际的值。在操作对象赋值的时候,赋的是引用值,就是让变量也指向那个内存地址。

  • 动态属性:就是给变量添加属性,但是变量必须是对象属性才可以保存,否则是undefined。
  • 复制值:在通过变量把一个原始值(基本数据类型)赋值到另一个变量时,这个值跟存储在原始变量中的值是完全独立的。当把引用值从一个变量复制给另一个变量时,只是复制了一个指针,二者指向同一个位置。
  • 传递参数:ES中所有函数的参数都是按值传递的。也就是说,对于对象来说,对象这个整体是一个引用类型,但是里面的基本数据类型的属性是复制的值,并不是指向。
  • 确定类型:
    • typeof:只能准确判断String、Number、Boolean、undefined;
    • 其他数据类型:instanceof(前面几个也可以用instanceof来判断)。用法:a instanceof Array,返回值Boolean。但是instanceof检验任何引用值和object构造函数都会返回true。

执行上下文与作用域

变量或函数的执行上下文决定了他们可以访问哪些数据以及他们的行为。而我们常说的this指向,也就是执行上下文的一个具体化表现。上下文中的代码在执行的时候,会创建变量对象的一个作用域链。上下文在其所有代码都执行完毕后会被销毁,包括定义在它上面的所有变量和函数

  • 函数上下文:作用域为当前函数。则外部的语句无法访问函数内部的变量。

  • 全局上下文:最外层的上下文(根据宿主环境 全局上下文的对象可能不一样)浏览器中就是window对象,var定义的全局函数和变量会挂在window上,而let和const的顶级声明不会再全局上下文中,但在作用域链解析后效果相同。

    下文中的代码在执行的时候,会创建变量对象的一个作用域链,这个作用域链决定了各级上下文中的代码在访问变量和函数时的顺序。代码正在执行的上下文位于作用域链最前,全局上下文始终在最末。也就是说,从里层向外找,当前没有就向外找一层,直至找到或作用域链结束。

作用域链增强

某些语句会导致在作用域链前端临时添加一个上下文,这个上下文在代码执 行后会被删除。

  • try/catch语句里的catch块:创建一个新的变量对象,包含要抛出的错误对象的声明。
  • with语句:向作用域链前端添加指定的对象
变量声明

ES6新增了let和const声明变量。所以var和let const的比较以前也会经常被问到

  • 作用域:var声明的变量是函数作用域,函数内部可以访问,外部不可以

    ​ let声明的变量是块级作用域,{let a}花括号内部可以访问a,外部不可以

  • 变量提升:var有变量提升,也就是说可以在声明一个变量前读取和使用该变量,但值为undefined

    ​ let没有变量提升,不可以在声明前使用,会报错

  • 重复声明:var可以重复声明,比如 var a ; var a=1;

    ​ let不可以重复声明,会报错

  • const与let类似,唯一区别就是const用来声明常量,声明并赋值,因为后续任何时候都不可以再赋值。但是如果是引用类型,不修改引用地址,只修改内部的属性是不会报错的。

垃圾回收

js有垃圾回收机制,执行环境负责在代码执行时管理内存。通过自动内存管理实现内存分配和闲置资源回收。标记不再使用的变量的两种主要实现方式:标记清理和引用计数

标记清理

垃圾回收程序运行的时候,会标记内存中存储的所有变量(记住,标记方法有很多种)。然后,它会将所有在上下文中的变量,以及被在上下文中的变量引用的变量的标记去掉。在此之后再被加上标记的变量就是待删除的了,原因是任何在上下文中的变量都访问不到它们了。随后垃圾回收程序做一次内存清理,销毁带标记的所有值并收回它们的内存。

js最常用的垃圾回收策略就是标记清理。上下文中的对象在上下文执行结束就会被销毁(如果没有其他地方引用)

引用计数

思路是对每个值都记录它被引用的次数。声明变量并给它赋一个引用值时,这个值的引用数为 1。如果同一个值又被赋给另一个变 量,那么引用数加 1。类似地,如果保存对该值引用的变量被其他值给覆盖了,那么引用数减 1。当一个值的引用数为 0 时,就说明没办法再访问到这个值了,因此可以安全地收回其内存了。垃圾回收程序下次运行的时候就会释放引用数为 0 的值的内存。

这种策略没那么常用,当循环引用的时候,这种策略下,循环引用的变量在函数结束后还会存在,因为它们的引用数永远不会变成 0。如果函数被多次调用,则会导致大量内存永远不会被释放。为避免这种情况,在确认变量不再使用可以赋值为null。

内存管理

解除引用:如果数据不再必要,那么把它设置为 null,从而释放其引用。(这个建议最适合全局变量和全局对象的属性。局部变量在超出作用域后会被自动解除引用)

  • 用const和let声明提升性能:都是块级作用域,与var相比有助于改进垃圾回收的过程。

  • 隐藏类和删除操作:能够共享相同隐藏类的对象性能会更好,也就是说不要对实例先创建再补充属性,而要在构造函数中一次性声明所有属性,听过传值造成差别,这样可以共享一个隐藏类。不需要其中一个实例的时候,不要delete a去删除,而是令a=null。 这样可以保持隐藏类不变和继续共享,也不耽误垃圾回收。

  • 内存泄漏:大部分由于不合理的引用引起。

    • 意外声明全局变量:声明变量时没用任何关键字,解释器会默认为window.a
    • 定时器一直运行:定时器一直运行,会导致垃圾回收一直无法清理其中引用的外部变量。
    • 闭包使用不当:let outer = function() { let name = 'Jake'; return function() { return name; }; }; 调用 outer()会导致分配给 name 的内存被泄漏。以上代码执行后创建了一个内部闭包,只要返回 的函数存在就不能清理 name,因为闭包一直在引用着它。
  • 静态分配与对象池:理论上,如果能合理使用分配的内存,同时避免多余的垃圾回收,就可以保住因释放内存而损失的性能。浏览器决定何时运行垃圾回收程序的一个标准就是对象更替的速度。如果有很多对象被初始化,然后一下子又都超出了作用域,那么浏览器就会采用更激进的方式调度垃圾回收程序运行,这样当然会影响性能。

    • 使用对象池:在初始化的某一时刻,可以创建一个对象池,用来管理一组可回收的对象。 应用程序可以向这个对象池请求一个对象、设置其属性、使用它,然后在操作完成后再把它还给对象池。 由于没发生对象初始化,垃圾回收探测就不会发现有对象更替,因此垃圾回收程序就不会那么频繁地运行。

      如果对象池只按需分配矢量(在对象不存在时创建新的,在对象存在时则复用存在的),那么这个实现本质上是一种贪婪算法,有单调增长但为静态的内存。这个对象池必须使用某种结构维护所有对象,数组是比较好的选择。不过,使用数组来实现,必须留意不要招致额外的垃圾回收。避免动态分配操作,可以在初始化时就创建一个大小够用的数组,从而避免先删除再创建的操作。不过,必须事先想好这个数组有多大。

小结

JavaScript 变量可以保存两种类型的值:原始值和引用值。原始值可能是以下 6 种原始数据类型之 一:Undefined、Null、Boolean、Number、String 和 Symbol。原始值和引用值有以下特点。

  • 原始值大小固定,因此保存在栈内存上。
  • 从一个变量到另一个变量复制原始值会创建该值的第二个副本。
  • 引用值是对象,存储在堆内存上。
  • 包含引用值的变量实际上只包含指向相应对象的一个指针,而不是对象本身。
  • 从一个变量到另一个变量复制引用值只会复制指针,因此结果是两个变量都指向同一个对象。
  • typeof 操作符可以确定值的原始类型,而 instanceof 操作符用于确保值的引用类型。

任何变量(不管包含的是原始值还是引用值)都存在于某个执行上下文中(也称为作用域)。这个 上下文(作用域)决定了变量的生命周期,以及它们可以访问代码的哪些部分。执行上下文可以总结如下。

  • 执行上下文分全局上下文、函数上下文和块级上下文。
  • 代码执行流每进入一个新上下文,都会创建一个作用域链,用于搜索变量和函数。
  • 函数或块的局部上下文不仅可以访问自己作用域内的变量,而且也可以访问任何包含上下文乃 至全局上下文中的变量。
  • 全局上下文只能访问全局上下文中的变量和函数,不能直接访问局部上下文中的任何数据。
  • 变量的执行上下文用于确定什么时候释放内存。

JavaScript 是使用垃圾回收的编程语言,开发者不需要操心内存分配和回收。JavaScript 的垃圾回收 程序可以总结如下。

  • 离开作用域的值会被自动标记为可回收,然后在垃圾回收期间被删除。
  • 主流的垃圾回收算法是标记清理,即先给当前不使用的值加上标记,再回来回收它们的内存。
  • 引用计数是另一种垃圾回收策略,需要记录值被引用了多少次。JavaScript 引擎不再使用这种算 法,但某些旧版本的 IE 仍然会受这种算法的影响,原因是 JavaScript 会访问非原生 JavaScript 对象(如 DOM 元素)。
  • 引用计数在代码中存在循环引用时会出现问题。
  • 解除变量的引用不仅可以消除循环引用,而且对垃圾回收也有帮助。为促进内存回收,全局对 象、全局对象的属性和循环引用都应该在不需要时解除引用。

第五章 基本引用类型

5.1 Date

日期对象,为1970.1.1零点至今的毫秒数(时间戳)创建:new Date(); 两个辅助方法

  • Date.parse()接受一个表示日期的字符串参数,将字符串转为时间戳。字符串不表示日期返回NaN。同时new Date(“May 23, 2019”) === new Date(Date.parse(“May 23, 2019”))。使用前者时,Date会自动调用Date.parse

  • Date.UTC()方法也返回时间戳,但是参数是年、0起月数、日、时、分、秒、毫秒。年月必须,不传默认日=1,其他为0;如:new Date(Date.UTC(2005, 4, 5, 17, 55,55))//2005.5.5 17:55:55

    new Date(Date.UTC(2000,0)) //2000.1.1 0:0:0,同Date.parse(), Date.UTC()也会被隐式调用

  • Date.toLocaleString(),返回与浏览器运行的本地环境一致的日期和时间

  • Date.toString(),返回带时区信息的日期和时间。

  • Date.valueOf(),返回的是日期的毫秒表示,因此直接用<、>等操作符就可以返回他们的值

日期的格式化方法:返回值都是字符串,但是输出会因浏览器而异,所以不能保证在用户界面显示一致的时间。

  • toDateString()显示日期中的周几、月、日、年(格式特定于实现)
  • toTimeString()显示日期中的时、分、秒和时区(格式特定于实现)
  • toLocaleDateString()显示日期中的周几、月、日、年(格式特定于实现和地区)
  • toLocaleTimeString()显示日期中的时、分、秒(格式特定于实现和地区)
  • toUTCString()显示完整的 UTC 日期(格式特定于实现)。

Date的其他方法
在这里插入图片描述
在这里插入图片描述

5.2 RegExp正则表达式

正则表达式匹配标记的模式/^ $/x,即x位置所允许的模式。在创建表达式对象的时候可以定义他们的模式:let pattern2 = new RegExp(“[bc]at”, “i”)

  • g:全局模式,查找字符串的全部内容,而不是找到一个匹配就结束
  • i:不区分大小写,表示在查找时忽略pattern和字符串的大小写
  • m:多行模式,查找到一行文本末尾时会继续查找
  • y:粘附模式,表示之查找从lastIndex开始及之后的字符串
  • u:Unicode模式,启用Unicode匹配
  • s:dotAll模式,表示元字符,匹配任何字符(包括\n或\r)

匹配元字符本身,元字符需要转义( [ { \ ^ $ | ) ] } ? * + . 如:?。写成字面量模式就是/\.at/。但是如果写成字符串模式,\也是需要转义的,所以就是"\\.at"

RegExp的实例属性,用于提供有关模式的信息

  • global:布尔值,表示是否设置了 g 标记
  • ignoreCase:布尔值,表示是否设置了 i 标记
  • unicode:布尔值,表示是否设置了 u 标记
  • sticky:布尔值,表示是否设置了 y 标记
  • lastIndex:整数,表示在源字符串中下一次搜索的开始位置,始终从 0 开始
  • multiline:布尔值,表示是否设置了 m 标记
  • dotAll:布尔值,表示是否设置了 s 标记
  • source:正则表达式的字面量字符串(//之间的内容),但没有开头和结尾的 斜杠
  • flags:正则表达式的标记字符串。始终以字面量而非传入构造函数的字符串模式形式返回(没 有前后斜杠)

RegExp实例方法

  • exec():主要用于配合捕获组使用,参数为用于匹配的正则
  • test():接收一个字符串参数,如果输入的文本与模式匹配,返回true。用于只想测试模式是否匹配,不需要实际匹配内容的情况。

RegExp构造函数属性

  • input,简写$_ ,最后搜索的字符串
  • lastMatch,简写$& , 最后匹配的文本
  • lastParen,简写$+ , 最后匹配的捕获组
  • leftContext,简写$` , input字符串张出现在lastMatch前面的文本
  • rightContext,简写$’ , input字符串出现在lastMatch后面的文本

模式局限参考Regular-Expressions.info 网站

5.3 原始值包装类型

Boolean

Boolean是布尔值的引用类型。要创建一个Boolean对象,就使用Boolean构造函数并传入true或false。通过let a = new Boolean(false), 这样创建出来的值是一个对象,所以用布尔值判断 a && true = true。

let a = new Boolean(false), b = false;

typeof a //object typeof b //boolean

a instanceof Boolean //true b instanceof Boolean // false

隐式和显示创建布尔值会有这些区别,所以创建的时候尽量不要使用Boolean构造函数

Number

Number是对应数值的引用类型。创建Number对象就用Number构造函数传入一个数值。Number重写了valueOf()、toLocalString()、toString()方法。

let num = 10

num.valueOf() //10 num.toString() // “10” , toString(x)方法中可以传参,x为进制数,默认10

可以将数值格式化成字符串的方法:

num.toFixed(2) //10.00 保留n位小数,并且将数值格式化为字符串

num.toExponential() //“1.0e+1”,返回以科学计数法表示的数值字符串

num.toPrecision(n) , 会根据情况返回最合理的输出结果,n参数,表示数字的总位数。

如:let num = 99 num.toPrecision(1) // 1e+2 num.toPrecision(2) //99 num.toPrecision(3) //99.0

隐式和显示创建Number会有类似Boolean的问题,所以创建的时候尽量不要使用Number构造函数

String

String是对应字符串的引用类型。创建String对象就用String构造函数传入一个数值。它的valueOf()、toLocalString()、toString()方法都是返回对象的原始字符串值。

  • str.charAt(index), 返回值为给定索引位置的字符。
  • str.charCodeAt(index), 返回值为给定索引位置的字符编码
  • fromCharCode()方法用于根据给定的 UTF-16 码元创建字符串中的字符。这个方法可以接受任意 多个数值,并返回将所有数值对应的字符拼接起来的字符串.(String.fromCharCode(0x61, 0x23, 0x32) // ‘a#2’)

对于 U+0000~U+FFFF 范围内的字符,length、charAt()、charCodeAt()和 fromCharCode() 返回的结果都跟预期是一样的。这个对应关系在扩展到 Unicode 增补字符平面时就不成立了。问题很简单,即 16 位只能唯一表示 65536 个字符。这对于大多数语言字符集是足够了,在 Unicode 中称为基本多语言平面(BMP)。为了 表示更多的字符,Unicode 采用了一个策略,即每个字符使用另外 16 位去选择一个增补平面。这种每个 字符使用两个 16 位码元的策略称为代理对。

  • normalize(‘NFC’)方法,用于比较,多种编码方式下,字符形态相同但字符不同的情况,即把他们统一标准(NFC、NFD、NFKC、NKFD)

    / /U+00C5:上面带圆圈的大写拉丁字母A console.log(String.fromCharCode(0x00C5)); // Å 他们比较时候互不相等,如果改成统一标准则可以进行比较

    // U+212B:长度单位“埃” console.log(String.fromCharCode(0x212B)); // Å

    // U+004:大写拉丁字母 A

    // U+030A:上面加个圆圈 console.log(String.fromCharCode(0x0041, 0x030A)); // Å

字符串操作相关方法

  • concat(), 用于连接字符串,可以传递多个参数,依次拼接let result = ‘hello’.concat(“world”, “!”);
  • slice(beginIndex, endIndex),返回结果为[beginIndex, endIndex)范围内的子字符串,不传end默认为提取到字符串末尾(包括最后一个字符),负参数从长度开始往前数,结束字符为-1
  • substring(beginIndex, endIndex), 跟slice类似。但所有负值参数都转换成0。会自动将begin和index中较小的一个参数作为起点位置,较大的作为终点。
  • substr(beginIndex, count), 从begin开始提取count个字符,返回字符串,不传count默认到结束。begin参数为负当成字符串长度加该值,count参数如果为负,转换成0

字符串位置相关方法

  • indexOf(),从字符串中搜索传入的字符串,返回位置,没有则返回-1。
  • lastIndexOf(),从字符串末尾开始查找,直到找到,返回的是最靠近结束的符合的字符串开始下标

字符串包含方法

  • startsWith(): 检查开始于索引0的匹配项。str.startsWith(‘he’); 类似于检查str是不是以’he’开头。还可以传入第二个参数,表示开始检索的位置str.startswith(‘he’, 2)下标为2开始,是不是he,返回布尔值。
  • endsWith():检索开始于str.length - substr.length的匹配项。即检测字符串是不是以substr结尾。可以接收第二个参数,该参数当作字符串结尾的位置,即将str.length值改为该参数
  • includes():检索字符串是否包含substr,如果不传第二个参数,则不限开始位置。第二个参数可传,表示为开始检索的位置。

字符串大小写转换:

  • toLowerCase():字符串转换成小写
  • toLocaleLowerCase():在基于特定地区的情况下,实现转换成小写
  • toUpperCase():字符串转换成大写
  • toLocaleUpperCase():在基于特定地区的情况下,实现转换成大写

字符串其他方法

  • trim(),字符串去除前后空格,返回字符串副本,原字符串不受影响。trimLeft()和trimRight()分别用于清除字符串开始和结尾的空格

  • repeat(n),n表示将字符串复制多少次。返回副本,不改变原字符串

  • padStart(length), padEnd(length),复制字符串,如果小于指定长度,则在相应一边填充字符,直至满足长度,可以接收第二个参数,第二个参数表示填充所用的字符串

  • iterator(),字符串原型上的方法,表示可迭代字符串的每个字符。

    手动迭代:let strIterator = str[Symbol.iterator]() ,使用strIterator.next()挨个访问

    for…of 和字符串解构也是内部在调用这个迭代器[…str]

  • localCompare(),比较两个字符串,返回如下3个值中的一个

    • 按照字母顺序表,字符串应该排在字符串参数前,返回负值
    • 字符串与字符串参数相等,返回0
    • 按照字母表顺序,字符串应该排在字符串参数后,返回正值

字符串模型匹配方法

  • match(),本质上跟RegExp的exec()方法相同,接收一个参数,可以是一个正则表达式字符串,也可以是一个RegExp对象。match()方法返回的数组与 RegExp 对象的 exec()方法返回的数组是一样的:第一个元素是与整 个模式匹配的字符串,其余元素则是与表达式中的捕获组匹配的字符串(如果有的话)。
  • search(),参数与match一样,返回模式第一个匹配(字符串中第一个字符)的位置索引,如果没找到返回-1。search()始终从字符串开头向后匹配模式。
  • replace(),替换字符串,第一个参数为目标字符串或者RegExp对象,第二个参数可以为字符串或函数。当第一个参数为字符串的时候,只会替换第一个结果,如果为正则并且加了/g全局模式,则替换所有符合的。
  • split(),根据传入的分隔符将字符串拆成数组。第一个参数为分隔符字符串,可以接收第二个参数,为数组最大长度,返回的数组长度将不会超过这个值

5.4 单例内置对象

Global

Global对象是最特别的对象,因为代码不会显式地访问它,它所针对的是不属于任何对象的属性和方法。在全局作用域中定义的变量和函数都会变成Global对象的属性。isNaN(), isFinite(),parseInt(),parseFloat()等,实际上都是Global对象的方法。还有些其他方法。

  • URL编码方法:encodeURI()和encodeURIComponent(),用于编码统一资源标识符,以便传给浏览器。有效的URI不能包含某些字符,使用URI编码方法编码后,可以让浏览器理解它们。同时又以特殊的UTF-编码替换掉所有无效字符。

    encodeURI()不会编码属于 URL 组件的特殊字符,比如冒号、斜杠、问号、 井号,而 encodeURIComponent()会编码它发现的所有非标准字符。

  • URL解码方法:decodeURI()和decodeURIComponent()。分别为上述对应两种编码方式的解码,decodeURI解码只能解encodeURI可以编码的符号,其余保持原状。

  • eval()方法:是一个完整的ECMAScript解释器,接受一个参数,为要执行的es字符串。例:eval("console.log('hello')"), 等价于console.log('hello')。且eval函数也可以引用外部变量。同样的,其内部也可以定义变量。eval("function sayHi() { console.log('hi'); }"); sayHi();通过 eval()定义的任何变量和函数都不会被提升,这是因为在解析代码的时候,它们是被包含在 一个字符串中的。它们只是在 eval()执行的时候才会被创建。严格模式下,内部创建的变量和函数无法被外部访问。

  • 所有原生引用类型构造函数,比如 Object 和 Function,也都是 Global 对象的属性。在这里插入图片描述
    在这里插入图片描述

  • window对象:浏览器将window对象实现为Global对象的代理。因此,所有全局作用域中声明的变量和函数都变成了window的属性。

Math

ES提供了math对象作为保存数学公式、信息和计算的地方。Math对象提供了一些辅助计算的属性和方法。

Math 对象上提供的计算要比直接在 JavaScript 实现的快得多,因为 Math 对象上的 计算使用了 JavaScript 引擎中更高效的实现和处理器指令。但使用 Math 计算的问题是精 度会因浏览器、操作系统、指令集和硬件而异。

  • 数学中的特殊值
    在这里插入图片描述

  • min()和max()方法:返回一组数值中的最小值/最大值。接收多个任意参数。如let max = Math.max(3, 54, 32, 16);,如果原始数据是数组,可以接触…扩展运算符展开计算。

  • 舍入方法

    • Math.ceil(),向上取整(入)
    • Math.floor(),向下取整(舍)
    • Math.round(),四舍五入
    • Math.fround(),返回数值最接近的单精度(32位)浮点值表示。
  • random()方法:返回一个0~1之间的随机数,其中包括0但不包括1。返回其他范围内的写法为:return Math.random() * (end- begin + 1) + begin。则,如果需要返回2~10范围内的随机整数就写成Math.floor(Math.random() * 9 + 2)。

    如果是为了加密而需要 生成随机数(传给生成器的输入需要较高的不确定性),那么建议使用 window.crypto. getRandomValues()。

  • 其他数学方法
    在这里插入图片描述

第六章 集合引用类型

6.1 Object

创建:

  • 显式创建:使用new和Object构造函数。let person = new Object();

  • 显式创建:使用对象字面量表示法。let person = {name : 'xiaoming', age: 18}

    在使用对象字面量表示法定义对象时,并不会实际调用Object构造函数

属性读取:一般用.语法获取,如person.name。但是也可以用中括号语法来获取,中括号中为字符串,如person[‘name’],中括号语法被大量用于使用变量读属性的情况。

6.2 Array

ES中的数组与其他语言不同的是,数组的每个位置都可以储存任意类型的数据,所以可能会有第一个元素是字符串,第二个是数值,第三个是对象这种情况发生,数组是动态大小的,会随着数据添加自动增长。

6.2.1 创建数组:
  • 使用Array构造函数。let colors = new Array(),

    可以通过给Array传入一个数值创建一个初始长度为num的数组,当然也可以不输入num。

    也可以通过给Array传入其他类型指定初始元素,如let colors = new Array(‘red’, ‘blue’)

  • 使用数组字面量表示法。let colors = [‘red’, ‘blue’]

    和对象一样,使用数组字面量表示法创建数组不会调用Array构造函数

  • 使用Array构造函数的静态方法创建数组。

    • Array.from(): 将类数组解构转为数组实例。

      • Array.from(‘Matt’) // [‘M’, ‘a’, ‘t’, ‘t’]

      • const m = new Map().set(1, 2) .set(3, 4); console.log(Array.from(m)); // [[1, 2], [3, 4]] ,将集合和映射转换成一个新数组

      • const a1 = [1, 2, 3, 4]; const a2 = Array.from(a1); //[1, 2, 3, 4]对现有数组执行浅复制

      • function getArgsArray() { return Array.from(arguments); } console.log(getArgsArray(1, 2, 3, 4)); // [1, 2, 3, 4] arguments对象转成数组

      • const arrayLikeObject = { 0: 1, 1: 2, 2: 3, 3: 4, length: 4 }; console.log(Array.from(arrayLikeObject)); // [1, 2, 3, 4] 也能转换带有必要属性的自定义对象

      Array.from()还接收第二个可选的映射函数参数。这个函数可以直接增强新数组的值,而无须像调用 Array.from().map()那样先创建一个中间数组。还可以接收第三个可选参数,用于指定映射函 数中 this 的值。但这个重写的 this 值在箭头函数中不适用。

      const a1 = [1, 2, 3, 4]; const a2 = Array.from(a1, x => x**2); const a3 = Array.from(a1, function(x) {return x**this.exponent}, {exponent: 2}); console.log(a2); // [1, 4, 9, 16] console.log(a3); // [1, 4, 9, 16]

    • Array.of(): 将一组参数转为数组实例。console.log(Array.of(1, 2, 3, 4)); // [1, 2, 3, 4]

6.2.2 数组空位

使用数组字面量初始化数组时,可以使用一串逗号来创建空位。const options = [, , , ,]//4个元素 ES6中新增方法会将这么空位当成存在的元素,值为undefined。ES6之前的方法会忽略空位,如map()会跳过空位置,join()会视空位为空字符串。 由于行为不一致和存在性能隐患,因此实践中要避免使用数组空位。如果确实需要 空位,则可以显式地用 undefined 值代替。

6.2.3 数组索引

要取得或设置数组的值,需要使用中括号并提供相应值的数字索引。如,colors[2] = ‘green’, 修改或添加下标为2的数组元素。

数组length的独特之处:不是只读的,修改length属性,可以从数组末尾删除或添加元素,设置数组的length属性小于当前长度,那么多出来的元素会被删掉,再读取就是undefined。

6.2.4 检测数组——判断一个对象是不是数组

在只有一个网页(因而只有一个全局作用域的情况下),使用 instanceof 操作符就足矣: if (value instanceof Array){ // 操作数组 } 使用 instanceof 的问题是假定只有一个全局执行上下文。

如果网页里有多个框架,则可能涉及两个不同的全局执行上下文,因此就会有两个不同版本的 Array 构造函数。如果要把数组从一个框架传给另一个框架,则这个数组的构造函数将有别于在第二个框架内本地创建的数组。

为解决这个问题,ECMAScript 提供了 Array.isArray()方法。这个方法的目的就是确定一个值是否为数组,而不用管它是在哪个全局执行上下文中创建的。来看下面的例子: if (Array.isArray(value)){ // 操作数组 }

6.2.5 迭代器方法

const a = [‘aa’, ‘bb’, ‘cc’]

  • keys():数组索引的迭代(遍历)[0, 1, 2]
  • values():数组元素的迭代 [‘aa’, ‘bb’, ‘cc’]
  • entries():返回索引/值的迭代器[[0, 'aa'],[1, 'bb'], [2, 'cc']]
6.2.6 复制和填充方法,不会改变数组大小

const zeros = [0, 0, 0 ,0, 0],ints = [1, 2, 3, 4, 5, 6]

  • fill():填充数组。用法(会自动忽略超出边界、零长度和反方向的索引,只填充可用部分)
    • zeros.fill(5) //用5填充 [5,5,5,5,5]
    • zeros.fill(6, 3) //用6填充索引>=3的元素 [0, 0, 0, 6, 6]
    • zeros.fill(7, 1, 3) //用7填充索引[1,3)包含首不包含尾 [0, 7, 7, 0, 0]
    • zeros.fill(8, -4, -1) //负数自动转成 -x + zeros.length [0, 8, 8, 8, 0]
  • copyWithin():批量复制,按照指定范围千夫指数组中的部分内容,然后将它们插入到指定索引开始的位置(会自动忽略超出边界、零长度和反方向的索引,只填充可用部分)。用法:
    • ints.copyWithin(3) //从数组0的位置开始复制,插入到3开始的位置 [1, 2, 3, 1, 2, 3]
    • ints.copyWithin(0, 3) //从数组复制3开始的内容,插入到0开始的位置 [4, 5, 6 , 4, 5, 6]
    • ints.copyWithin(2, 0, 3) //复制数组[0, 3)的内容,插入到2开始的位置[1, 2, 1, 2, 5, 6]
    • ints.copyWithin(-4, -3, -1) //自动转成-x + ints.length ,[1, 2, 4, 5, 5, 6]
6.2.7 转换方法
  • valueOf():返回数组本身。

  • toString():返回由数组中每个值的等效字符串拼接而成的一个逗号分隔符字符串。

  • toLocalString():回数组值的逗号分隔的字符串。可能与toString()结果相同,但也不一定
    在这里插入图片描述

  • join():array.join(‘’) 参数为字符串分隔符,得到的结果为以分隔符隔开的字符串。

6.2.8 栈方法
  • push():接收任意量的参数,并将它们添加到数组末尾。返回值为数组的最新长度。
  • pop():推出数组的最后一项,返回值为被推出的项。
6.2.9 队列方法
  • shift():删除数组第一项并返回,返回值为被删除的项。
  • unshift():接收任意量参数,将他们添加到数组开头。返回值为数组的最新长度。
6.2.10 排序方法
  • reverse():反转数组。只按照当前索引,将索引对应项从大到小排
  • sort():不传参数是就是将数组里每一项按照字符串形式从前往后排列。传入比较函数的话,一般情况下,如果返回值大于等于0,则不交换位置。当然这取决于你的比较函数写法
6.2.11 操作方法
  • concat():参数可以为一个或多个数组/参数,如a.concat(),如果参数有数组,则将数组的每一项都拼接到a数组后面,若参数不是数组,则直接添加到数组末尾。

  • slice():创建一个包含原有数组中一个或多个元素的新数组。可以接收1或2个参数:返回元素的开始索引和结束索引。如果有两个参数,返回[开始索引,结束索引);一个参数则返回从开始索引一直到结尾位置的有所元素。

    如果 slice()的参数有负值,那么就以数值长度加上这个负值的结果确定位置。比 如,在包含 5 个元素的数组上调用 slice(-2,-1),就相当于调用 slice(3,4)。如果结 束位置小于开始位置,则返回空数组。

  • splice():在数组中插入元素,但是根据参数不同,有不同的使用方法。

    • 删除:两个参数splice(begin, num),传入开始索引和要删除的数量。
    • 插入:三个参数splice(begin, 0 , NEW),开始位置,删除数量(插入为0),插入内容
    • 替换:三个参数splice(begin, num , NEW),开始位置,删除数量,该位置新插入的内容。这里删除数量和插入内容的个数不需要相等,就是从当前位置开始而已,比如把2个替换成5个也是可以的。
6.2.12 搜索和位置方法

ES提供两类搜索数组的方法:严格相等搜索和断言相等搜索。

严格相等:接收两个参数,要查找的元素和起始搜索位置(可选)

  • indexOf():查找等于参数的元素,返回第一个符合的索引。没找到则返回-1
  • lastIndexOf():从数组末尾向前查找等于参数的元素,返回第一个符合的索引。没找到返回-1
  • includes():返回布尔值,表示是否至少找到一个与指定元素匹配的项。

断言函数:考研找定义的断言函数搜索数组,每个索引都会调用这个函数。断言函数的返回值决定了相应索引的元素是否被认为匹配。接收三个参数:元素、索引和数组本身。如people.find((element, index, array) => element.age < 28)

  • find():从数组的最小索引开始,返回第一个匹配的元素。找到匹配项后不继续搜索。
  • findIndex():从数组最小索引开始,返回第一个匹配元素的索引。找到匹配项后不继续搜索。
6.2.13 迭代方法

ES为数组定义了5个迭代方法。每个方法接收两个参数:以每一项为参数的运行函数,作为函数运行上下文的作用域对象(可选)。传给每个方法的函数接收3个参数:数组元素,元素索引,数组本身;同上。

  • every():对数组每一项都运行传入的函数,如果对每项对返回ture,方法返回true
  • filter():对数组每项都运行传入的函数,函数返回true的项会组成数组之后返回。
  • forEarch():对数组每项都运行传入的函数,没有返回值。
  • map():对数组每项都运行传入的函数,返回由每次函数调用结果构成的数组
  • some():对数组每一项都运行传入的函数,如果有一项返回true,则方法返回true
6.2.14 归并方法

迭代数组的所有项,并在此基础上构建一个最终返回值。传给每个方法的函数接收四个参数:上一个归并值、当前项、当前项的索引和数组本身。这个函数返回的任何值都会作为下一次调用同一个函数的第一个参数。如果没有给这两个方法 传入可选的第二个参数(作为归并起点值),则第一次迭代将从数组的第二项开始,因此传给归并函数 的第一个参数是数组的第一项,第二个参数是数组的第二项。

  • reduce():从数组第一项开始遍历到最后一项
  • reduceRight():从最后一项开始遍历至第一项

6.3 定型数组

6.3.2 ArrayBuffer

ArrayBuffer()是一个普通的 JavaScript 构造函数,可用于在内存中分配特定数量的字节空间。分配的内存不能超过 Number.MAX_SAFE_INTEGER(2^53 - 1)字节。如:const buf = new ArrayBuffer(16); // 在内存中分配 16 字节一经创建就不能再调整大小。不过,可以使用 slice()复制其全部或部分到一个新实例中。

6.3.3 DataView

第一种允许你读写 ArrayBuffer 的视图是 DataView。这个视图专为文件 I/O 和网络 I/O 设计,其 API 支持对缓冲数据的高度控制,但相比于其他类型的视图性能也差一些。DataView 对缓冲内容没有任何预设,也不能迭代。必须在对已有的 ArrayBuffer 读取或写入时才能创建 DataView 实例。这个实例可以使用全部或部分ArrayBuffer,且维护着对该缓冲实例的引用,以及视图在缓冲中开始的位置。

const buf = new ArrayBuffer(16); 
// DataView 默认使用整个 ArrayBuffer 
const fullDataView = new DataView(buf); 
alert(fullDataView.byteOffset); // 0 
alert(fullDataView.byteLength); // 16 
alert(fullDataView.buffer === buf); // true 

要通过 DataView 读取缓冲,还需要几个组件。 DataView 完成读、写操作的前提是必须有充足的缓冲区,否则就会抛出 RangeError。DataView 在写入缓冲里会尽最大努力把一个值转换为适当的类型,后备为 0。如果无法转换,则抛出错误。

  • 首先是要读或写的字节偏移量。可以看成 DataView 中的某种“地址”。

  • DataView 应该使用 ElementType 来实现 JavaScript 的 Number 类型到缓冲内二进制格式的转 换。

    DataView对存储在缓冲内的数据类型没有预设。它暴露的API强者开发者在读、写时指定一个ElementType,然后DataView就会忠实地为读、写而完成相应的转换。
    在这里插入图片描述

    DataView 为上表中的每种类型都暴露了 get 和 set 方法,这些方法使用 byteOffset(字节偏移 量)定位要读取或写入值的位置。类型是可以互换使用的,如:

    // 在内存中分配两个字节并声明一个 DataView 
    const buf = new ArrayBuffer(2); 
    const view = new DataView(buf); 
    // 说明整个缓冲确实所有二进制位都是 0 
    // 检查第一个和第二个字符
    alert(view.getInt8(0)); // 0 
    alert(view.getInt8(1)); // 0 
    // 检查整个缓冲
    alert(view.getInt16(0)); // 0 
    // 将整个缓冲都设置为 1 
    // 255 的二进制表示是 11111111(2^8 - 1)
    view.setUint8(0, 255); 
    // DataView 会自动将数据转换为特定的 ElementType 
    // 255 的十六进制表示是 0xFF 
    view.setUint8(1, 0xFF); 
    // 现在,缓冲里都是 1 了
    // 如果把它当成二补数的有符号整数,则应该
    alert(view.getInt16(0)); // -1
    
  • 最后是内存中值的字节序。默认为大端字节序。

    “字节序”指的是计算系统维护的一种字节顺序的约定。DataView 只支持两种约定:大端字节序和小端字节序。大端字节序也称为“网络字节序”,意思是最高有效位保存在第一个字节,而最低有效位保存在最后一个字节。小端字节序正好相反,即最低有效位保存在第一个字节,最高有效位保存在最后一个字节。

    JavaScript 运行时所在系统的原生字节序决定了如何读取或写入字节,但 DataView 并不遵守这 个约定。对一段内存而言,DataView 是一个中立接口,它会遵循你指定的字节序。DataView 的所 有 API 方法都以大端字节序作为默认值,但接收一个可选的布尔值参数,设置为 true 即可启用小端 字节序。

    // 在内存中分配两个字节并声明一个 DataView 
    const buf = new ArrayBuffer(2); 
    const view = new DataView(buf); 
    // 填充缓冲,让第一位和最后一位都是 1 
    view.setUint8(0, 0x80); // 设置最左边的位等于 1 
    view.setUint8(1, 0x01); // 设置最右边的位等于 1 
    // 缓冲内容(为方便阅读,人为加了空格)
    // 0x8 0x0 0x0 0x1 
    // 1000 0000 0000 0001 
    // 按大端字节序读取 Uint16 
    // 0x80 是高字节,0x01 是低字节
    // 0x8001 = 2^15 + 2^0 = 32768 + 1 = 32769 
    alert(view.getUint16(0)); // 32769 
    // 按小端字节序读取 Uint16 
    // 0x01 是高字节,0x80 是低字节
    // 0x0180 = 2^8 + 2^7 = 256 + 128 = 384 
    alert(view.getUint16(0, true)); // 384 
    // 按大端字节序写入 Uint16 
    view.setUint16(0, 0x0004); 
    // 缓冲内容(为方便阅读,人为加了空格)
    // 0x0 0x0 0x0 0x4 
    // 0000 0000 0000 0100 
    alert(view.getUint8(0)); // 0 
    alert(view.getUint8(1)); // 4 
    // 按小端字节序写入 Uint16 
    view.setUint16(0, 0x0002, true); 
    // 缓冲内容(为方便阅读,人为加了空格)
    // 0x0 0x2 0x0 0x0 
    // 0000 0010 0000 0000 
    alert(view.getUint8(0)); // 2 
    alert(view.getUint8(1)); // 0 
    
6.3.4 定型数组

定型数组是另一种形式的ArrayBuffer视图。虽然概念上与DataView接近,但定型数组的区别在于,它更特定于一种ElementType且遵循系统原生的字节序。相应地,定型数组提供了适用面更广的API和更高的性能。设计定性数组的目的就是提高与WebGl等原生库交换二进制数据的效率。由于定型数组的二进制表示对操作系统而言是一种容易使用的格式,JS引擎可以重度优化算术运算、换位运算和其他对定型数组的常见操作,因此使用它们速度极快。

创建定型数组的方式包括读取已有的缓冲、使用自有缓冲、填充可迭代结构,以及填充基于任意类型的定型数组。另外,通过<ElementType>.from()<ElementType>.of()也可以创建定型数组

  • 定型数组行为:从很多方面看,定型数组与普通数组都很相似。定型数组支持如下操作符、方法和属性:绝大部分方法,除了会调整数组缓冲大小的。而且定型数组有一个符号属性Symbol.iterator,因此可以通过for…of循环和扩展操作符来操作。

  • 合并、复制和修改定型数组:定型数组同样使用数组缓冲来存储数据,而数组缓冲无法调整大小。因此,下列方法不适用于定型数组:concat()、pop()、push()、shift()、unshift()、splice()。定制数组提供了两个新方法,可以快速向外或向内复制数据:set()、subarray();

    • set():从提供的数组或定型数组中把值复制到当前定性数组中指定的索引位置:

      // 创建长度为 8 的 int16 数组
      const container = new Int16Array(8); 
      // 把定型数组复制为前 4 个值
      // 偏移量默认为索引 0 
      container.set(Int8Array.of(1, 2, 3, 4)); 
      console.log(container); // [1,2,3,4,0,0,0,0] 
      // 把普通数组复制为后 4 个值
      // 偏移量 4 表示从索引 4 开始插入
      container.set([5,6,7,8], 4); 
      console.log(container); // [1,2,3,4,5,6,7,8] 
      // 溢出会抛出错误
      container.set([5,6,7,8], 7); 
      // RangeError 
      
    • subarray():基于从原始定型数组中复制的值返回一个新定型数组。复制值时开始的索引和结束索引是可选的。

      const source = Int16Array.of(2, 4, 6, 8); 
      // 把整个数组复制为一个同类型的新数组
      const fullCopy = source.subarray(); 
      console.log(fullCopy); // [2, 4, 6, 8] 
      // 从索引 2 开始复制数组
      const halfCopy = source.subarray(2); 
      console.log(halfCopy); // [6, 8] 
      // 从索引 1 开始复制到索引 3 
      const partialCopy = source.subarray(1, 3); 
      console.log(partialCopy); // [4, 6]
      

    定型数组没有原生的拼接能力,但使用定型数组API提供的很多工具可以手动构建。

  • 上溢和下溢

    定型数组中值的上溢和下溢不会影响到其他索引,但仍然需要考虑数组的元素应该是什么类型。定型数组对于可以存储的每个索引只接收一个相关位,而不考虑他们对实际数值的影响。以下代码演示了如何处理上溢和下溢

    // 长度为 2 的有符号整数数组
    // 每个索引保存一个二补数形式的有符号整数
    // 范围是-128(-1 * 2^7)~127(2^7 - 1)
    const ints = new Int8Array(2); 
    // 长度为 2 的无符号整数数组
    // 每个索引保存一个无符号整数
    // 范围是 0~255(2^7 - 1)
    const unsignedInts = new Uint8Array(2); 
    // 上溢的位不会影响相邻索引
    // 索引只取最低有效位上的 8 位
    unsignedInts[1] = 256; // 0x100 
    console.log(unsignedInts); // [0, 0] 
    unsignedInts[1] = 511; // 0x1FF 
    console.log(unsignedInts); // [0, 255] 
    // 下溢的位会被转换为其无符号的等价值
    // 0xFF 是以二补数形式表示的-1(截取到 8 位), 
    // 但 255 是一个无符号整数
    unsignedInts[1] = -1 // 0xFF (truncated to 8 bits) 
    console.log(unsignedInts); // [0, 255] 
    // 上溢自动变成二补数形式
    // 0x80 是无符号整数的 128,是二补数形式的-128 
    ints[1] = 128; // 0x80 
    console.log(ints); // [0, -128] 
    // 下溢自动变成二补数形式
    // 0xFF 是无符号整数的 255,是二补数形式的-1 
    ints[1] = 255; // 0xFF 
    console.log(ints); // [0, -1]
    

    除了 8 种元素类型,还有一种“夹板”数组类型:Uint8ClampedArray,不允许任何方向溢出。 超出最大值 255 的值会被向下舍入为 255,而小于最小值 0 的值会被向上舍入为 0。 const clampedInts = new Uint8ClampedArray([-1, 0, 255, 256]); console.log(clampedInts); // [0, 0, 255, 255] 按照 JavaScript 之父 Brendan Eich 的说法:“Uint8ClampedArray 完全是 HTML5 canvas元素的历史留存。除非真的做跟 canvas 相关的开发,否则不要使用它。”

6.4 Map

作为 ECMAScript 6 的新增特性,Map 是一种新的集合类型,为这门语言带来了真正的键/值存储机 制。Map 的大多数特性都可以通过 Object 类型实现,但二者之间还是存在一些细微的差异。

6.4.1 基本API

使用new关键字和Map构造函数可以创建一个空映射:const m = new Map();如果想在创建的同时初始化实例,可以给 Map 构造函数传入一个可迭代对象,需要包含键/值对数 组。可迭代对象中的每个键/值对都会按照迭代顺序插入到新映射实例中

  • 使用嵌套数组初始化映射:const m1 = new Map([ ["key1", "val1"], ["key2", "val2"]]); alert(m1.size); // 3
  • 使用自定义迭代器初始化映射:const m2 = new Map({ [Symbol.iterator]: function*() {yield ["key1", "val1"]; yield ["key2", "val2"]} }); alert(m2.size); // 3
  • 映射期待的键/值对,无论是否提供:const m3 = new Map([[]]); alert(m3.has(undefined)); // true alert(m3.get(undefined)); // undefined

初始化之后,可以使用 set()方法再添加键/值对。另外,可以使用 get()和 has()进行查询,可 以通过 size 属性获取映射中的键/值对的数量,还可以使用 delete()和 clear()删除值。

const m = new Map(); 
m.set("firstName", "Matt") 
 .set("lastName", "Frisbie"); //set()方法返回映射实例,因此可以把多个操作连缀起来,包括初始化声明
alert(m.has("firstName")); // true 
alert(m.get("firstName")); // Matt 
alert(m.size); // 2 

m.delete("firstName"); // 只删除这一个键/值对
alert(m.has("firstName")); // false 
alert(m.has("lastName")); // true 
alert(m.size); // 1 

m.clear(); // 清除这个映射实例中的所有键/值对
alert(m.has("firstName")); // false 
alert(m.has("lastName")); // false 
alert(m.size); // 0

与 Object 只能使用数值、字符串或符号作为键不同,Map 可以使用任何 JavaScript 数据类型作为 键。Map 内部使用 SameValueZero 比较操作(ECMAScript 规范内部定义,语言中不能使用),SameValueZero 比较意味着独立实例不冲突。基本上相当于使用严格对象相等的标准来检查键的匹配性。与 Object 类似,映射的值是没有限制的。

const m = new Map(); 
const functionKey = function() {}; 
const symbolKey = Symbol(); 
const objectKey = new Object(); 
m.set(functionKey, "functionValue"); 
m.set(symbolKey, "symbolValue"); 
m.set(objectKey, "objectValue"); 
alert(m.get(functionKey)); // functionValue 
alert(m.get(symbolKey)); // symbolValue 
alert(m.get(objectKey)); // objectValue 
// SameValueZero 比较意味着独立实例不冲突
alert(m.get(function() {})); // undefined 

与严格相等一样,在映射中用作键和值的对象及其他“集合”类型,在自己的内容或属性被修改时 仍然保持不变。

6.4.2 顺序与迭代

Object类型的一个主要差异是:Map实例会维护键值对的插入顺序,因此可以根据插入顺序执行迭代操作。映射实例可以提供一个迭代器(Iterator),能以插入顺序生成[key, value]形式的数组。可以通过 entries()方法(或者 Symbol.iterator 属性,它引用 entries())取得这个迭代器。

const m = new Map([ [“key1”, “val1”], [“key2”, “val2”], [“key3”, “val3”] ]);

  • for (let pair of m.entries()) //输出结果为[key, value]的键值对的遍历

  • for (let pair of m[Symbol.iterator]())//输出[key, value]的键值对的遍历

  • Map实例也可以直接使用…扩展运算符展开,[...m]得到[key,value], [key,value]键值对的数组

  • 也可以使用forEach(callback, opt_thisArg)方法遍历。第二个参数可选,可以重新回调内部this的指向。m.forEach((val, key) => {})

  • 也可以用keys(), values()方法。for (let key of m.keys())//输出[key1, key2]键名的集合

    for (let key of m.values()) //输出[value1, value2]值的集合

键和值在迭代器遍历时是可以修改的,但映射内部的引用则无法修改。当然,这并不妨碍修改作为 键或值的对象内部的属性,因为这样并不影响它们在映射实例中的身份。作为键的字符串原始值是不能修改的,修改了作为键的对象的属性,但对象在映射内部仍然引用相同的值。

6.4.3 选择Object还是Map
  • 内存占用:固定大小内存情况下,Map存储数量更多

    Object 和 Map 的工程级实现在不同浏览器间存在明显差异,但存储单个键/值对所占用的内存数量 都会随键的数量线性增加。批量添加或删除键/值对则取决于各浏览器对该类型内存分配的工程实现。 不同浏览器的情况不同,但给定固定大小的内存,Map 大约可以比 Object 多存储 50%的键/值对。

  • 插入性能:大致相当。大量插入操作时,Map性能更佳

    向 Object 和 Map 中插入新键/值对的消耗大致相当,不过插入 Map 在所有浏览器中一般会稍微快 一点儿。对这两个类型来说,插入速度并不会随着键/值对数量而线性增加。如果代码涉及大量插入操 作,那么显然 Map 的性能更佳

  • 查找速度:Object有时速度更快。大量查找操作时,Object更好

    与插入不同,从大型 Object 和 Map 中查找键/值对的性能差异极小,但如果只包含少量键/值对, 则 Object 有时候速度更快。在把 Object 当成数组使用的情况下(比如使用连续整数作为属性),浏 览器引擎可以进行优化,在内存中使用更高效的布局。这对 Map 来说是不可能的。对这两个类型而言, 查找速度不会随着键/值对数量增加而线性增加。如果代码涉及大量查找操作,那么某些情况下可能选 择 Object 更好一些。

  • 删除性能:Object一般都用伪删除,Map 的 delete()操作都比插入和查找更快。大量删除操作选Map

    使用 delete 删除 Object 属性的性能一直以来饱受诟病,目前在很多浏览器中仍然如此。为此, 出现了一些伪删除对象属性的操作,包括把属性值设置为 undefined 或 null。但很多时候,这都是一 种讨厌的或不适宜的折中。而对大多数浏览器引擎来说,Map 的 delete()操作都比插入和查找更快。 如果代码涉及大量删除操作,那么毫无疑问应该选择 Map。

6.5 WeakMap

ECMAScript 6 新增的“弱映射”(WeakMap)是一种新的集合类型,为这门语言带来了增强的键/ 值对存储机制。WeakMap 是 Map 的“兄弟”类型,其 API 也是 Map 的子集。WeakMap 中的“weak”(弱), 描述的是 JavaScript 垃圾回收程序对待“弱映射”中键的方式。

6.5.1 基本API

可以使用new关键字实例化一个空的WeakMap。const wm = new WeakMap();

弱映射中的键只能是Object或者继承自Objcet的类型,尝试使用非对象设置键会抛出TypeError。值的类型没有限制。

如果想在初始化时填充弱映射,则构造函数可以接收一个可迭代对象,之情需要包含键值对数组。可迭代对象中的每个键值都会按照迭代顺序插入新实例中。初始化之后可以使用 set()再添加键/值对,可以使用 get()和 has()查询,还可以使用 delete() 删除。

初始化是一个全有或者全无的操作,自己要有一个键无效就会抛出错误,导致整个初始化失败

6.5.2 弱键

WeakMap 中“weak”表示弱映射的键是“弱弱地拿着”的。意思就是,这些键不属于正式的引用, 不会阻止垃圾回收。但要注意的是,弱映射中值的引用不同。只要键存在,键/值 对就会存在于映射中,并被当作对值的引用,因此就不会被当作垃圾回收。

如: const wm = new WeakMap(); wm.set({}, "val"); set()方法初始化了一个新对象并将它用作一个字符串的键。因为没有指向这个对象的其他引用, 所以当这行代码执行完成后,这个对象键就会被当作垃圾回收。然后,这个键/值对就从弱映射中消失了,使其成为一个空映射。在这个例子中,因为值也没有被引用,所以这对键/值被破坏以后,值本身也会成为垃圾回收的目标。

const wm = new WeakMap(); 
const container = { 
 key: {} 
}; 
wm.set(container.key, "val"); 
function removeReference() { 
 container.key = null; 
}

这一次,container 对象维护着一个对弱映射键的引用,因此这个对象键不会成为垃圾回收的目 标。不过,如果调用了 removeReference(),就会摧毁键对象的最后一个引用,垃圾回收程序就可以 把这个键/值对清理掉。

6.5.3 不可迭代键

因为 WeakMap 中的键/值对任何时候都可能被销毁,所以没必要提供迭代其键/值对的能力。当然, 也用不着像 clear()这样一次性销毁所有键/值的方法。WeakMap 确实没有这个方法。因为不可能迭代, 所以也不可能在不知道对象引用的情况下从弱映射中取得值。即便代码可以访问WeakMap实例,也没办法看到其中的内容。

WeakMap 实例之所以限制只能用对象作为键,是为了保证只有通过键对象的引用才能取得值。如果允许原始值,那就没办法区分初始化时使用的字符串字面量和初始化之后使用的一个相等的字符串了。

6.5.4 使用弱映射
  • 私有变量:私有变量会存储在弱映射中,以对象实例为键,以私有成员的字典为值。对于没有 闭包操作的实现,外部代码只需要拿到对象实例的引用和弱映射,就可以 取得“私有”变量了。为了避免这种访问,可以用一个闭包把 WeakMap 包装起来,这样就可以把弱映射与外界完全隔离开了。拿不到弱映射中的健,也就无法取得弱映射中对应的值。虽然这防止了前面提到的访问,但整个代码也完全陷入了 ES6 之前的闭包私有变量模式。

    const User = (() => {  //这里为闭包操作
            const wm = new WeakMap(); 
            class User { 
                constructor(id) { 
                this.idProperty = Symbol('id'); 
                this.setId(id); 
            } 
            setPrivate(property, value) { 
                const privateMembers = wm.get(this) || {}; 
                privateMembers[property] = value; 
                wm.set(this, privateMembers); 
            } 
            getPrivate(property) { 
                return wm.get(this)[property]; 
            } 
            setId(id) { 
                this.setPrivate(this.idProperty, id); 
            } 
            getId(id) { 
                return this.getPrivate(this.idProperty); 
            } 
         } //这里为闭包操作
     	return User;//这里为闭包操作
     })();//这里为闭包操作
    
  • DOM节点元数据

    因为 WeakMap 实例不会妨碍垃圾回收,所以非常适合保存关联元数据。来看下面这个例子,其中 使用了常规的 Map:

    const m = new Map(); const loginButton = document.querySelector('#login'); // 给这个节点关联一些元数据 m.set(loginButton, {disabled: true});

    假设在上面的代码执行后,页面被 JavaScript 改变了,原来的登录按钮从 DOM 树中被删掉了。但 由于映射中还保存着按钮的引用,所以对应的 DOM 节点仍然会逗留在内存中,除非明确将其从映射中 删除或者等到映射本身被销毁。 如果这里使用的是弱映射,如以下代码所示,那么当节点从 DOM 树中被删除后,垃圾回收程序就 可以立即释放其内存(假设没有其他地方引用这个对象)

    const wm = new WeakMap(); const loginButton = document.querySelector('#login'); // 给这个节点关联一些元数据 wm.set(loginButton, {disabled: true});

6.6 Set

Set 是一种新集合类型,为这门语言带来集合数据结构。Set 在很多方面都 像是加强的 Map,这是因为它们的大多数 API 和行为都是共有的。

6.6.1 基本API

使用new关键字和Set构造函数可以创建一个空集合:const m = new Set();如果想在创建的同时初始化实例,则可以给 Set 构造函数传入一个可迭代对象,其中需要包含插入 到新集合实例中的元素。

  • 使用数组初始化集合:const s1 = new Set(["val1", "val2", "val3"]);
  • 使用自定义迭代器初始化集合:const s2 = new Set({ [Symbol.iterator]: function*() { yield "val1"; yield "val2"; yield "val3"; } });

初始化之后,可以使用 add()增加值,使用 has()查询,通过 size 取得元素数量,以及使用 delete() 和 clear()删除元素。

与 Map 类似,Set 可以包含任何 JavaScript 数据类型作为值。集合也使用 SameValueZero 操作 (ECMAScript 内部定义,无法在语言中使用),基本上相当于使用严格对象相等的标准来检查值的匹配性。

与严格相等一样,用作值的对象和其他“集合”类型在自己的内容或属性被修改时也不会改变。

add()和 delete()操作是幂等的。delete()返回一个布尔值,表示集合中是否存在要删除的值:

6.6.2 顺序与迭代

Set 会维护值插入时的顺序,因此支持按顺序迭代。

集合实例可以提供一个迭代器,能以插入顺序生成集合内容。可以通过values()方法及其别名方法keys()(或者Symbol.iterator属性,它引用values())取得这个迭代器。

  • values()是默认迭代器,所以可以直接对集合实例使用扩展操作,把集合转换为数组。[…s]

  • entires()方法返回一个迭代器,可以按照插入顺序产生包含两个元素的数组,这两个元素是集合中每个值的重复出现。for (let pair of s.entries()) { console.log(pair); } // ["val1", "val1"] // ["val2", "val2"]

  • forEach()回调方式,依次迭代每个键值对。传入的回调接收可选的第二个参数,这个参数用于重写回调内部this的值。s.forEach((val, dupVal) => alert(${val} -> ${dupVal})); // val1 -> val1

6.6.3 定义正式集合操作

从各方面来看,Set 跟 Map 都很相似,只是 API 稍有调整。唯一需要强调的就是集合的API对自身的简单操作。很多开发者都喜欢使用 Set 操作,但需要手动实现:或者是子类化 Set,或者是定义一个实用函数库。要把两种方式合二为一,可以在子类上实现静态方法,然后在实例方法中使用这些静态方法。在实现这些操作时,需要考虑几个地方。

  • 某些Set操作是有关联性的,因此最好让实现的方法能支持处理任意多个集合的实例。
  • Set保留插入顺序,所有方法返回的集合必须保证顺序。
  • 尽可能高效的使用内存。扩展操作符的语法很简洁,但尽可能避免集合和数组间的相互转换能够节省对象初始化成本。
  • 不要修改已有的集合实例。union(a,b)或者a.union(b)应该返回包含结果的新集合实例。

6.7 WeakSet

ES6 新增的“弱集合”(WeakSet)是一种新的集合类型,为这门语言带来了集合数据结 构。WeakSet是 Set 的“兄弟”类型,其 API 也是 Set 的子集。WeakSet 中的“weak”(弱),描述的是 JavaScript 垃圾回收程序对待“弱集合”中值的方式。

6.7.1 基本API
  • 初始化:new关键字 const ws = new WeakSet();。弱集合中的值只能是 Object 或者继承自 Object 的类型,尝试使用非对象设置值会抛出 TypeError。

  • 初始化实例并填充:const val1 = {id: 1}, val2 = {id: 2}, val3 = {id: 3}; const ws1 = new WeakSet([val1, val2, val3]);

初始化是全有或者全无的操作,只要有一个值无效就会抛出错误导致整个初始化失败

  • 添加新值:add(),且可以连续调用。如ws.add(val2).add(val1).add(val3)

  • 查询:has(),返回布尔值。ws.has(val1) //true

6.7.2 弱值

WeakSet表示的弱集合的值,这些值不属于正式的引用,不会阻止垃圾回收。如const ws = new WeakSet(); ws.add({}); add()方法初始化了一个新对象,并将它用作一个值。因为没有指向这个对象的其他引用,所以当 这行代码执行完成后,这个对象值就会被当作垃圾回收。然后,这个值就从弱集合中消失了,使其成为 一个空集合。

6.7.3 不可迭代值

因为 WeakSet 中的值任何时候都可能被销毁,所以没必要提供迭代其值的能力。当然,也用不着 像 clear()这样一次性销毁所有值的方法。WeakSet 确实没有这个方法。因为不可能迭代,所以也不 可能在不知道对象引用的情况下从弱集合中取得值。即便代码可以访问 WeakSet 实例,也没办法看到 其中的内容。

WeakSet 之所以限制只能用对象作为值,是为了保证只有通过值对象的引用才能取得值。如果允许 原始值,那就没办法区分初始化时使用的字符串字面量和初始化之后使用的一个相等的字符串了。

6.7.4 使用弱集合

相比于 WeakMap 实例,WeakSet 实例的用处没有那么大。不过,弱集合在给对象打标签时还是有 价值的。例,这里使用了一个普通 Set: const disabledElements = new Set(); const loginButton = document.querySelector('#login'); // 通过加入对应集合,给这个节点打上“禁用”标签 disabledElements.add(loginButton); 这样,通过查询元素在不在 disabledElements 中,就可以知道它是不是被禁用了。

不过,假如元素从 DOM 树中被删除了,它的引用却仍然保存在 Set 中,因此垃圾回收程序也不能回收它。 为了让垃圾回收程序回收元素的内存,可以在这里使用 WeakSet: const disabledElements = new WeakSet(); const loginButton = document.querySelector('#login'); // 通过加入对应集合,给这个节点打上“禁用”标签 disabledElements.add(loginButton); 这样,只要 WeakSet 中任何元素从 DOM 树中被删除,垃圾回收程序就可以忽略其存在,而立即 释放其内存(假设没有其他地方引用这个对象)

6.8 迭代与扩展操作(扩展操作符 …)

有4种原生集合类型定义了默认迭代器:Array、所有定型数组、Map、Set

上述4个类型都支持顺序迭代,可以使用for…of循环。这也意味着这些类型都支持扩展操作符。扩展操作符在对可迭代对象执行浅复制时特别有用。

  • let arr=[1, 2, 3] let arr2 = [...arr]这样得到的数组,arr和arr2都是[1,2,3],但他们指向的地址并不相同。

  • 也可以构建部分元素。let arr3 = [1, 2, ...arr2] //[1, 2, 1, 2, 3]

  • 内部元素为对象时只会复制对象引用。let arr1=[{}] let arr4 = [...arr1] 当给arr1内部的引用地址添加值时,arr1[0].k1 = 1,打印arr2[0] 得到的也是1

上述4个类型都支持多种构建方法,比如Array.of(), Array.from()。与扩展操作符一起使用时,可以非常方便地实现互操作。

6.9 小结

JS中的对象是引用值,可以通过几种内置引用类型创建特定类型的对象。

  • 引用类型与传统面向对象编程语言中的类相似,但实现不同。
  • Object 类型是一个基础类型,所有引用类型都从它继承了基本的行为。
  • Array 类型表示一组有序的值,并提供了操作和转换值的能力。
  • 定型数组包含一套不同的引用类型,用于管理数值在内存中的类型。
  • Date 类型提供了关于日期和时间的信息,包括当前日期和时间以及计算。
  • RegExp 类型是 ECMAScript 支持的正则表达式的接口,提供了大多数基本正则表达式以及一些 高级正则表达式的能力。

JavaScript 比较独特的一点是,函数其实是 Function 类型的实例,这意味着函数也是对象。由于函数是对象,因此也就具有能够增强自身行为的方法。 因为原始值包装类型的存在,所以 JavaScript 中的原始值可以拥有类似对象的行为。有3种原始值包装类型:Boolean、Number 和 String。它们都具有如下特点。

  • 每种包装类型都映射到同名的原始类型。
  • 在以读模式访问原始值时,后台会实例化一个原始值包装对象,通过这个对象可以操作数据。
  • 涉及原始值的语句只要一执行完毕,包装对象就会立即销毁。

JavaScript 还有两个在一开始执行代码时就存在的内置对象:Global 和 Math。其中,Global 对 象在大多数 ECMAScript 实现中无法直接访问。不过浏览器将 Global 实现为 window 对象。所有全局 变量和函数都是 Global 对象的属性。Math 对象包含辅助完成复杂数学计算的属性和方法。 ECMAScript 6 新增了一批引用类型:Map、WeakMap、Set 和 WeakSet。这些类型为组织应用程序 数据和简化内存管理提供了新能力。
rr=[1, 2, 3] let arr2 = […arr]`这样得到的数组,arr和arr2都是[1,2,3],但他们指向的地址并不相同。

  • 也可以构建部分元素。let arr3 = [1, 2, ...arr2] //[1, 2, 1, 2, 3]

  • 内部元素为对象时只会复制对象引用。let arr1=[{}] let arr4 = [...arr1] 当给arr1内部的引用地址添加值时,arr1[0].k1 = 1,打印arr2[0] 得到的也是1

上述4个类型都支持多种构建方法,比如Array.of(), Array.from()。与扩展操作符一起使用时,可以非常方便地实现互操作。

6.9 小结

JS中的对象是引用值,可以通过几种内置引用类型创建特定类型的对象。

  • 引用类型与传统面向对象编程语言中的类相似,但实现不同。
  • Object 类型是一个基础类型,所有引用类型都从它继承了基本的行为。
  • Array 类型表示一组有序的值,并提供了操作和转换值的能力。
  • 定型数组包含一套不同的引用类型,用于管理数值在内存中的类型。
  • Date 类型提供了关于日期和时间的信息,包括当前日期和时间以及计算。
  • RegExp 类型是 ECMAScript 支持的正则表达式的接口,提供了大多数基本正则表达式以及一些 高级正则表达式的能力。

JavaScript 比较独特的一点是,函数其实是 Function 类型的实例,这意味着函数也是对象。由于函数是对象,因此也就具有能够增强自身行为的方法。 因为原始值包装类型的存在,所以 JavaScript 中的原始值可以拥有类似对象的行为。有3种原始值包装类型:Boolean、Number 和 String。它们都具有如下特点。

  • 每种包装类型都映射到同名的原始类型。
  • 在以读模式访问原始值时,后台会实例化一个原始值包装对象,通过这个对象可以操作数据。
  • 涉及原始值的语句只要一执行完毕,包装对象就会立即销毁。

JavaScript 还有两个在一开始执行代码时就存在的内置对象:Global 和 Math。其中,Global 对 象在大多数 ECMAScript 实现中无法直接访问。不过浏览器将 Global 实现为 window 对象。所有全局 变量和函数都是 Global 对象的属性。Math 对象包含辅助完成复杂数学计算的属性和方法。 ECMAScript 6 新增了一批引用类型:Map、WeakMap、Set 和 WeakSet。这些类型为组织应用程序 数据和简化内存管理提供了新能力。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/376013.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JVM内存溢出与内存泄露

1. 什么是内存溢出? 当前创建的对象的大小大于可用的内存容量大小&#xff0c;发生内存溢出。2. 什么是内存泄露? 该回收的垃圾对象没有被回收&#xff0c;发生了内存泄露&#xff0c;垃圾对象越堆越多&#xff0c; 可用内存越来越少&#xff0c;若可用内存无法存放新的垃圾…

【神经网络】LSTM为什么能缓解梯度消失

1.LSTM的结构 我们先来看一下LSTM的计算公式&#xff1a; 1.遗忘门&#xff1a; 2.输入门&#xff1a; 3.细胞状态 4.输出门 2.LSTM的梯度路径 根据LSTM的计算公式&#xff0c;可以得出LSTM的cell state与、、都存在计算关系&#xff0c;而、、的计算公式又全部都与有关&#x…

【目标检测】入门基础原理学一遍就够了吧

我菜就爱学 文章目录目标检测实战1 目标检测简介1.1 目标检测分类1.2 检测的任务1.3 目标定位实现的思路1.4 两种bbox名称解释2 R-CNN2.1 目标检测-Overfeat模型2.2 目标检测-R-CNN模型2.2.1 候选区域&#xff08;ROI&#xff09;2.2.2 CNN网络提取特征2.2.3 特征向量训练分类器…

vue2--基于zm-tree-org实现公司部门组织架构图

1.安装zm-tree-org npm i zm-tree-org -S2.引入使用 import Vue from vue; import ZmTreeOrg from zm-tree-org; import "zm-tree-org/lib/zm-tree-org.css";Vue.use(ZmTreeOrg);3.个人需求 组织架构图中&#xff1a;部门可拖动更改所属部门&#xff0c;可增加部门…

javascript学习+Vue学习+项目开发

一 《学习JavaScript数据结构与算法》 1.ES2015的类是基于原型语法的语法糖 2.TypeScript是一个开源的、渐进式包含类型的JavaScript超集 以&#xff0e;ts为扩展名 以tsc命令来编译它 TypeScript允许我们给变量设置一个类型&#xff0c;不过写法太啰唆了。 TypeScript有一…

博客系统——项目测试报告

目录 前言 博客系统——项目介绍 1、测试计划 1.1、功能测试 1.1.1、编写测试用例 1.1.2、实际执行步骤 1.2、使用Selenium进行Web自动化测试 1.2.1、引入依赖 1.2.2、提取共性&#xff0c;实现代码复用 1.2.3、创建测试套件类 1.2.4、博客登录页自动化测试 1.2.5、…

SQL注入报错注入之floor()报错注入原理分析

简介 对于SQL注入的报错注入通常有三个函数需要我们掌握&#xff1a; extractValue(xml_frag, xpath_expr)updateXML(xml_target, xpath_expr,new_xml)floor() 对于extractValue和updateXML函数来说比较好理解&#xff0c;就不做解释了&#xff0c;这里只对floor函数的报错注…

LabVIEW网络服务安全2

LabVIEW网络服务安全2在客户端应用程序中创建签名对请求进行签名要求您具有能够从客户端的编程语言调用的MD5摘要算法以及SHA256加密摘要算法的实现。这两种算法通常都可用于大多数平台。还需要&#xff1a;1. 要使用的HTTP方法的字符串&#xff08;“GET”、“POST”、“PUT”…

收发器上的10G网络变压器有什么特殊要求?

Hqst盈盛电子导读&#xff1a;那么&#xff0c;为了保证我们购买到正常的真正的具备POE功能的10G网络变压器&#xff0c;我们又要如何做呢以及如何判断呢&#xff1f;随着高速以太网网络传速的快速发展&#xff0c;10G以太网&#xff0c;10G网络变压器滤波器在各个领域也得到了…

基于SpringCloud的可靠消息最终一致性05:保存并发送事务消息

在有了分布式事务的解决方案、项目的需求、骨架代码和基础代码,做好了所有的准备工作之后,接下来就可以继续深入了解「核心业务」了。 在前面了解分布式事务时,可靠消息最终一致性方案的流程图是这样的: 图三十一:可靠消息最终一致性 整个的流程是: 1、业务处理服务在事务…

GLSL shader学习系列1-Hello World

这是GLSL shader系列第一篇文章&#xff0c;本文学习目标&#xff1a; 安装编辑工具编写hello world程序 安装插件 我使用VSCode编写shader代码&#xff0c;在VSCode上有两个好用的插件需要先装一下&#xff1a; Shader languages support for VS Code glsl-canvas&#xf…

优维科技实力入选《2023深圳金融业信息技术融合创新案例汇编》

日前&#xff0c;由深圳市金融科技协会编制的《2023深圳金融业信息技术融合创新案例汇编》于“2022中国&#xff08;深圳&#xff09;金融科技全球峰会”正式对外发布&#xff0c;共汇编近90个优秀金融技术应用实践案例&#xff0c;优维科技凭借在“某银行自动化运维XC改造项目…

STM32——毕设智能感应窗户

智能感应窗户 一、功能设计 以STM32F103芯片最小系统作为主控&#xff0c;实现自动监测、阈值设定功能和手动控制功能。 1、自动监测模式下&#xff1a; ① 采用温湿度传感器&#xff0c;实现采集当前环境的温度、湿度数值。 ② 采用光敏传感器&#xff0c;实现判断当前的环境…

【数据库/MySQL】MySQL三大日志提要

MySQL三大日志 mysql常用日志 错误日志查询日志慢查询日志事务日志【redo log&#xff08;重做日志&#xff09;、undo log&#xff08;回滚日志&#xff09;】二进制日志【bin log】 MySQL日志中比较重要的包括redo log&#xff08;重做日志&#xff09;、binlog&#xff0…

Docker 网络详解

前置网络知识 OSI七层网络模型 从下到上依次为&#xff1a;物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。 交换机&#xff1a;处在第二次&#xff0c;也就是数据链路层。作用&#xff1a;通过一个或者多个设备将不同子网连接在一起&#xff0c;相互通信&am…

Ant Design Vue 如何添加时间选择框

第一步引入 组件 import JDate from /components/jeecg/JDate使用 重点代码 <j-date<a-col :span"24/2"><a-form-item :labelCol"labelCol" :wrapperCol"wrapperCol" label"验收日期"><j-date placeholder"…

Lesson9---回归问题

9.1 机器学习基础 课程回顾 Python语言基础Numpy/Matplotlib/Pandas/Pillow应用TensorFlow2.0 低阶API 即将学习 机器学习、人工神经网络、深度学习、卷积神经网络典型模型的TensorFlow2.0实现 9.1.1 机器学习 机器学习&#xff08;Machine Learning&#xff09;&#xf…

2023年湖北七大员证书查询网站是哪里?

一、湖北七大员是哪七大员&#xff1f; 湖北七大员分别是&#xff1a;施工员、质量员、资料员、材料员、标准员、劳务员和机械员。其中施工员和质量员分为&#xff1a;市政、土建、装饰装修和设备安装四个子专业&#xff0c;其他不分。 二、湖北七大员证书查询网站是哪里&#…

Node下载阿里OSS存储文件【不知目录结构】

前言&#xff1a;前端传模型ID&#xff0c;后台根据ID去阿里OSS存储下载对应文件&#xff08;不知文件内部层级结构&#xff0c;且OSS只能单个文件下载&#xff09;&#xff0c;打包成zip字节流形式返回给前端下载。 需求分析&#xff1a; 生成OSS文件关系树Node做文件下载存…

ClickHouse 相关面试题

文章目录什么是 ClickHouse&#xff0c;它的特点是什么&#xff1f;ClickHouse的数据存储方式是什么&#xff0c;它与传统的行式存储有什么区别&#xff1f;ClickHouse 引擎ClickHouse的数据模型是什么&#xff0c;它与传统的关系型数据库的数据模型有什么区别&#xff1f;Clic…