javascript 全面解析 6w字长文 基础篇

JavaScript基础篇

前言:本人将网上的一些视频资源与红宝书作为参考进行总结共分为上中下三篇,在进行学习之前建议有一些html,css基础有其他语言基础更好。为了更好地掌控整体进度和有基础的同学进行学习,做出以下xmind流程图如已经掌握某些知识可以直接跳过。如时间有限可以按照我下面的图例,将不重要的部分权且略过。

在基础篇主要是将js进行一个理论的概述,类似面向对象,set,map,正则,原型链,闭包及大量的ES6,ES7,ES8相关内容等将会放在中篇汇总,那么对于高级篇将先进行对前面内容进行整合补充,后再结合json,ajax等将代理反射、promise、模块化、异常处理结合。

文章目录


图例:(必看):

⭐ 了解即可

✔️ 必须掌握

✅ 强烈推荐

⭕ 有难点(可以略过,后面再学)

❗ 有难点(值得深度思考)

❎ 暂时没必要学

🔥 案例 或 面试题

在这里插入图片描述

JavaScript基础篇

JavaScript简介

那么接下来就进行基础篇的总结,总结时或多或少会涉及些后面的甚至ES6的相关内容,其次是内容如有错误希望大家批评指正,这样我们才会共同进步。

JavaScript(Java脚本)是一种基于对象(Object)和事件驱动( Event Driven)并具有安全性能的脚本语言,使用JavaScript可以轻松的实现与HTML的互操作,并且完成丰富的页面交互效果,它是通过嵌入或调入在标准的HTML语言中实现的,它的出现弥补了HTML的缺陷,是java与HTML折衷的选择。

✔️上面是百度的描述,我根据了解到的总结如下:javascript是一种运行在客户端的脚本语言,主要的面向方向为浏览器可以做出各种特效和点击事件等,最早主要是用于表单验证的,js是一种单线程语言就是各种任务进行时需要先将其中一个做完再做另一个,是基于异步进行的这点很重要。其次js和java虽然名字相似但没啥关系,类似于老婆和老婆饼? 与各种高级语言不同js当年听说只用了十几天就设计出来了所以说也有些缺陷,是一种弱类型语言于是后面就有了ES5,6等版本来弥补之前的问题。在之前的版本定义变量时基本不需要考虑数据类型直接 var 一个即可。数组存储时也是一个数组可以存储number,string等不同的类型。还有一点是js的执行过程是js 解释器( js 引擎)逐行来进行解释并执行所以被称作为解释型语言。在连续赋值时如a=b=3 这种时是从左到右进行定义变量,但赋值却是从右到左。编译型语言是指java,c++那种全部程序执行完之后,才会进行执行报错。

⭐JavaScript历史

个人认为对于js的历史稍微了解一些即可,能够知道js的发展历程等就够用了学习语言关键还是语法思想这些。以下文章来自《JavaScript高级程序设计》

随着 Web 日益流行,对客户端脚本语言的需求也越来越强烈。当时,大多数用户使用 28.8kbit/s 的调制解调器上网,但网页变得越来越大、越来越复杂。为验证简单的表单而需要大量与服务器的往返通信成为用户的痛点。想象一下,你填写完表单,单击“提交”按钮,等 30 秒处理,然后看到一条消息,告诉你有一个必填字段没填。网景在当时是引领技术革新的公司,它将开发一个客户端脚本语言来处理这种简单的数据验证提上了日程。1995 年,网景公司一位名叫 Brendan Eich 的工程师,开始为即将发布的 Netscape Navigator 2 开发一个叫 Mocha(后来改名为 LiveScript)的脚本语言。当时的计划是在客户端和服务器端都使用它,它在服务器端叫 LiveWire。为了赶上发布时间,网景与 Sun 公司结为开发联盟,共同完成 LiveScript 的开发。就在 Netscape Navigator 2 正式发布前,网景把 LiveScript 改名为 JavaScript,以便搭上媒体当时热烈炒作 Java 的顺风车。2 第 1 章 什么是 JavaScript 由于 JavaScript 1.0 很成功,网景又在 Netscape Navigator 3 中发布了 1.1 版本。尚未成熟的 Web 的受欢迎程度达到了历史新高,而网景则稳居市场领导者的位置。这时候,微软决定向 IE 投入更多资源。就在 Netscape Navigator 3 发布后不久,微软发布了 IE3,其中包含自己名为 JScript(叫这个名字是为了避免与网景发生许可纠纷)的 JavaScript 实现。1996 年 8 月,微软重磅进入 Web 浏览器领域,这是网景永远的痛,但它代表 JavaScript 作为一门语言向前迈进了一大步。微软的 JavaScript 实现意味着出现了两个版本的 JavaScript:Netscape Navigator 中的 JavaScript,以及 IE 中的 JScript。与 C 语言以及很多其他编程语言不同,JavaScript 还没有规范其语法或特性的标准,两个版本并存让这个问题更加突出了。随着业界担忧日甚,JavaScript 终于踏上了标准化的征程。1997 年,JavaScript 1.1 作为提案被提交给欧洲计算机制造商协会(Ecma)。第 39 技术委员会(TC39)承担了“标准化一门通用、跨平台、厂商中立的脚本语言的语法和语义”的任务(参见 TC39-ECMAScript)。TC39 委员会由来自网景、Sun、微软、Borland、Nombas 和其他对这门脚本语言有兴趣的公司的工程师组成。他们花了数月时间打造出 ECMA-262,也就是 ECMAScript(发音为“ek-ma-script”)这个新的脚本语言标准。1998 年,国际标准化组织(ISO)和国际电工委员会(IEC)也将 ECMAScript 采纳为标准(ISO/ IEC-16262)。自此以后,各家浏览器均以 ECMAScript 作为自己 JavaScript 实现的依据,虽然具体实现
各有不同。

ECMA-262 第 6 版,俗称 ES6、ES2015 或 ES Harmony(和谐版),于 2015 年 6 月发布。这一版包含了大概这个规范有史以来最重要的一批增强特性。ES6 正式支持了类、模块、迭代器、生成器、箭头函数、期约、反射、代理和众多新的数据类型。
ECMA-262 第 7 版,也称为 ES7 或 ES2016,于 2016 年 6 月发布。这次修订只包含少量语法层面的增强,如 Array.prototype.includes 和指数操作符。
ECMA-262 第 8 版,也称为 ES8、ES2017,完成于 2017 年 6 月。这一版主要增加了异步函数(async/ await)、SharedArrayBuffer 及 Atomics API,以及 Object.values()/Object.entries()/Object. getOwnPropertyDescriptors()和字符串填充方法,另外明确支持对象字面量最后的逗号。
ECMA-262 第 9 版,也称为 ES9、ES2018,发布于 2018 年 6 月。这次修订包括异步迭代、剩余和扩展属性、一组新的正则表达式特性、Promise finally(),以及模板字面量修订。
ECMA-262第 10版,也称为 ES10、ES2019,发布于 2019年 6月。这次修订增加了 Array.prototype. flat()/flatMap()、String.prototype.trimStart()/trimEnd()、Object.fromEntries()方法,以及 Symbol.prototype.description 属性,明确定义了 Function.prototype.toString()的返回值并固定了 Array.prototype.sort()的顺序。另外,这次修订解决了与 JSON 字符串兼容的问题,并定义了 catch 子句的可选绑定。

前端三剑客

html是结构、css是表现、js是行为。如果把三者比喻成动画片,那么html就是铅笔稿,Css就是在铅笔稿之上加入的颜色,JS就是最后添加的动作,三者结合便可以呈现一部完美的作品了。

根据我的了解来说,js占的比重非常多js是一个前端人员功底的体现,因此在学习过程中尽量理解深入这样后期学习vue等框架的时候才能理解顺畅茅塞顿开。后期我也准备做一份关于js特效的文章和js的一些重难点面试题等。这里给大家的建议是谦虚学习,毕竟任何一门语言的出现及盛行都不是没有原因的,不要因为前期简单而马虎跳着学。我始终认为一个成功的大师时刻都会保持一颗学徒的心。

img!

✔️JavaScript组成、作用

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0IH1xFWr-1648293077238)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220215213933495.png)]

ECMAScript 是由ECMA 国际( 原欧洲计算机制造商协会)进行标准化的一门编程语言,这种语言在万维网上应用广泛,它往往被称为 JavaScript 或 JScript,但实际上后两者是 ECMAScript 语言的实现和扩展。

文档对象模型(Document Object Model,简称DOM),是W3C组织推荐的处理可扩展标记语言的标准编程接口。通过 DOM 提供的接口可以对页面上的各种元素进行操作(大小、位置、颜色等)。主要就是对网页内的DOM元素进行操作。

BOM (Browser Object Model,简称BOM) 是指浏览器对象模型,它提供了独立于内容的、可以与浏览器窗口进行互动的对象结构。通过BOM可以操作浏览器窗口,比如弹出框、控制浏览器跳转、获取分辨率等。主要就是对浏览器进行操作。

JavaScript的作用

  • 表单动态校验(密码强度检测) ( JS 产生最初的目的 )
  • 网页特效
  • 服务端开发(Node.js)
  • 桌面程序(Electron)
  • App(Cordova)
  • 控制硬件-物联网(Ruff)
  • 游戏开发(cocos2d-js)

🔥 学习资料及计划

以下推荐仅代表个人意见

书籍推荐:JavaScript高级程序设计✅ ,JavaScript权威指南,JavaScriptDOM编程艺术等

视频推荐:黑马(不多解释懂的都懂) ,后盾人✅ (建议有一定基础后再看),b站小夏老师,前端周老师 ( 这两个老师的视频更适合查缺补漏面试等)。

软件推荐:编程软件:vs code ✅ webstrom 笔记:typora ( 写博客,记笔记。配合markdown语法食用) 印象笔记

博客网站推荐:csdn(虽然网上好多劝退的,但对于小白来说还是很友好的 ) github gitee 等。

论坛:思否,Stack Overflow(可能我太菜了没玩明白),知乎,掘金等。

刷题网站:牛客(强推,主要用来刷网络,os等基础题) 算法肯定首推 leetcode了 。

**学习计划的建议:**这里我认为如果有时间真的要深入的把算法,网络这些内功练好其次就是最起码要有一个拿得出手的语言。最好是学一下c++或者java。如果时间不足只学前端也是可以的但后面一定会乏力随意及时补充内功也是有必要的。这里我只能给小白些建议毕竟我也不是什么大佬。 对于js方面个人认为是可以按照如下步骤学的,先找一个可以接受的视频学一遍,然后按照红宝书一章一章的查缺补漏,如某章没完全消化可以选择直接去b站搜索,也可以暂时跳过因为有些知识点是需要后面的铺垫的。最后就是做些常见特效和网站的搭建,这里我的建议是不要用jquery。不是说jq已经淘汰的问题,如果想对js理解非常深刻还是建议以原生入手,三剑客熟练后再接触任何框架都会学的很快并且对底层的源码也会很了解。然后再像我这样写篇博客记录下,这样包括博客我们就已经学了最少三遍js了,剩下的就是日常工作中遇到问题再解决,这也并不是学习阶段所能做得到的。任何语言都不可能只学一遍就会了,所以还是要经常反复练习,大概就这样。

✔️语言基础

JavaScript使用

工欲善其事,必先利其器:我这里使用的是 vscode 相关的安装和设置,汉化插件等就不多赘述了,有时间可以单出一篇文章。vscode很多插件还是很好用的,大家可以看下b站课程或其他文章关于插件的文章很多的。

js可以以行内,内嵌或外部引入三种方式由html文件引入,类似于css一样。

行内式:(不推荐)

<input type="button" value="点我试试" onclick="alert('Hello World')" />//写在行内 代码过多会导致错乱,不好后期维护

内嵌式:

 <script> //一般用于平时写代码或代码数量少时
        alert('Hello World');
    </script>

内嵌式可以放在两个地方:一个是放在 和标签之间 另一个是放在整个文档最下面即 之上(推荐),这种和外部文件是一样的都需要考虑加载顺序的问题。

在这里插入图片描述

在这里插入图片描述

关于这里的两种方式其实是由一些区别的:因为js的解释运行顺序是从上到下一行一行的读取,因此

① 就是把标签放在整个文档最下面

② 使用window的监听事件 使js文档必须在整个文档加载后再出现:(两者区别如下所示)

❎③还有一种方式是红宝书提到的就是将

window.addEventListener('load',function(){
   
//   我是网页上所有资源(图片,音频,视频等)被加载后才会触发load事件。
})
window.addEventListener('DOMContentLoaded',function(){
   
    //   我是DOM节点加载后再触发 但各个浏览器兼容性不同 
    })

DOMContentLoade和load区别

DOMContentLoaded是DOM节点加载后再触发 但各个浏览器兼容性不同,load是网页上所有资源(图片,音频,视频等)被加载后才会触发load事件。

简单理解下浏览器和输入链接后浏览器做了什么事:首先输入链接后网页会发生跳转到我们的网页,这是网页是空白的因为html并未加载进来,慢慢的图片视频,文字加载了,这就是按照解析顺序先解析html文档然后是css,这就是就是DOM元素的加载,全部展示出来就触发了DOMContentLoaded。

浏览器渲染原理:

当在浏览器地址栏输入URL回车后,浏览器会发送请求到服务器,服务器将请求的HTML文档发送回浏览器,浏览器将文档下载下来后,便开始从上到下解析文档,解析完成之后,会生成DOM。如果页面中有css,会根据css的内容形成CSSOM,然后DOM和CSSOM会生成一个渲染树,最后浏览器会根据渲染树的内容计算出各个节点在页面中的确切大小和位置,并将其绘制在浏览器上。

因此这样看来load一定是在DOMContentLoade加载之后才能加载,而DOMContentLoade又不支持全部浏览器,那么就只能用load加载 或者使用兼容代码,将DOMContentLoade兼容所有浏览器 如下

function ready(fn){
   
 
    if(document.addEventListener) {
   
        document.addEventListener('DOMContentLoaded', function() {
   
            document.removeEventListener('DOMContentLoaded',arguments.callee, false);
            fn();
        }, false);
    } 
 
    // 如果IE
    else if(document.attachEvent) {
   
        // 确保当页面是在iframe中加载时,事件依旧会被安全触发
        document.attachEvent('onreadystatechange', function() {
   
            if(document.readyState == 'complete') {
   
                document.detachEvent('onreadystatechange', arguments.callee);
                fn();
            }
        });
 
        // 如果是IE且页面不在iframe中时,轮询调用doScroll 方法检测DOM是否加载完毕
        if(document.documentElement.doScroll && typeof window.frameElement === "undefined") {
   
            try{
   
                document.documentElement.doScroll('left');
            }
            catch(error){
   
                return setTimeout(arguments.callee, 20);
            };
            fn();
        }
    }
};

外部文件

当需要进行分档存储js,css,html自然是要采用这种方式引入外部js文件。但仍需考虑上述问题:html和js谁先加载的问题。

<script src="./js.js"></script> 

语法

打印输入输出

解决完如何引入js以后,就可以进行写代码了,这里以内嵌为例:输入输出方式一般有四种:

	 document.write("hello word !");//我是页面输出
      console.log("hello word !"); //我是控制台输出('hello word !');
      alert("hello word !"); //我是弹窗('hello word !');
      prompt("请输入内容");//我是输入内容

console.log 控制台输出 ( 浏览器F12 点console)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BHqDfplI-1648293077239)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220215225358846.png)]

alert 弹窗输出

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3Gm6gqae-1648293077239)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220215225900287.png)]

document.write 页面输出

在这里插入图片描述

prompt 输入

在这里插入图片描述

控制台输出

第一种控制台输出就是常用的在浏览器中调出检查 F12

第二种是在vscode (或其他软件)自带的调试控制台

第三种是利用 live serve 插件来进行实时刷新 以提高工作效率

我这里使用vscode举例:在js页面按 快捷键按 F5控制台会出现打印的内容这样就可以避免切到浏览器刷新了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LPOJiQLM-1648293077240)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220215230718317.png)]

这里有时候并不会直接打印而是弹出下面的框框,这时我们选择 Chrome 会跳到另一个文件内

在这里插入图片描述

这时在url输入框内输入该网页的网址即可,再回到原js 按F4就可以在调试控制台输出了

在这里插入图片描述

但在调试控制台输出有个弊端就是无法进行点击等操作,只能看到输出的内容,此时我们可以使用另一种办法:使用闻名已久的即时刷新网页的插件:live serve ;安装->设置->使用 如下三图所示

在这里插入图片描述

在这里插入图片描述

最后点击 下面的 go live 就可以 实时刷新浏览器了

在这里插入图片描述

字面量和变量

变量和字面量是每个语言都会有的其他语言中 会以数据类型用不同的格式命名变量 如 int a=10; char b=‘A’;

在js中虽有数据类型 但变量命名并不需要指定数据格式直接使用 var 就可以(ES6中使用 let 和const 区别后面讲 )

变量赋值的过程分为:声明赋值两步 ( 这里分为两步是有意义的,因为变量会提升,后面来说)

  • var 是一个 JS关键字,用来声明变量( variable 变量的意思 )。使用该关键字声明变量后,计算机会自动为变量分配内存空间,不需要程序员管
  • age 是程序员定义的变量名,也可以叫做标识符 我们要通过变量名来访问内存中分配的空间
  • 10 可以称作为 字面量 是数字类型的字面量,如果是布尔值那么就是 true 或者false 字符串 就是 ‘sjlwd’
//  声明变量  
var age; //  声明一个 名称为age 的变量     
//赋值
age=10; //给age赋值为10 
//正常会同时进行 声明和赋值 即
var age=10;  //此时的一步操作其实和之前两步是一样的  这是var 的特性
//变量可以直接不声明直接赋值  但不推荐 在严格模式下报错
myname='lee'; //可以但不推荐

关键字与保留字

标识符起名时要遵循以下原则:

  • 首先要知道的是,ECMAScript 中一切都区分大小写。无论是变量、函数名还是操作符,都区分大小写。换句话说,变量 test 和变量 Test 是两个不同的变量。类似地,typeof 不能作为函数名,因为它是一个关键字(后面会介绍)。但 Typeof 是一个完全有效的函数名。

  • 标识符,就是变量、函数、属性或函数参数的名称。标识符可以由一或多个下列字符组成:

  • 第一个字符必须是一个字母、下划线(_)或美元符号($); 剩下的其他字符可以是字母、下划线、美元符号或数字。

  • 标识符中的字母可以是扩展 ASCII(Extended ASCII)中的字母,也可以是 Unicode 的字母字符,如 À 和 Æ(但不推荐使用)。

  • 按照惯例,ECMAScript 标识符使用驼峰大小写形式,即第一个单词的首字母小写,后面每个单词的首字母大写

    关键字与保留字

    ECMA-262 描述了一组保留的关键字,这些关键字有特殊用途,比如表示控制语句的开始和结束,或者执行特定的操作。按照规定,保留的关键字不能用作标识符或属性名。ECMA-262 第 6 版规定的所有关键字如下:
    break do in typeof
    case else instanceof var
    catch export new void
    class extends return while
    const finally super with
    continue for switch yield
    debugger function this
    default if throw
    delete import try

    规范中也描述了一组未来的保留字,同样不能用作标识符或属性名。虽然保留字在语言中没有特定用途,但它们是保留给将来做关键字用的。以下是 ECMA-262 第 6 版为将来保留的所有词汇。
    始终保留:
    enum
    严格模式下保留:
    implements package public
    interface protected static
    let private
    模块代码中保留:
    await

注释

vs code 单行注释快捷键 ctrl +/ 多行快捷键 Alt+shift+A

//ECMAScript 采用 C 语言风格的注释,包括单行注释和块注释。单行注释以两个斜杠字符开头,如:
// 单行注释
//块注释以一个斜杠和一个星号(/*)开头,以它们的反向组合(*/)结尾,如:
/* 这是多行
注释 */ 

✔️数据类型

数据类型简介

ECMAScript 有 6 种简单数据类型(也称为原始类型):Undefined、Null、Boolean、Number、String 和 Symbol。Symbol(符号)是 ECMAScript 6 新增的。还有一种复杂数据类型叫 Object(对象)。Object 是一种无序名值对的集合。因为在 ECMAScript 中不能定义自己的数据类型,所有值都可以用上述 7 种数据类型之一来表示。只有 7 种数据类型似乎不足以表示全部数据。但 ECMAScript 的数据类型很灵活,一种数据类型可以当作多种数据类型来使用。

在这里插入图片描述

✔️赋值操作:

除了之前有提到过的使用字面量直接创建对象外还可以采用new构造函数的方法来创建对象,但此方法仅仅对于引用值才可以使用,string,Number,object等都可以使用构造函数创建.本质上来说 var b=‘abc’;的过程也是用构造函数创建的。只是区别在于 a是字面量而 g是对象。这就是原始值与引用值的区别(后面会讲)

var a=10;   //"number"表示值为数值;
var b='abc';   //"string"表示值为字符串;
var c=true;  //"boolean"表示值为布尔值;
var d=null;  //空值

var e=undefined;//"undefined"表示值未定义;
let f=new Object();//"object"表示值为对象(而不是函数)或 null;
let g=new String('ccc');
let h=Symbol();//"symbol"表示值为符号。创建时不需要 new
let i=new Function();//"function"表示值为函数;

console.log(b);//abc  //原始值
console.log(g);//String ('ccc') 对象的实例类似于指针将g指向字符串为‘abc’的内存空间

如果学过原型相关课程应该知道 构造函数的prototype和对象都有 一个constuctor属性这个属性可以将对象和原型对象指回构造函数,那么 b和g的constuctor如果相同就代表他们指向同一个构造函数,也就证明字面量赋值时其实也是由构造函数创建的、

console.log(b.constructor===g.constructor);//true

✔️Number

与其他语言不同的是在js里 Number既可以表示整数也可以表示浮点数,JavaScript的Number类型为[双精度IEEE 754 64位浮点类型。例如:

let intNum = 55; // 整数
let intFloat = 55.522; // 整数
console.log(intNum);
console.log(intFloat);

✔️进制表示

整数也可以用八进制(以 8 为基数)或十六进制(以 16 为基数)字面量表示。对于八进制字面量,第一个数字必须是零(0),然后是相应的八进制数字(数值 0~7)。如果字面量中包含的数字超出了应有的范围,就会忽略前缀的零,后面的数字序列会被当成十进制数,如下所示:

let octalNum1 = 070; // 八进制的 56 
let octalNum2 = 079; // 无效的八进制值,当成 79 处理
let octalNum3 = 08; // 无效的八进制值,当成 8 处理
let hexNum1 = 0xA; // 十六进制 10 
let hexNum2 = 0x1f; // 十六进制 31

由于 JavaScript 保存数值的方式,实际中可能存在正零(+0)和负零(0)。正零和负零在所有情况下都被认为是等同的,这里特地说明一下。

浮点数

要定义浮点值,数值中必须包含小数点,而且小数点后面必须至少有一个数字。虽然小数点前面不是必须有整数,但推荐加上。下面是几个例子:

let floatNum1 = 1.1; 
let floatNum2 = 0.1; 
let floatNum3 = .1; // 有效,但不推荐
let floatNum = 3.125e7; // 等于 31250000  科学计数法表示

在浮点数这里主要就有一个点比较重要也是一道非常经典的面试题:

🔥 0.1+0.2==0.3 这是对还是错 为什么?

按照正常的理解肯定是觉得 0.1+0.2==0.3的但是对于计算机而言计算方法远没有那么简单。之所以存在这种舍入错误,是因为使用了 IEEE 754数值,计算机是以0,1将数据进行转换在进行加减的因此这其中会涉及到很多算法包括原码,反码,补码。小数,负数又有自己的独特算法这个具体可以参考下计算机组成原理大概前两章就有。总的来说计算机在做浮点数计算时会将部分精度省略 最后结果为 0.1 + 0.2 == 0.300000004 这不是js的问题 而是很多语言都有这个问题。这里想深究的朋友可以去搜下专门的博客,我这面不仔细推导了,这里面涉及到原反补码和计算机底层的东西了解下总归是好的

✔️数字型范围

JavaScript中数值的最大和最小值

最大值:Number.MAX_VALUE,这个值为: 1.7976931348623157e+308

最小值:Number.MIN_VALUE,这个值为:5e-32

✔️数字型三个特殊值

Infinity ,代表无穷大,大于任何数值

-Infinity ,代表无穷小,小于任何数值

NaN ,Not a number,代表一个非数值 NaN 有几个独特的属性。首先,任何涉及 NaN 的操作始终返回 NaN(如 NaN/10),在连续多步计算时这可能是个问题。其次,NaN 不等于包括 NaN 在内的任何值。例如,下面的比较操作会返回 false:要确定一个值是不是有限大(即介于 JavaScript 能表示的最小值和最大值之间),可以使用 **isFinite()**函数

✔️isNaN

用来判断一个变量是否为非数字的类型,返回 true 或者 false

也可以用 typeof 来查看数值类型

let intNum = 55; // 整数
let intFloat = 55.522; // 整数
console.log(Number.isNaN(intNum)); //isNaN 是否不是一个数值 false 表示是
console.log(typeof intFloat); //number


console.log(isNaN(NaN)); // true 
console.log(isNaN(10)); // false,10 是数值
console.log(isNaN("10")); // false,可以转换为数值 10 
console.log(isNaN("blue")); // true,不可以转换为数值
console.log(isNaN(true)); // false,可以转换为数值 1 

✔️数值的转换:

//Number()、parseInt() 和 parseFloat()的区别

Number()是转型函数,可用于任何数据类型。后两个函数主要用于将字符串转换为数值。

let a='123';
let b=true;
let c='123.001';

console.log(Number(a));  //123
console.log(Number(b));  //1  类型转换时 布尔的true为1 false 为 0;
console.log(parseInt(a)); //123 
console.log(parseFloat(c)); //123.001 注意parseFloat只接收字符串 并且12.00会默认转换为12 

parseInt()

parseInt(*string*, *radix*) 解析一个字符串并返回指定基数的十进制整数, radix 是2-36之间的整数,表示被解析字符串的基数。 这里的第二个参数是很多人不太注意的地方,第二个位置如果填入就表示进制如下所示:

parseInt("0xF", 16);//15
parseInt("F", 16);//15
parseInt("17", 8);//15
parseInt(021, 8);//15
parseInt("015", 10);   // parseInt(015, 8); 返回 13
parseInt(15.99, 10);//15
parseInt("15,123", 10);//15
parseInt("FXX123", 16);//15
parseInt("1111", 2);//15
parseInt("15 * 3", 10);//15
parseInt("15e2", 10);//15
parseInt("15px", 10);//15
parseInt("12", 13);//15

🔥 关于parseInt的一道面试题:(涉及到map不太了解的可以先去看看,map相关视频)

var arr = ['1', '2', '3'].map(parseInt)
console.log(arr)//[1, NaN, NaN]

parseInt('1', 0)
parseInt('2', 1)
parseInt('3', 2)

这道题主要考察map的三个参数和parseInt的第二个参数问题,map三个参数分别代表,元素,元素对应的索引和原数组。这道题的意思就是先将数组的第一个元素作为parsInt的第一个参数,索引作为第二个参数。进行第一次循环的结果为,parseInt(‘1’, 0) 也就是 1,因此就是 parseInt(‘2’, 1) 注意这里的 1代表进制 1进制里并没有2 因此结果为 NaN ,第三次 就是 parseInt(‘3’, 2) 2进制里同样没有3 所以也返回 NaN

Number()和parseInt

考虑到用 Number()函数转换字符串时相对复杂且有点反常规,通常在需要得到整数时可以优先使用 ==parseInt()==函数。另外将字符串等转换为数字型时还可以使用 +, 一元加号运算符 与 Number() 等价.都是通过 toNumber() 的抽象方法进行转换

let num1 = parseInt("1234blue"); // 1234  字符串类型遇到非数字就停止
let num2 = parseInt(""); // NaN   空字符串为NaN
let num3 = parseInt("0xA"); // 10,解释为十六进制整数
let num4 = parseInt(22.5); // 22    
let num5 = parseInt("70"); // 70,解释为十进制值
let num6 = parseInt("0xf"); // 15,解释为十六进制整数

//+等价Number() 都是调用toNumber()  paseInt()有两个参数 区别有很多 可以做下对比
let a=true;
let b=null;
let c=Infinity;
let d=''
let e='123abc';
let f ="000011"; // 11 
console.log(Number(a));//1
console.log(Number(b));//0
console.log(Number(c));//Infinity
console.log(Number(d));// 0
console.log(Number(e));// NaN
console.log(+e);   // NaN  //与Number函数结果相同
console.log(parseInt(e));// 123  //与Number结果不同 
console.log(Number(f));// 11

✔️String

String(字符串)数据类型表示零或多个 16 位 Unicode 字符序列。字符串可以使用双引号(")、单引号(')或反引号(`)标示,因此下面的代码都是合法的:与Number相同 String声明赋值的时候也可以有两种方式。

let firstName = "John";  
let lastName = 'Jacob'; 
let lastName = 'JacobJi"ngleheime"rschmidt'; //嵌套 可以外单内双 也可以外双内单
let lastName = 'Jacob";   //但是不要一个单一个双 会报错
let lastName = `Jingleheimerschmidt` //es6语法 

转义字符

任何语言在处理字符串时都会进行转义字符的处理。类似HTML里面的特殊字符,字符串中也有特殊字符,我们称之为转义符。转义符都是 \ 开头的,常用的转义符及其说明如下:

转义符 解释说明
\n 换行符,n 是 newline 的意思
\ \ 斜杠 \
’ 单引号
" ”双引号
\t tab 缩进
\b 空格 ,b 是 blank 的意思

字符串长度

字符串类似于数组因此都具有length属性 并且都可以通过 [ ] 方式查看里面的数值、这里和数组是有区别的。数组不仅仅可以查看甚至可以修改里面的内容。这里有个重要的概念就是字符串不可以被修改,我们后面所说用内置的方法也仅仅是增删改查出一个新的字符串。具体的方法在内置对象会说、类似concat,slice,substr等 这些都是返回新的字符串。在内存中开辟一块新的空间。

let str='abcdef';

console.log(str.length); //6 获取字符串长度 
console.log(str[0]);  //a  查看第1项的数值
str[1]='b'; 
console.log(str[0]);  //a 这里不可以修改 达咩!!!

//案例 注意转义字符 
let text = "This is the letter sigma: \u03a3."; 
console.log(test.length); //28   \u03a3 是一个字符 Unicode码  另外空格也算一个

字符串的拼贴、转换

除了使用cancat等内置方法外 还可以使用 +运算符进行字符串与字符串 字符串与其他类型的拼贴 如下所示:

let lang = "Java"; 
lang = lang + "Script"; 
//拼接案例
let lang = "Java"; 
lang = lang + 50+'元';
console.log(lang); //Java50元

如果想将其他类型转换为字符串还可以使用 toString() 方法进行转换 ,toString()虽然没有参数但可以传一个底数表示进制

let age = 11; 
let ageAsString = age.toString(); // 字符串"11" 
let found = true; 
let foundAsString = found.toString(); // 字符串"true" 

console.log(num.toString()); // "10" 
console.log(num.toString(2)); // "1010" 
console.log(num.toString(8)); // "12" 
console.log(num.toString(10)); // "10" 
console.log(num.toString(16)); // "a" 

toString()方法可见于数值、布尔值、对象和字符串值。(没错,字符串值也有 toString()方法,该方法只是简单地返回自身的一个副本。)null 和 undefined 值没有 toString()方法。

与number相同的 String也有转型函数 String()l

let value1 = 10; 
let value2 = true; 
let value3 = null; 
let value4; 
console.log(String(value1)); // "10" 
console.log(String(value2)); // "true" 
console.log(String(value3)); // "null" 
console.log(String(value4)); // "undefined" 

✔️Boolean

Boolean(布尔值)类型是 ECMAScript 中使用最频繁的类型之一,有两个字面值:true 和 false。这两个布尔值不同于数值,因此 true 不等于 1,false 不等于 0。下面是给变量赋布尔值的例子:

let found = true;  //true和false 区分大小写 首字母不要大写 
let lost = false; 

类型转换

在这里插入图片描述

布尔虽然简单但也非常常用,经常用在流程控制语句当中作为判定条件。同样通过转型函数 Boolean也可以做到类型转换,在数值中0为假 非零为真 而不只是1为真

console.log(Boolean(5));true
console.log(Boolean(0));true
console.log(Boolean('5'));true

🔥Null和Undefined区别

这里就不分别来谈了,总结了以下几点进行对比和区分更有利于学习的效率。

  • Undefined字面意思即未定义,null为空。一般来说声明的变量如果未赋值那么就会返回undefined,null一般是在定义的时候人为设置的表示暂时不知道该设置什么值就拿null先占位。

  • undefined 原始值的初始值;未定义; nu1l对象的初始值,空对象的引用;

  • 如果函数没有给返回值时,返回的是undefined、

  • undefined的类型为undefined null为 object。二者作为布尔值时都为false (null为object可能是js早期的一个失误 实际上应该不算object)

  • 比较二者时 使用== 返回的是true ====返回的是 fslse 这个操作符会为了比较而转换它的操作数

  • 二者再用Number转换时 Undefined会显示NaN 即 不是一个数字,而null 则显示 0

  • null还可以用于垃圾回收时

  • undefined是全局的一个变量

  • 用等于操作符(==)比较 null 和 undefined 始终返回 true。但要注意,这个操作符会为了比较
    而转换它的操作数

  • undefined 值是由 null 值派生而来的,因此 ECMA-262 将它们定义为表面上相等 (红宝书)

  • console.log(null == Object);  //false
    console.log(null === Object);  //false
    console.log(typeof null);  //object
    console.log(typeof undefined); //undefined
    console.log(Number(undefined)); //NaN
    console.log(Number(null)); //0
    

⭕ Symbol

Symbol(符号)是 ECMAScript 6 新增的数据类型。符号是原始值,且符号实例是唯一、不可变的。符号的用途是确保对象属性使用唯一标识符,不会发生属性冲突的危险。最主要的就是唯一性 symbol在迭代器中也有所用到 symbol.iterater 属性 ,代表了具有迭代属性。

var a=Symbol('c');
var b=Symbol('c');
console.log(a==b);//false
console.log(typeof a); //symbol

调用 Symbol()函数时,也可以传入一个字符串参数作为对符号的描述(description),将来可以通过这个字符串来调试代码。但是,这个字符串参数与符号定义或标识完全无关:

最重要的是,Symbol()函数不能与 new 关键字一起作为构造函数使用。这样做是为了避免创建符号包装对象,像使用 Boolean、String 或 Number 那样,它们都支持构造函数且可用于初始化包含原始值的包装对象:但也可以自己进行包装


//使用符号包装对象
var sym=Symbol();
var c=new Object(sym);
console.log(typeof c);

符号的另一个用处就是 全局符号注册表

第一次使用某个字符串调用时,它会检查全局运行时注册表,发现不存在对应的符号,于是就会生成一个新符号实例并添加到注册表中。后续使用相同字符串的调用同样会检查注册表,发现存在与该字符串对应的符号,然后就会返回该符号实例。

Symbol.for() 和Symbol.keyFor() 定义全局符号和查询全局注册表


//查询全局注册表 
var o=Symbol('ww');
var m=Symbol.for('ww');
console.log(Symbol.keyFor(o)); //普通符号
console.log(Symbol.keyFor(m)); //全局符号

常用内置符号
ECMAScript 6 也引入了一批常用内置符号(well-known symbol),用于暴露语言内部行为,开发者可以直接访问、重写或模拟这些行为。这些内置符号都以 Symbol 工厂函数字符串属性的形式存在。这些内置符号最重要的用途之一是重新定义它们,从而改变原生结构的行为。比如,我们知道for-of 循环会在相关对象上使用 Symbol.iterator 属性,那么就可以通过在自定义对象上重新定义Symbol.iterator 的值,来改变 for-of 在迭代该对象时的行为。这些内置符号也没有什么特别之处,它们就是全局函数 Symbol 的普通字符串属性,指向一个符号的实例。所有内置符号属性都是不可写、不可枚举、不可配置的。

Object 类型后面结合原型,类一起来分析

✔️操作符

算术运算符

一元操作符

只操作一个值的操作符叫一元操作符(unary operator)。一元操作符是 ECMAScript中最简单的操作符。

运算符 描述 例子 运算结果
+ 一元加法 +‘123’ 123
- 一元减法 -‘123’ -123
++ 递增 ++2,2++ 3
递减 –2,2– 1

一元加法与一元减法

一元加减看似是加减法实际上是用来转换类型设计的,功能类似于Number(),都是由toNumber实现的。本来这两个运算符理解上并不难,难的是各个类型的转换得出来的值不容易想。一元加减结果是一样的除了符号因此我只打上一个符号的代码了

let s1 = "01"; 
let s2 = "1.1"; 
let s3 = "z"; 
let b = false; 
let f = 1.1; 
let o = {
    
 valueOf() {
    
 return -1; 
 } 
}; 
s1 = +s1; // 值变成数值 1 
s2 = +s2; // 值变成数值 1.1 
s3 = +s3; // 值变成 NaN 
b = +b; // 值变成数值 0 
f = +f; // 不变,还是 1.1 
o = +o; // 值变成数值-1 

递增与递减运算符

递增和递减操作符直接照搬自 C 语言,但有两个版本:前缀版和后缀版。顾名思义,前缀版就是位于要操作的变量前头,后缀版就是位于要操作的变量后头。前缀递增操作符会给数值加 1,把两个加号(++)放到变量前头即可:

主要用在for循环等需要递增的地方,相当于 sum+=1 也就是sum=sum+1;这里注意 二者还是有细小的区别的:


let a=0;
let b=a++;  //后置++先赋值再++
let c=++b;  //前置++先++再赋值 
console.log(a,b,c); //b=1 c=1 a=1

//sum+=1 和 a++ 的区别 注意数据类型
let sum=0
sum+=1;
console.log(sum);//sum=1
let sum2='0'
sum2+=1;
sum2+=1;
console.log(sum2);//sum='011' //sum=sum+1 此时sum是 '1' 

let a=0
a+=1;
console.log(a);//sum=1
let a2='0';
a2++;
a2++;
console.log(a2);//sum=2  // a2++ 可以将a2的 '0' 字符串转换为 0 

位操作符

每个数字,字符等都会在底层转换为2进制的0,1串。在这里主要说操作数的是数字,例如2 转换为二进制就是 10 那么在32位系统上存在的形式就是 00000000 00000000 00000000 00000010 。每一位的可能取值都是0或1 因此位操作符就是针对于每一位进行的操作。在计算机中的存储与计算比我们想象的还要复杂,例如负数的存储 首位代表了符号位,然而计算机又是按补码存储的因此位运算符的存在是很有意义的。所有的底层运算都是基于简单的加法构成的,这里我就不细说了具体想研究的伙伴可以去看 计组相信会豁然开朗。

按位与

按位与操作符用和号(&)表示,有两个操作数。本质上,按位与就是将两个数的每一个位对齐,然后基于真值表中的规则,对每一位执行相应的与操作。

在这里插入图片描述

总结来说有假为假 全真为真这个和逻辑与的运算思想一样,不过这是每一位进行运算。

在这里插入图片描述

按位或

按位或操作符用管道符(|)表示,同样有两个操作数。按位或遵循如下真值表:

在这里插入图片描述

总结来说有真或为真 全假为假这个和逻辑或的运算思想一样,不过这是每一位进行运算。

按位非

按位非操作符用波浪符(~)表示,它的作用是返回数值的一补数。按位非是 ECMAScript 中为数不多的几个二进制数学操作符之一。看下面的例子:

不多的几个二进制数学操作符之一。看下面的例子:
let num1 = 25; // 二进制 00000000000000000000000000011001 
let num2 = ~num1; // 二进制 11111111111111111111111111100110 
console.log(num2); // -26 

总结来说全部取反即可

按位异或

按位异或用脱字符(^)表示,同样有两个操作数。下面是按位异或的真值表:

在这里插入图片描述

异或总结就是相同为0 不同为 1

左/右移运算符

左移

这两个运算符也是相对于位来操作的,左移操作符用两个小于号(<<)表示,会按照指定的位数将数值的所有位向左移动。比如,如果数值 2(二进制 10)向左移 5 位,就会得到 64(二进制 1000000),如下所示:左移不用考虑符号直接将空位填充0

let oldValue = 2; // 等于二进制 10 
let newValue = oldValue << 5; // 等于二进制 1000000,即十进制 64 

在这里插入图片描述

有符号右移

有符号右移由两个大于号(>>)表示,会将数值的所有 32 位都向右移,同时保留符号(正或负)。有符号右移实际上是左移的逆运算。比如,如果将 64 右移 5 位,那就是 2:符号位不变,空位按照符号位填充

let oldValue = 64; // 等于二进制 1000000 
let newValue = oldValue >> 5; // 等于二进制 10,即十进制 2 

在这里插入图片描述

无符号右移

无符号右移用 3 个大于号表示(>>>),会将数值的所有 32 位都向右移。对于正数,无符号右移与有符号右移结果相同。仍然以前面有符号右移的例子为例,64 向右移动 5 位,会变成 2:不管征服全部填充0

let oldValue = 64; // 等于二进制 1000000 
let newValue = oldValue >>> 5; // 等于二进制 10,即十进制 2 

对于负数,有时候差异会非常大。与有符号右移不同,无符号右移会给空位补 0,而不管符号位是什么。对正数来说,这跟有符号右移效果相同。但对负数来说,结果就差太多了。

let oldValue = -64; // 等于二进制 11111111111111111111111111000000 
let newValue = oldValue >>> 5; // 等于十进制 134217726 

算术运算符

这里的算数运算符主要包含以下几种 加,减,乘,除,取模,以及指数这类,运算不算难和我们学过的差不多,这些运算符主要运用到做算法题这种地方,比较难的一点还是对于不同类型的处理,书上的例子比较多 我这里就贴出一部分吧有想仔细研究的朋友可以看看红宝书

在这里插入图片描述

  • 如果两个操作数都是数值,加法操作符执行加法运算并根据如下规则返回结果:

  • 如果有任一操作数是 NaN,则返回 NaN;

  • 如果是 Infinity 加 Infinity,则返回 Infinity;

  • 如果是-Infinity 加-Infinity,则返回-Infinity;

  • 如果是 Infinity 加-Infinity,则返回 NaN;

  • 如果是+0 加+0,则返回+0;

  • 如果是-0 加+0,则返回+0;

  • 如果是-0 加-0,则返回-0。

  • 不过,如果有一个操作数是字符串,则要应用如下规则:

  • 如果两个操作数都是字符串,则将第二个字符串拼接到第一个字符串后面;

  • 如果只有一个操作数是字符串,则将另一个操作数转换为字符串,再将两个字符串拼接在一起。

  • 如果有任一操作数是对象、数值或布尔值,则调用它们的 toString()方法以获取字符串,然后再

  • 应用前面的关于字符串的规则。对于 undefined 和 null,则调用 String()函数,分别获取"undefined"和"null"。

    🔥这里有一道坑,就是 字符串+数字+数字 的结果 例如下述

    let num1 = 5; 
    let num2 = 10; 
    let message = "The sum of 5 and 10 is " + num1 + num2; 
    console.log(message); // "The sum of 5 and 10 is 510" 
    

    正常按照我们的想法应该是 The sum of 5 and 10 is 15 但请注意 前面是字符串 与nums1相加时 结果变为了 The sum of 5 and 10 is 5

    此时还是字符串 再与num2 相加 结果为 The sum of 5 and 10 is 510 ,其实有其他语言如java的朋友应该能理解,字符串相加一次后还是字符串, +具有连接作用了。此时可以将 num1+num2用括号 括起来就可以了 "The sum of 5 and 10 is " + (num1 + num2);

    结果就为:The sum of 5 and 10 is 15

关系运算符

关系运算符应该是从我们小学就开始学的了,和我们所认知的一样算法。关系运算符常常用来在循环语句中进行判断 正确返回true 错误返回 false

运算符 描述 比较 返回值
> 大于 x>8 false
< 小于 x<8 true
>= 大于或等于 x>=8 false
<= 小于或等于 x<=8 true

在这里插入图片描述

都是数值的就不多赘述了 接下来列出非数值的一些比较规律:

  • 如果操作数都是数值,则执行数值比较。
  • 如果操作数都是字符串,则逐个比较字符串中对应字符的编码。
  • 如果有任一操作数是数值,则将另一个操作数转换为数值,执行数值比较。
  • 如果有任一操作数是对象,则调用其 valueOf()方法,取得结果后再根据前面的规则执行比较。
  • 如果没有 valueOf()操作符,则调用 toString()方法,取得结果后再根据前面的规则执行比较。
  • 如果有任一操作数是布尔值,则将其转换为数值再执行比较。
let result = "Brick" < "alphabet"; // true 
let result = "Brick".toLowerCase() < "alphabet".toLowerCase(); // false 
let result = "23" < "3"; // true 
let result = "23" < 3; // false 
let result = "a" < 3; // 因为"a"会转换为 NaN,所以结果是 false 
let result1 = NaN < 3; // false 
let result2 = NaN >= 3; // false 

相等和全等和不等

ECMAScript 中的等于操作符用两个等于号(==)表示,如果操作数相等,则会返回 true。不等于操作符用叹号和等于号(!=)表示,如果两个操作数不相等,则会返回 true。这两个操作符都会先进行类型转换(通常称为强制类型转换)再确定操作数是否相等。

在转换操作数的类型时,相等和不相等操作符遵循如下规则。

  • ​ 如果任一操作数是布尔值,则将其转换为数值再比较是否相等。false 转换为 0,true 转换为 1。
  • ​ 如果一个操作数是字符串,另一个操作数是数值,则尝试将字符串转换为数值,再比较是否相等。
  • ​ 如果一个操作数是对象,另一个操作数不是,则调用对象的 valueOf()方法取得其原始值,再根据前面的规则进行比较。在进行比较时,这两个操作符会遵循如下规则。
  • null 和 undefined 相等。但不全等!!!
  • ​ null 和 undefined 不能转换为其他类型的值再进行比较。
  • ​ 如果有任一操作数是 NaN,则相等操作符返回 false,不相等操作符返回 true。记住:即使两个操作数都是 NaN,相等操作符也返回 false,因为按照规则,NaN 不等于 NaN。
  • 如果两个操作数都是对象,则比较它们是不是同一个对象。如果两个操作数都指向同一个对象,则相等操作符返回 true。否则,两者不相等。

下表总结了一些特殊情况及比较的结果。有一些容易错的我标出来了 其他的基本根据强制类型转换可以做;

在这里插入图片描述

赋值运算符

简单赋值用等于号(=)表示,将右手边的值赋给左手边的变量这里主要与 == 区分好就可以 有时经常在做循环语句的时候会填错。而且有时还不报错。复合赋值使用乘性、加性或位操作符后跟等于号(=)表示。这些赋值操作符是类似如下常见赋值操作的简写形式:

var age = 10;
age += 5;  // 相当于 age = age + 5;
age -= 5;  // 相当于 age = age - 5;
age *= 10; // 相当于 age = age * 10;
age /= 10; // 相当于 age = age /10;
age %= 10; // 相当于 age = age % 10;
  • 乘后赋值(*=)

  • 除后赋值(/=)

  • 取模后赋值(%=)

  • 加后赋值(+=)

  • 减后赋值(-=)

  • 左移后赋值(<<=)

  • 右移后赋值(>>=)

  • 无符号右移后赋值(>>>=)

  • 这些操作符仅仅是简写语法,使用它们不会提升性能。

    逗号操作符

    在一条语句中同时声明多个变量是逗号操作符最常用的场景。不过,也可以使用逗号操作符来辅助赋值。在赋值时使用逗号操作符分隔值,最终会**返回表达式中最后一个值**:

    let num = (5, 1, 4, 8, 0); // num 的值为 0 
    

逻辑运算符

逻辑非操作符 !也可以用于把任意值转换为布尔值。同时使用两个叹号(!!),相当于调用了转型函数 Boolean()。无论操作数是什么类型,第一个叹号总会返回布尔值。第二个叹号对该布尔值取反,从而给出变量真正对应的布尔值。结果与对同一个值使用 Boolean()函数是一样的:

  • 如果操作数是对象,则返回 false。
  • 如果操作数是空字符串,则返回 true。
  • 如果操作数是非空字符串,则返回 false。
  • 如果操作数是数值 0,则返回 true。
  • 如果操作数是非 0 数值(包括 Infinity),则返回 false。
  • 如果操作数是 null,则返回 true。
  • 如果操作数是 NaN,则返回 true。
  • 如果操作数是 undefined,则返回 true。

以下示例验证了上述行为:

console.log(!false); // true 
console.log(!"blue"); // false 
console.log(!0); // true 
console.log(!NaN); // true 
console.log(!""); // true 
console.log(!12345); // false 

console.log(!!"blue"); // true 
console.log(!!0); // false 
console.log(!!NaN); // false 
console.log(!!""); // false 
console.log(!!12345); // true 

逻辑与操作符 &&(短路与)

如果两端是布尔值则 全真为真 有假为假 但如果两端不是布尔时:

  • 如果第一个操作数是对象,则返回第二个操作数。

  • 如果第二个操作数是对象,则只有第一个操作数求值为 true 才会返回该对象。

  • 如果两个操作数都是对象,则返回第二个操作数。

  • 如果有一个操作数是 null,则返回 null。

  • 如果有一个操作数是 NaN,则返回 NaN。

  • 如果有一个操作数是 undefined,则返回 undefined。

    短路原理 第一个为真返回第二个操作数(理解为通路),第一个为假则返回 第二个(短路了)

    console.log( 123 && 456 );        // 456
    console.log( 0 && 456 );          // 0
    console.log( 123 && 456&& 789 );  // 789
    

逻辑或操作符 ||(短路或)

如果两端是布尔值则 全假为假 有真为真 但如果两端不是布尔时:

  • 如果第一个操作数是对象,则返回第一个操作数。

  • 如果第一个操作数求值为 false,则返回第二个操作数。

  • 如果两个操作数都是对象,则返回第一个操作数。

  • 如果两个操作数都是 null,则返回 null。

  • 如果两个操作数都是 NaN,则返回 NaN。

  • 如果两个操作数都是 undefined,则返回 undefined

    console.log( 123 || 456 );         //  123
    console.log( 0 ||  456 );          //  456
    console.log( 123 || 456 || 789 );  //  123
    

    短路原理 第一个为真返回第一个操作数(理解为短路了),第一个为假则返回 第二个(通路了)

三元表达式

表达式1 ? 表达式2 : 表达式3;  //与if else 逻辑类似

说明:如果表达式1为true ,则整个表达式的结果就是表达式2的值,如果表达式false,则整个表达式的结果就是表达式3的值.

let a=2;
let b=3;
a>b?a:b;//3   a大于b么 大于则结果是a  不大于结果是b

⭐运算符的优先级

下面的表格将所有运算符按照优先级的不同从高(19)到低(1)排列。

优先级 运算符类型 结合性 运算符
19 分组 n/a(不相关) ( … )
18 成员访问 从左到右 … . …
需计算的成员访问 从左到右 … [ … ]
new(带参数列表) n/a new … ( … )
函数调用 从左到右 … ( … )
可选链(Optional chaining) 从左到右 ?.
17 new(无参数列表) 从右到左 new …
16 后置递增 n/a … ++
后置递减 … –
15 逻辑非 (!) 从右到左 ! …
按位非 (~) ~ …
一元加法 (+) + …
一元减法 (-) - …
前置递增 ++ …
前置递减 – …
typeof typeof …
void void …
delete delete …
await await …
14 幂 (**) 从右到左 … ** …
13 乘法 (*) 从左到右 … * …
除法 (/) … / …
取余 (%) … % …
12 加法 (+) 从左到右 … + …
减法 (-) … - …
11 按位左移 (<<) 从左到右 … << …
按位右移 (>>) … >> …
无符号右移 (>>>) … >>> …
10 小于 (<) 从左到右 … < …
小于等于 (<=) … <= …
大于 (>) … > …
大于等于 (>=) … >= …
in … in …
instanceof … instanceof …
9 相等 (==) 从左到右 … == …
不相等 (!=) … != …
一致/严格相等 (===) … === …
不一致/严格不相等 (!==) … !== …
8 按位与 (&) 从左到右 … & …
7 按位异或 (^) 从左到右 … ^ …
6 按位或 (|) 从左到右 … | …
5 逻辑与 (&&) 从左到右 … && …
4 逻辑或 (||) 从左到右 … || …
空值合并 (??) 从左到右 … ?? …
3 条件(三元)运算符 从右到左 … ? … : …
2 赋值 从右到左 … = …
… += …
… -= …
… **= …
… *= …
… /= …
… %= …
… <<= …
… >>= …
… >>>= …
… &= …
… ^= …
… |= …
… &&= …
… ||= …
… ??= …
1 逗号 / 序列 从左到右 … , …

✔️流程控制

ECMA-262 描述了一些语句(也称为流控制语句),而 ECMAScript 中的大部分语法都体现在语句中。语句通常使用一或多个关键字完成既定的任务。语句可以简单,也可以复杂。简单的如告诉函数退出,复杂的如列出一堆要重复执行的指令。流程控制语句包含循环,顺序,分支等语句这是各个语言的基础,其语法结构基本相同如果有其他语言基础这部分甚至可以直接略过。

在这里插入图片描述

if…else…

if 语句是使用最频繁的语句之一,语法如下:if (condition) statement1 else statement2 这里的条件(condition)可以是任何表达式,并且求值结果不一定是布尔值。ECMAScript 会自动调用 Boolean()函数将这个表达式的值转换为布尔值。如果条件求值为 true,则执行语句statement1;如果条件求值为 false,则执行语句 statement2。这里的语句可能是一行代码,也可能是一个代码块(即包含在一对花括号中的多行代码)。来看下面的例子:(if语句可以加else 如果不需要也可不加 )

if (i > 25) 
 console.log("Greater than 25."); // 只有一行代码的语句
else {
    
 console.log("Less than or equal to 25."); // 一个语句块
} 

//if 语句
if (i > 25) 
 console.log("Greater than 25."); // 只有一行代码的语句 {} 这时可加可不加 但 ;一定要加

多分支:else if 的设计可以进行多种判断,不仅仅局限于一次判断。普通的单分支判断有时可以用到三元表达式进行代替。当我们需要进行多次判断时那么可以使用如下语法:这里值得注意的是()内不一定要填布尔值的结果,如果是其他值js会调用Boolean()将其转换为布尔值

if (i > 25) {
    
 console.log("Greater than 25."); 
} else if (i < 0) {
    
 console.log("Less than 0."); 
} else {
    
 console.log("Between 0 and 25, inclusive."); 
} 

当语句只有一条时也可以省略但后面不要忘了 ; return 在循环中不仅仅表示返回值 更重要的是表示**结束循环** 等同于 break

if (i > 25) return ; //这里return直接结束循环执行 后面的语句 

for

for循环和wile循环都是循环语句,简单来说我需要求1-100以内的加减法,阶乘等都离不开循环语句在日常使用中或是刷算法题的过程中没有不需要这俩个的。因此for循环的作用是不言而喻的。

var sum=0;  
for (var i = 0 ; i<10 ; i++) {
     //先执行 i=0 再执行 i<10 再执行 sum+=i  最后 i++ 
    sum+=i;  //45
}
console.log(sum);

在 for 循环的初始化代码中,其实是可以不使用变量声明关键字的。不过,初始化定义的迭代器变量在循环执行完成后几乎不可能再用到了。因此,最清晰的写法是使用 let 声明迭代器变量,这样就可以将这个变量的作用域限定在循环中。初始化、条件表达式和循环后表达式都不是必需的。因此,下面这种写法可以创建一个无穷循环:还是不建议这样搞得,当然除了循环结束也可以人为终止循环,就是break和continue

var sum=0;  
for (;;) {
    
    sum+=1;  //45
    console.log(sum);
}

while

while语句写法和for完全不一样但用处是一样的都是为了循环,无法通过 while 循环实现的逻辑,同样也无法使用 for 循环实现。因此 for 循环只是将循环相关的代码封装在了一起而已。while的语法如下所示相比较for循环看着更简单,也是可以通过break和continue中断。

while (条件表达式) {
   
    // 循环体代码 
}
//实例
while(i<10){
   
    sum+=i; 
     i++;
}
console.log(sum); //45

执行思路:

  • 1 先执行条件表达式,如果结果为 true,则执行循环体代码;如果为 false,则退出循环,执行后面代码
  • 2 执行循环体代码
  • 3 循环体代码执行完毕后,程序会继续判断执行条件表达式,如条件仍为true,则会继续执行循环体,直到循环条件为 false 时,整个循环过程才会结束

注意:

  • 使用 while 循环时一定要注意,它必须要有退出条件,否则会成为死循环

do…while

do-while 语句是一种后测试循环语句,它和while的区别在于,do…while第一次会直接进入循环,而while每次进入循环必须先判断。即循环体中的代码执行后才会对退出条件进行求值。换句话说,循环体内的代码至少执行一次。do-while 的语法如下:

do {
   
    // 循环体代码 - 条件表达式为 true 时重复执行循环体代码
} while(条件表达式);
/
let i = 0; 
do {
    
 i += 2; 
} while (i < 10); 

switch

switch 语句属于多分支语句,是与 if 语句紧密相关的一种流控制语句,从其他语言借鉴而来。ECMAScript中 switch语句跟 C 语言中 switch 语句的语法非常相似,如下所示:

switch( 表达式 ){
    
    case value1:
        // 表达式 等于 value1 时要执行的代码
        break;
    case value2:
        // 表达式 等于 value2 时要执行的代码
        break;
    default:
        // 表达式 不等于任何一个 value 时要执行的代码
}
    • switch :开关 转换 , case :小例子 选项

    • 关键字 switch 后面括号内可以是表达式或值, 通常是一个变量

    • 关键字 case , 后跟一个选项的表达式或值,后面跟一个冒号

    • switch 表达式的值会与结构中的 case 的值做比较

    • 如果存在匹配全等(===) ,则与该 case 关联的代码块会被执行,并在遇到 break 时停止,整个 switch 语句代码执行结束

    • 如果所有的 case 的值都和表达式的值不匹配,则执行 default 里的代码

      注意: 执行case 里面的语句时,如果没有break,则继续执行下一个case里面的语句。

  • switch 语句和 if else if 语句的区别

    • 一般情况下,它们两个语句可以相互替换
    • switch…case 语句通常处理 case为比较确定值的情况, 而 if…else…语句更加灵活,常用于范围判断(大于、等于某个范围)
    • switch 语句进行条件判断后直接执行到程序的条件语句,效率更高。而if…else 语句有几种条件,就得判断多少次。
    • 当分支比较少时,if… else语句的执行效率比 switch语句高。
    • 当分支比较多时,switch语句的执行效率比较高,而且结构更清晰。

总结:在需要条件判断时可以使用if 语句,需要循环时可以使用for while等 需要进行分支选择如 菜单选项 选择0-5项其中一个,进入下一个菜单可以使用switch。这些语句除了自己用以外还可以嵌套使用。

for(let i=0;i<10;i++){
   

    if(i<5){
   
        console.log('for');
    }    else{
   
        console.log('if');
    }
}

break 和 continue 语句

那么之前有提到过break和continue,break 和 continue 语句为执行循环代码提供了更严格的控制手段。其中,break 语句用于立即退出循环,强制执行循环后的下一条语句。而 continue 语句也用于立即退出循环,但会再次从循环顶部开始执行。下面看一个例子:区别就在于 跳出本次循环和直接结束

for (var i = 1; i <= 5; i++) {
   
    if (i == 3) {
   
        console.log('退出');
        continue; // 跳出本次循环,跳出的是第3次循环 
     }
     console.log('第' + i + '次循环');
}

在这里插入图片描述

for (var i = 1; i <= 5; i++) {
   
    if (i == 3) {
   
        break; // 直接退出整个for 循环,跳到整个for下面的语句
    }
    console.log('第' + i + '次循环');
  }

在这里插入图片描述

⭕ for-in和 for…of

这两个也是循环但和之前的循环完全不同,确切地说这两个属于是迭代遍历。之前的循环主要针对于数字型,数组等进行循环。而这两个的针对对象是 Object,数组,Set,Map等引用类型。二者的区别在与 for…in 是迭代 对象这种不可迭代没有顺序的,而for…of针对的是类似数组这种可迭代对象 也就是有顺序的。当然这是比较浅显的区别,更多的区别我在下面列举:这里不深入探讨留在中篇再说这里需要迭代,对象,Map,Set 相关的知识。

for…of是ES9新增的语法主要针对数组,Map这类属性含有Symbol.iterator属性的对象。 for…in是ES3的语法主要针对的是内部没有顺序的。

for … of遍历获取的是对象的键值,for … in 获取的是对象的键名

for … in会遍历对象的整个原型链性能非常差不推荐使用,而for … of只遍历当前对象不会遍历原型链

for of 会遍历拥有迭代器对象的集合,调用 Symbol.iterator 生成一个迭代器对象。

var myArray=[1,2,4,5,6,7];
    for (var value of myArray) {
   
      console.log(value); //1234567
    }
 
var myObject={
   name:'lee'};
for (var keys in myObject) {
   
  console.log(keys); //name
  console.log(myObject); //{name: 'lee'}
  console.log(myObject[keys]); //lee
}

✔️数组基础

除了 Object,Array 应该就是 ECMAScript 中最常用的类型了。ECMAScript 数组跟其他编程语言的数组有很大区别。跟其他语言中的数组一样,ECMAScript 数组也是一组有序的数据,但跟其他语言不同的是,数组中每个槽位可以存储任意类型的数据。这意味着可以创建一个数组,它的第一个元素是字符串,第二个元素是数值,第三个是对象。ECMAScript 数组也是动态大小的,会随着数据添加而自动增长。

总结以下:数组是存储数据的集合,在内存空间是连续的,它与string类型一样同样具有length属性可以表示长度,因此获取数组的长度可以使用 arr.length,一般在说到这种存储类型时通常会从物理存储结构和逻辑结构两方面来考虑,如下图所示:

在这里插入图片描述

下面是我画的一些结构图方便区分,在我们编写程序时只会一种数据结构是完全不行的,越是复杂的数据结构用处也越为广泛。

在这里插入图片描述

数组的获取

数组的索引是从0开始计数的,因此数组最后一位是 arr.length-1.我们可以通过 和字符串相同的办法获取到数组的某一位数值甚至可以将它修改掉。通过中括号的方式不仅仅可以查询更可以修改数组中的元素,如下图所示:

在这里插入图片描述

var arr=[1,2,3,4,5,6];  
arr[3]=5;   //数组名[索引]=值
console.log(arr);// [1,2,3,5,5,6]

在这里插入图片描述

数组的长度

之前有提到过数组和字符串同样具有length属性,因此可以获取数组的长度。索引 (下标) :用来访问数组元素的序号。如图所示,arr.length的长度比最后一位索引总大1,这个通常在for循环中遍历数组会用到。
在这里插入图片描述

数组的创建

数组的创建方式有两种和其他数据类型相似,有构造函数和字面量两种:

1、字面量创建:(在使用数组字面量表示法创建数组不会调用 Array 构造函数。) 但它的原型链上依旧有构造函数的原型对象即可以继承Array的方法。

在js当中无论是数组还是变量都类似一种容器可以装任意的数据类型这与其他语言是不同的,例如在c或java里数组所有内容都必须是一种类型 int [3]=[1,2,3,],但在js中可以存放任意类型如下图所示,

var  数组名 = ['内容','内容','内容','内容'];  
var arr=[]; 
var arrStus = ['小白',12,true,28.9]; //数组内的类型不一定要相同!!!

2、构造函数创建

与其他引用类型相似,可以使用new关键字来创建数组:

var arr = new Array();   // 创建一个新的空数组
arr=[1,2,3,4]
var arr = new Array([1,2,3,4]);   // 创建一个带值的数组 
var arr=new Array(1,2,3,4);
var arr=new Array(3);  //创建一个长度为3的空数组

二者的区别如图所示,字面量创建是将内容创建在栈区通过arr1变量把它存储了,而new出来的是在堆区通过arr2变量指向堆区的内容,实际上arr2是一个指针也就是内存地址。虽然与字符串创建时没什么区别,但在修改,增加时却大大不同。若想将12345的2改成8 数组只需要在原数中修改即可,但字符串需要将原字符串拷贝一份再修改。还是那句话字符串不能修改,我们看见的是修改后的新字符串。 (图中只是将堆栈画了出来实际上,还有代码区,全局区等不过js里也不需要了解那么多,毕竟也不用修改指针等操作还是比较方便的)。

在这里插入图片描述

⭕两个特殊的创建数组的方法:

Array.from() 和Array.of()

Array.from()的第一个参数是一个类数组对象,即任何可迭代的结构,或者有一个 length 属性和可索引元素的结构。这种方式可用于很多场合:(类数组对象包含:arguments对象,字符串)

// 类数组对象

// 1、argumments对象:arguments对象用于保存数组中的实参,可以直接通过arguments[0]访问函数的第一个实参

// 2、document.getElementsByTagName():返回一个类数组对象

// 3、字符串String:一个可读的类数组对象,可以通过str[0]获取第一个字符,str.length获取字符串的长度

// 字符串会被拆分为单字符数组
console.log(Array.from("Matt")); // ["M", "a", "t", "t"] 
// 可以使用 from()将集合和映射转换为一个新数组
const m = new Map().set(1, 2
  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序lee

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值