JavaScript基础笔记

前言

在JavaScript诞生的前几年,有人说:

JavaScript是一门四不像的语言;JavaScript是一门没有规范的语言;JavaScript是一门兼容糟糕的语言;JavaScript是一门不精准的语言;JavaScript是一个半成品语言;JavaScript是一门糟糕的语言;JavaScript只是一个玩具胶水语言;

这些声音从JavaScript面世之初就一直伴随着她,声音的主人不乏已掌握多门语言的coding老兵,每一条负面都事实支撑。就连JavaScript之父也曾经说过:

"与其说我爱Javascript,不如说我恨它。它是C语言和Self语言一夜情的产物。十八世纪英国文学家约翰逊博士说得好:'它的优秀之处并非原创,它的原创之处并不优秀。'(the part that is good is not original, and the part that is original is not good.)"

Ruby的设计者——松本行弘为此感叹:

“这样的出身,得到这样的成功,还真让人出乎意料,……”,“但由于开发周期短,确实也存在着不足……”。

Douglas Crockford写了一本《JavaScript:The Good Parts》,在书中他这样写到:

JavaScript建立在一些非常好的想法和少数非常坏的想法之上。

那些非常好的想法包括函数、弱类型、动态对象和一个富有表现力的对象字面量表示法,而那些坏的想法包括基于全局变量的编程模型、缺乏块作用域、“保留”了一堆根本没用到的保留字,不支持真正的数组(它所提供的类数组对象性能不好)等等。

还有一些是“鸡肋”,比如with语句,原始类型的包装对象,new,void等等

但如今,JavaScript已经成为大部分全球开发者与编程爱好者最常用/最喜欢的语言之一。

在这里插入图片描述
[github Octoverse 调查报告][ https://octoverse.github.com/]合作开发中应用最多的语言排行
在这里插入图片描述

stackoverflow 2019年度调查报告最受欢迎的语言排行

这中间到底发生了什么,为什么会导致如此大的变化,让我们一起与JavaScript相关的历史年表;

时间 事件
1990年 万维网诞生
1992年 第一个浏览器诞生
1994年 Netscape(网景)成立开发第一代Netscape Navigator浏览器
1995年 Mocha诞生,之后改为LiveScript,最后与sun公司达成协议改为javascript
1996年 微软开发JScript,Netscape公司将JavaScript提交给国际标准化组织ECMA
1997年 ECMAscript1.0版发布 JavaScript进入标准化时代 ECMA-262发布
1998年 ECMAScript 2.0版发布。
1999年 ECMAScript 3.0版发布,成为JavaScript的通行标准,得到了广泛支持。
2004年 Gmail发布 Dojo诞生
2005年 Ajax 即“ Asynchronous Javascript And XML” AJAX 推广 CouchDB基于json格式的数据库
2006年 XMLHttpRequest被w3c纳入正式标准 同年 jQuery发布
2008年 V8引擎发布
2009年 ECMAScript 5.0发布 & node.js诞生
2010年 Express 发布 & angular发布 NPM、BackboneJS和RequireJS 诞生
2011年 React原型成立
2012年 Webpack诞生
2013年 mongodb 2.4* 开始支持JavaScript ELECTRON诞生 HTML5.1 发布
2014年 Vue.js 发布
2015年 ECMAScript 6正式发布,并且更名为 ECMAScript 2015 ,iotjs 发布
2016年 ECMAScript 2016发布
2017 ECMAScript 2017发布 主流浏览器全面支持 WebAssembly
个人观点: 真香
JavaScript的起步非常糟糕, 有着这样那样的问题让人诟病 , 但是他的灵魂足够有趣; 让人们乐于为他添砖加瓦,修枝剪叶,每一步都恰巧踩在了时代的脉搏上; The lucky JavaScript  

JavaScript 的基本语法和对象体系,是模仿 Java 而设计的。但是,JavaScript 没有采用 Java 的静态类型。正是因为 JavaScript 与 Java 有很大的相似性,所以这门语言才从一开始的 LiveScript 改名为 JavaScript。基本上,JavaScript 这个名字的原意是“很像Java的脚本语言”。

JavaScript 语言的函数是一种独立的数据类型,以及采用基于原型对象(prototype)的继承链。这是它与 Java 语法最大的两点区别。JavaScript 语法要比 Java 自由得多。

另外,Java 语言需要编译,而 JavaScript 语言则是运行时由解释器直接执行。

总之,JavaScript 的原始设计目标是一种小型的、简单的动态语言,与 Java 有足够的相似性,使得使用者(尤其是 Java 程序员)可以快速上手。

什么是JavaScript?

JavaScript 是一门弱类型的动态脚本语言,支持多种编程范式,包括面向对象和函数式编程,被广泛用于 Web 开发。

JavaScript是一门基于原型的动态解释性脚本语言

一般来说,前端领域完整的JavaScript包括以下几个部分:

  • ECMAScript,描述了该语言的语法和基本对象
  • 文档对象模型(DOM),描述处理网页内容的方法和接口
  • 浏览器对象模型(BOM),描述与浏览器进行交互的方法和接口

它的基本特点如下:

  • 是一种解释性脚本语言(代码不进行预编译)。
  • 主要用来向HTML页面添加交互行为。
  • 可以直接嵌入HTML页面,但写成单独的js文件有利于结构和行为的分离。

JavaScript常用来完成以下任务:

  • 嵌入动态文本于HTML页面
  • 对浏览器事件作出响应
  • 读写HTML元素
  • 在数据被提交到服务器之前验证数据
  • 检测访客的浏览器信息

BOM - 浏览器对象模型

一套操作浏览器功能的API

通过BOM可以操作浏览器窗口,比如:弹出框、控制浏览器跳转、获取分辨率等

DOM - 文档对象模型

一套操作页面元素的API

DOM可以把HTML看做是文档树,通过DOM提供的API可以对树上的节点进行操作

JavaScript 与 Java 的关系

本质上讲 JavaScript和 Java没有关系,只是JavaScript诞生之初 网景与sun合作想要推出 web端的脚本语言。JavaScript 的基本语法和对象体系,是模仿 Java 而设计的。但是,JavaScript 没有采用 Java 的静态类型。除此之外JavaScript和Java在语言层面上可以说是大相径庭。处于某种商业意图,这门语言才从一开始的LiveScript 改名为 JavaScript。

JavaScript与ECMAScript的关系

ECMAScript 只用来标准化 JavaScript 这种语言的基本语法结构,与部署环境相关的标准都由其他标准规定,比如 DOM 的标准就是由 W3C组织(World Wide Web Consortium)制定的。

ECMAScript 和 JavaScript 的关系是,前者是后者的规格,后者是前者的一种实现。在日常场合,这两个词是可以互换的。

JavaScript能做什么?

任何能够用JavaScript实现的应用系统,最终都必将用JavaScript实现 —Atwood定律

前端领域

ajax出现之后 JavaScript有了在WEB领域立足的土壤和根基,时至今日。JavaScript已经是WEB前端领域最重要的基石,一切现代化的WEB项目都离不开JavaScript。

  • 数据交互
  • UI管理
  • 用户行为交互
  • 数据校验
  • 工程化/模块化
  • MVVM
后端领域

V8 JIT NodeJS 让JavaScript可以在服务端崭露头角 打破了JavaScript只能寄生在浏览器上的魔咒

CouchDB mongodb等基于JSON格式的NoSQL类型的数据库诞生 让JavaScript也可以在DB操作上大展身手

  • WEB服务框架: express/KOA
  • NoSQL 数据库: mongodb CouchDB
  • 博客系统 : Ghost/hexo
  • 自动化构建领域: Gulp/Grunt

APP

目前来说主流的App开发方式有三种:Native App 、Web App、Hybird App,而3种方式下又分别有众多的框架可供选择。JavaScript 可以介入开发的有下面三种:

  • Web App HTML5 APP 框架开发模式
  • Hybrid App 混合模式移动应用
  • PWA Progressive Web App 渐进式网页应用
桌面应用

JavaScript还可以介入的桌面应用开发,主流有 electron Node-webkit hex React Navite

electron代表作:

  • vscode
  • atom

Node-webkit代表作:

  • teambition

hex代表作

  • 有道词典
图形/游戏

世界上最流行的 2D 游戏引擎之一 Cocos2d 和最流行的 3D 游戏引擎之一 Unity3D 均支持 JS 开发游戏。

以及 Cocos2d-js 轻量型跨平台Web游戏引擎

嵌入式与IOT开发

JavaScript 不只是 Web 时代的通用语言,如今还延伸到了使人难以置信的其它地方: 物联网;

JavaScript for Microcontrollers and IoT

为什么要学JavaScript?

JavaScript 的上手方便 表达灵活 社区支持度高 应用广泛,是当今最受欢迎 应用最广泛的语言之一…

现实点说,在现代互联网环境下,你很难找到不需要JavaScript开发岗位的企业。

学习JavaScript所需要的的环境与设备

  • 一台可以运行浏览器的电脑
  • 浏览器
  • 开发工具 (推荐 vscode)

学习JavaScript所需要做的心里准备

不论你之前是否学习接触过其他编程语言,学习JavaScript你需要做好以下几点心里准备

  • 不要用常规认知去理解JavaScript世界的规则 3<2<1 0.2 + 0.7
  • 一切以事实为准,一切认知建立在实践基础上
  • 多练 多练 多练 多练 多练 多练 多练 多练…

JavaScript基础

在HTML中嵌入JavaScript代码

<html>
    <head>
          <script src="js/index.js"></script>
    </head>
    <body>
        <input type="button" value="按钮" onclick="alert('Hello World')" />
        <script>
        	var str = 'hello world!';
            console.log(str);
        </script>
    </body>
</html>

第一段JavaScript代码

var str = 'hello world!';
console.log(str);

JavaScript基础名词概念

var num = 1 + 1;

语句

JavaScript语句为由上至下 单行执行; 每一行都是一条语句, 每一条语句执行完成之后会进入下一行 语句由于 ; 结尾

表达式

上述代码中 1 + 1 就是一个表达式(expression) 指一个为了得到返回值的计算式。

语句可以理解为一条命令,并不一定需要的到一个具体的期望值。表达式的目的是为了得到一个值,方便后面利用值去做什么事情,在上述例子中 表达式 1+1得到的值 赋值 给了 变量 num;

变量

上述例子中 num 为我们 声明变量 , 变量可以理解为一个容器,用于存放各种各样的。由于需要存放各种不同的值 所以我们要为 变量命名 num 就是 上述例子中 的变量名

变量声明

上述例子中 我们通过表达式 1 + 1得到的值, 赋值给了 我们通过 var关键字 声明变量 str ; 后续 num 所指向的内存地址中存储的值就是 1 + 1 的结果。 在这个过程中我们其实做了两件事情:

  1. 我们声明变量 是通过 关键字var 创建了变量 num

  2. 我们将 表达式 1 + 1 的值 赋值给了 变量 num

// 声明变量 变量名为 num 没有赋值 默认值为 undefined undefined也是一个 JavaScript 关键字,表示“无定义”。
var num;
// 这一步 将 1 + 1 的结果赋值给 变量 num 让str所代表的的内存地址所存储的内容为 1 + 1 的值, 后续我们想要使用 1 + 1的结果 可以直接通过 调用变量 num 调用
num = 1 + 1;

变量赋值

我们可以通过 var 关键字 创建一个变量 , 创建出现的变量默认值为 undefined 未定义, 我们可以通过赋值 表达式 = 来给变量存储值 格式如下

变量名称 =;

变量引用

我们学会了如何创建变量 以及如何给变量赋值, 那赋值后的变量我们如何使用呢?

//使用console.log方法 在控制台中 打印 num变量的值
console.log(num);
//将变量 num 带入表达式 num + 1 让表达式成为为 num变量的内容 + 1 并且赋值给 变量 count
var count = num + 1;

标识符 ( 变量名称 )

标识符(identifier)指的是用来识别各种值的合法名称。最常见的标识符就是变量名,以及后面要提到的函数名。JavaScript 语言的标识符对大小写敏感,所以aA是两个不同的标识符。

命名规则

必须遵守的命名规则 如果不遵守 就会报错

- 由字母、数字、下划线、$符号组成,不能以数字以及其他符号开头
- 不能是保留关键字,例如:for、while。

保留关键字:

arguments、break、case、catch、class、const、continue、debugger、default、delete、do、else、enum、eval、export、extends、false、finally、for、function、if、implements、import、in、instanceof、interface、let、new、null、package、private、protected、public、return、static、super、switch、this、throw、true、try、typeof、var、void、while、with、yield。

*合法标识符 中文为合法标识符任意 Unicode 字母(包括英文字母和其他语言的字母)

num
$con
_target
π
计数器 

*非法标识符

1x
321
***
-x
undefined
for
命名规范

遵守规范能够给自己和他人带来更好的开发体验,不遵守并不会导致报错

  • 具有语义性的 英文单词 ;
  • 多个单词使用驼峰命名法
  • 变量名称 为名词 可以使用形容词为前缀

*良好的命名

maxCount
petName
str
num

*不好的命名

max-count
maxcount
getName

变量规则

未声明变量直接使用

console.log(x); 
// ReferenceError: x is not defined

上面代码直接使用变量x,系统就报错,告诉你变量x没有声明。

省略 var 关键字

a = 30;
console.log(a); //30

在javascript中 变量可以省略 var关键字 直接调用或者赋值,解释器会帮我们 隐式声明 变量

但是,不写var的做法,不利于表达意图,而且容易不知不觉地创建全局变量,所以建议总是使用var命令声明变量。

重复赋值

var x = 10;
x = 20;
console.log(x); //20

解释为 

var x;
x = 10;
x = 20;
console.log(x);

x 一开始声明并且赋值为10 后面如果想要修改x的值 不需要重新声明 直接再次赋值20 覆盖之前x的值内容即可

重复声明

var x = 1;
var x;
console.log(x); //1

解释为 

var x; 
x = 1;
console.log(x);

对同一个变量进行二次声明 第二次声明是无效的操作 因为同一个 环境中 变量名是唯一的;

重复声明赋值

var x = 1;
var x = 2;
console.log(x); //2

解释为

var x;
x = 1;
x = 2;
console.log(x);

结合上一个重复声明, 当重复声明且赋值的时候, 第二行的声明无效 但 赋值操作有效 所以 变量 x 的值 由1 覆盖为 2

批量声明

var a,b,c,d = 10;

解释为

var a;
var b;
var c;
var d;
d = 10;

在上面的代码中 我们可以通过 , 隔开多个变量, 通过一个 var 关键字进行批量声明 , 最后一个 变量 d 赋值为10;

变量提升

console.log(num); //undefined
var num = 10;

上面的这个代码中 我们书写语句的顺序是

  1. 调用了 num 进行打印
  2. 声明了变量 num 并且 赋值 10

实际在javascript引擎解释后 顺序为

var num;
console.log(num); //undefined
num = 10;
  1. 先声明 num 这一步称为 变量提升
  2. 调用console.log() 打印 num的值 这时因为没有给num赋值 num的值还是 初始默认值 undefined
  3. 给num 赋值 为 10

注释

在javascript也会频繁用到注释 注释形式有 两种

// 这是单行注释 ctrl+/

/* 
	这是
	多行
	注释 
	ctrl+shift+/
*/

数据类型

JavaScript 语言的每一个值,都属于某一种数据类型。JavaScript 的数据类型,共有七种。

  • 数值(number):整数和小数(比如13.14
  • 字符串(string):文本(比如'Hello World')。
  • 布尔值(boolean):表示真伪的两个特殊值,即true(真)和false(假)
  • undefined:表示“未定义”或不存在,即由于目前没有定义,所以此处暂时没有任何值
  • null:表示空值,即此处的值为空 。
  • 对象(object):各种值组成的集合。
  • symbol (symbol ) : 唯一标识符 //es6学习前不做讨论
通常,数值、字符串、布尔值这三种类型,合称为原始类型(primitive type)的值,即它们是最基本的数据类型,不能再细分了。对象则称为合成类型(complex type)的值,因为一个对象往往是多个原始类型的值的合成,可以看作是一个存放各种值的容器。至于undefined和null,一般将它们看成两个特殊值。

类型分类

传统分类通过存储位置 把数据类型分为 基础类型 和 引用类型

基础类型 存储在 栈内存 中
Undefined、Null、Boolean、Number、String和symbol

在这里插入图片描述

Number 类型
  • 数值字面量:数值的固定值的表示法

    110 1024 60.5

  • 进制

十进制
	var num = 9;
	进行算数计算时,八进制和十六进制表示的数值最终都将被转换成十进制数值。
十六进制
	var num = 0xA;
	数字序列范围:0~9以及A~F
八进制
    var num1 = 07;   // 对应十进制的7
    var num2 = 019;  // 对应十进制的19
    var num3 = 08;   // 对应十进制的8
    数字序列范围:0~7
    如果字面值中的数值超出了范围,那么前导零将被忽略,后面的数值将被当作十进制数值解析
  • 浮点数

    • 浮点数的精度问题
浮点数
	var n = 5e-324;   // 科学计数法  5乘以10的-324次方  
浮点数值的最高精度是 17 位小数,但在进行算术计算时其精确度远远不如整数
   var result = 0.1 + 0.2;    // 结果不是 0.3,而是:0.30000000000000004
   console.log(0.07 * 100);
   不要判断两个浮点数是否相等
  • 数值范围
最小值:Number.MIN_VALUE,这个值为: 5e-324
最大值:Number.MAX_VALUE,这个值为: 1.7976931348623157e+308
无穷大:Infinity
无穷小:-Infinity
  • 数值判断

    • NaN:not a number 表示“非数字”(Not a Number),主要出现在将字符串解析成数字出错的场合。
      • NaN 与任何值都不相等,包括他本身
    • isNaN: is not a number
    • NaN进行任何数学运算 结果也是 NaN
数值精度问题

根据国际标准 IEEE 754,JavaScript 浮点数的64个二进制位,从最左边开始,是这样组成的。

  • 第1位:符号位,0表示正数,1表示负数
  • 第2位到第12位(共11位):指数部分
  • 第13位到第64位(共52位):小数部分(即有效数字)

符号位决定了一个数的正负,指数部分决定了数值的大小,小数部分决定了数值的精度。

指数部分一共有11个二进制位,因此大小范围就是0到2047。IEEE 754 规定,如果指数部分的值在0到2047之间(不含两个端点),那么有效数字的第一位默认总是1,不保存在64位浮点数之中。也就是说,有效数字这时总是1.xx...xx的形式,其中xx..xx的部分保存在64位浮点数之中,最长可能为52位。因此,JavaScript 提供的有效数字最长为53个二进制位。

(-1)^符号位 * 1.xx...xx * 2^指数部分

上面公式是正常情况下(指数部分在0到2047之间),一个数在 JavaScript 内部实际的表示形式。

精度最多只能到53个二进制位,这意味着,绝对值小于2的53次方的整数,即-253到253,都可以精确表示。

数值范围

根据标准,64位浮点数的指数部分的长度是11个二进制位,意味着指数部分的最大值是2047(2的11次方减1)。也就是说,64位浮点数的指数部分的值最大为2047,分出一半表示负数,则 JavaScript 能够表示的数值范围为21024到2-1023(开区间),超出这个范围的数无法表示。

如果一个数大于等于2的1024次方,那么就会发生“正向溢出”,即 JavaScript 无法表示这么大的数,这时就会返回Infinity

String类型

‘joker’ “kyogre”

  • 字符串字面量

    ‘海牙老师 真的帅’

  • 转义符

    在这里插入图片描述

  • 字符串长度 (只读)

    length属性用来获取字符串的长度

    var str = '海牙 Hello World';
    console.log(str.length);
    
  • 字符串拼接

    字符串拼接使用 + 连接

    console.log(11 + 11);
    console.log('hello' + ' world');
    console.log('100' + '100');
    console.log('11' + 32);
    console.log('male:' + true);
    
    1. 两边只要有一个是字符串,那么+就是字符串拼接功能
    2. 两边如果都是数字,那么就是算术功能。
  • 字符串换行

    var longString = '第一行 '
      + '第二行 '
      + '第三行 '
      + '文本内容';
      
      
    var longString = '第一行 \
    第二行 \
    第三行 \
    文本内容';
    
    longString
    
  • 按位取值(只读)

    var str = 'hello world!';
    console.log(str[1]); //e
    str[1] = 'x'; //无法改写 
    
Boolean类型

布尔值代表“真”和“假”两个状态。“真”用关键字true表示,“假”用关键字false表示。布尔值只有这两个值。

  • Boolean字面量: true和false,区分大小写
  • 计算机内部存储:true为1,false为0
Undefined和Null

nullundefined都可以表示“没有”,含义非常相似。将一个变量赋值为undefinednull,老实说,语法效果几乎没区别。

  1. undefined表示一个声明了没有赋值的变量,变量只声明的时候值默认是undefined
  2. null表示一个空,变量的值如果想为null,必须手动设置

在javascript设计初期 null就像在 Java 里一样,被当成一个对象。初像 Java 一样,只设置了null表示"无"。根据 C 语言的传统,null可以自动转为0。 但是javascript并没有完整的ERROR机制 null可以转换为0 对于javascript这种弱类型的语言来说 不利于发现bug 所以设计了 undefined 变量默认值也就成为了 undefined

所以在学习javascript的过程中 不能用java等编程语言的null来理解javascript中的null

在javascript中 null是 对象的延伸 是一个 ‘空’ 对象。var str = '';'' 不能用null判断 他是有值的 内存中分配了空间来存储 '' 一个空字符串值。null在javascript中常见于释放内存空间 var str = null;

类型判断

JavaScript 有三种方法,可以确定一个值到底是什么类型。

  • typeof运算符
  • instanceof运算符
  • Object.prototype.toString方法
typeof

typeof 是一个一元运算,放在一个运算数之前,运算数可以是任意类型。它返回值是一个字符串,该字符串说明运算数的类型。typeof 可以用来检测给定变量的数据类型

typeof在判断null、array、object以及函数实例(new + 函数)时,得到的都是object(其实这也是原型继承)。这使得在判断这些数据类型的时候,得不到真的数据类型。由此引出instanceof

typeof返回的结果是字符串,返回值有number、string、boolean、function、undefined、object六个,null返回object;

typeof 也存在弊端,它虽然可以判断基础数据类型(null 除外),但是引用数据类型中,除了function 类型以外,其他的也无法判断

typeof string类型都是小写

typeof 123 // "number"
typeof '123' // "string"
typeof false // "boolean"
typeof undefined //"undefined"
typeof null // "object"

typeof 针对未声明的变量

if (typeof v === "undefined" ) {
   
   
   console.log("变量 v 不存在")
}
instanceof

用来判断一个构造函数的prototype属性所指向的对象是否存在另外一个要检测对象的原型链上。通常来讲,使用 instanceof 就是判断一个实例是否属于某种类型。

instanceof 只能处理两个对象是否属于的实例关系,原始类型不可以检测;

instanceof 可以准确地判断复杂引用数据类型,但是不能正确判断基础数据类型

instance of返回的是布尔值,true或false;

instanceof 类型大写开头

Object.prototype.toString.call(obj)

精确判断对象的类型

console.log(Object.prototype.toString.call("jerry"));//[object String]
console.log(Object.prototype.toString.call(12));//[object Number]
console.log(Object.prototype.toString.call(true));//[object Boolean]
console.log(Object.prototype.toString.call(undefined));//[object Undefined]
console.log(Object.prototype.toString.call(null));//[object Null]
console.log(Object.prototype.toString.call({
   
   name: "jerry"}));//[object Object]
console.log(Object.prototype.toString.call(function(){
   
   }));//[object Function]
console.log(Object.prototype.toString.call([]));//[object Array]
console.log(Object.prototype.toString.call(new Date));//[object Date]
console.log(Object.prototype.toString.call(/\d/));//[object RegExp]
function Person(){
   
   };
console.log(Object.prototype.toString.call(new Person));//[object Object]

原理

[1,2,3].toString()//'1,2,3'

一个数组,调用了toString,返回了字符串 ,这是正常预期,为什么呢,因为Array和Object里都有toString() ,根据原型链的就近原则,会先取Array.prototype,此时我们如果删除Array里面的toString,先检测Array里 有没有toString。

Array.prototype.hasOwnProperty('toString')//true

如果把Array里面的toString方法删除

delete Array.prototype.toString//true

再调用toString()

[1,2,3].toString()//'[object Object]'

此时,会发现,明明放进去的是数组,怎么返回了对象类型,因为Object.prototype 本身是对象类型,所以返回了对象类型,要是想判断传入值的类型,需要把传入值的this,指向Object.prototype ,才能判断传入值的类型。

Object.prototype.toString.call([])//'[object Array]'

总结

  1. toString为Object的原型方法,而Array 、Function等类型作为Object的实例,都重写了toString方法。
  2. 不同的对象类型调用toString方法时,根据原型链的知识,调用的是对应的重写之后的toString方法(Function类型返回内容为函数体的字符串,Array类型返回元素组成的字符串…),而不会去调用Object上原型toString方法(返回对象的具体类型)所以采用obj.toString()不能得到其对象类型,只能将obj转换为字符串类型;因此,在想要得到对象的具体类型时,应该调用Object上原型toString方法。
  3. Object对象本身就有一个toString()方法,返回的是当前对象的字符串形式,原型上的toString()返回的才是我们真正需要的包含对象数据类型的字符串。
  4. 为什么需要call?
    由于Object.prototype.toString()本身允许被修改,像Array、Boolean、Number的toString就被重写过,所以需要调用Object.prototype.toString.call(arg)来判断arg的类型,call将arg的上下文指向Object,所以arg执行了Object的toString方法。
  5. 至于call,就是改变对象的this指向,当一个对象想调用另一个对象的方法,可以通过call或者apply改变其this指向,将其this指向拥有此方法的对象,就可以调用该方法了。
isNaN

isNaN方法可以用来判断一个值是否为NaN

isNaN(NaN) // true
isNaN(123) // false

但是,isNaN只对数值有效,如果传入其他值,会被先转成数值。比如,传入字符串的时候,字符串会被先转成NaN,所以最后返回true,这一点要特别引起注意。也就是说,isNaNtrue的值,有可能不是NaN,而是一个字符串。

isNaN('Hello') // true
// 相当于
isNaN(Number('Hello')) // true

出于同样的原因,对于对象和数组,isNaN也返回true

isNaN({}) // true
// 等同于
isNaN(Number({})) // true

isNaN(['xzy']) // true
// 等同于
isNaN(Number(['xzy'])) // true

但是,对于空数组和只有一个数值成员的数组,isNaN返回false

isNaN([]) // false
isNaN([123]) // false
isNaN(['123']) // false

上面代码之所以返回false,原因是这些数组能被Number函数转成数值

因此,使用isNaN之前,最好判断一下数据类型。

function myIsNaN(value) {
  return typeof value === 'number' && isNaN(value);
}

判断NaN更可靠的方法是,利用NaN为唯一不等于自身的值的这个特点,进行判断。

function myIsNaN(value) {
  return value !== value;
}
isFinite

isFinite方法返回一个布尔值,表示某个值是否为正常的数值。

isFinite(Infinity) // false
isFinite(-Infinity) // false
isFinite(NaN) // false
isFinite(undefined) // false
isFinite(null) // true
isFinite(-1) // true

除了Infinity-InfinityNaNundefined这几个值会返回falseisFinite对于其他的数值都会返回true

操作符

运算符 operator

5 + 6

表达式 组成 操作数和操作符,会有一个结果

算术运算符

  • 加法运算符x + y
  • 减法运算符x - y
  • 乘法运算符x * y
  • 除法运算符x / y
  • 指数运算符x ** y
  • 余数运算符x % y
  • 自增运算符++x 或者 x++
  • 自减运算符--x 或者 x--
  • 数值运算符+x
  • 负数值运算符-x

一元运算符

一元运算符:只有一个操作数的运算符

1 + 2 两个操作数的运算符 二元运算符

++ 自身加1

– 自身减1

  • 前置++

    var num1 = 5;
    ++ num1; 
    
    var num2 = 6;
    console.log(num1 + ++ num2);
    
  • 后置++

    var num1 = 5;
    num1 ++;    
    var num2 = 6 
    console.log(num1 + num2 ++);
    
  • 猜猜看

    var a = 1; var b = ++a + ++a; console.log(b);    
    var a = 1; var b = a++ + ++a; console.log(b);    
    var a = 1; var b = a++ + a++; console.log(b);    
    var a = 1; var b = ++a + a++; console.log(b);  
    

    总结
    前置++:先加1,后参与运算
    后置++:先参与运算,后加1
    上面两个理解后,下面两个自通
    前置-- :先减1,后参与运算
    后置-- :先参与运算,后减1

逻辑运算符(布尔运算符)

&& 与 两个操作数同时为true,结果为true,否则都是false
|| 或 两个操作数有一个为true,结果为true,否则为false
!  非  取反

关系运算符(比较运算符)

<  >  >=  <= == != === !==
=====的区别:==只进行值得比较,===类型和值同时相等,则相等

var result = '55' == 55;  	// true
var result = '55' === 55; 	// false 值相等,类型不相等
var result = 55 === 55; 	// true

赋值运算符

= += -= *= /= %=

例如:
var num = 0;
num += 5;	//相当于  num = num + 5;

二进制运算符 (了解)

javascript 支持二进制运算 ~ | & >> << >>> ^

异或运算 ^ 
异或运算(^)在两个二进制位不同时返回1,相同时返回0。

一般可以用来做开关或者倒值
var a = 33;
var b = 66;

a ^= b, b ^= a, a ^= b;

a // 66
b // 33
否运算符 ~

一般用于 双否 取整
~~ 13.33    // 13

运算符的优先级

优先级从高到底
	1. ()  优先级最高
	2. 一元运算符  ++   --   !
	3. 算数运算符  先*  /  %   后 +   -
	4. 关系运算符  >   >=   <   <=
	5. 相等运算符   ==   !=    ===    !==
	6. 逻辑运算符 先&&   后||
	7. 赋值运算符
	8. 默认从左至右 除了 赋值运算 = 三目运算 ?: 指数运算 **
// 练习1:
4 >= 6 || '山海' != '琨' && !(12 * 2 == 144) && true
// 练习2:
var num = 10;
5 == num / 2 && (2 + 2 * num).toString() === '22'

注: Unicode 码表查询地址 https://www.ltool.net/characters-to-unicode-charts-in-simplified-chinese.php

数据类型转换

chrome浏览器中 不同类型的值 打印颜色不同

字符串的颜色是黑色的,数值类型是蓝色的,布尔类型也是蓝色的,undefined和null是灰色的

转换成字符串类型

  • toString()

    var num = 5;
    console.log(num.toString());
    
  • String()

    String()函数存在的意义:有些值没有toString(),这个时候可以使用String()。比如:undefined和null
    
  • 拼接字符串方式

    num + “”,当 + 两边一个操作符是字符串类型,一个操作符是其它类型的时候,会先把其它类型转换成字符串再进行字符串拼接,返回字符串

转换成数值类型

  • Number(Obj)

    Number()可以把任意值转换成数值,如果要转换的字符串中有一个不是数值的字符,返回NaN
    
  • parseInt(string,radix)

    var num1 = parseInt("12.3abc");  // 返回12,如果第一个字符是数字会解析知道遇到非数字结束
    var num2 = parseInt("abc123");   // 返回NaN,如果第一个字符不是数字或者符号就返回NaN
    
  • parseFloat(string)

    parseFloat()把字符串转换成浮点数
    parseFloat()和parseInt非常相似,不同之处在与
    	parseFloat会解析第一个. 遇到第二个.或者非数字结束
    	如果解析的内容里只有整数,解析成整数
    
  • +,-0等运算

    var str = '500';
    console.log(+str);		// 取正
    console.log(-str);		// 取负
    console.log(str - 0);
    

转换成布尔类型

  • Boolean()

  • !!

    ''(空字符串) null undefined NaN 0 会转换成false 其它都会转换成true

隐式转换

递增递减运算符(前置、后置)

  1. 如果包含的是有效数字字符串或者是有效浮点数字符串,则会将字符串转换(Number())为数值,再进行加减操作,返回值的类型是:number类型。

  2. 如果不包含有效数字字符串,则会将字符串的值转换为NaN,返回值的类型是:number类型。

  3. 如果是boolean类型,则先会把true或者false转换为1或者0,再进行加减操作,返回值的类型是:number类型。

  4. 如果是null类型,则先会把null转换为0,在进行加减操作,返回值的类型是:number类型。

  5. 如果是undefined,则先会把undefined转换为NaN,再进行加减操作,返回值的类型是:number类型。

  6. 如果是对象,则先会通过对象的valueOf()方法,进行转换,如果返回的是NaN,调用toString()方法,在进行前面的操作,返回值的类型是:number类型。(注:空数组[]会返回0,在进行加减操作,空对象则会返回NaN)。

逻辑操作符中的隐式转换规律

注:只有undefined、null、NaN、0、空字符串会被转换为false,其余都为true

逻辑操作符一般用于语句判断中。通过判断结果返回的值进行后面的语句操作。

  1. 逻辑非(!)操作符:首先会通过Boolean()函数将其操作值转换为布尔值,然后求反。
  2. 逻辑与(&&)操作符:如果第一个值经过Boolean()函数转换后为true,则返回第二个操作值,否则返回第一个操作值。
  3. 逻辑或(||)操作符:如果第一个值经过Boolean()函数转换为false,则返回第二个操作值,否则返回第一个操作值。
    (注:逻辑操作符的运算为短路逻辑运算:前一个条件已经能够得出结果后续条件不再执行!)
短路技巧 个位补0
function padLeft(num = 0) {
    return String(num)[1] && String(num) || '0' + num;
}

关系操作符的隐式转换规律

(关系操作符的操作值也可以是任意类型):

  1. 如果两个操作值都是数值,则直接比较大小。
  2. 如果两个操作值都是字符串,则字符串进行其Unicode编码进行比较。
  3. 如果一个操作值是数值,则另一个值转换为数值进行比较。
  4. 如果一个操作值是对象,则调用对象的valueOf()和toString()方法,然后再进行上述比较。
  5. 如果一个操作值是布尔值,则将布尔值转换为数值再进行比较。
    (注:NaN和任何值都不相等,包括自己,同时它与任何类型比较都会返回false。)

相等操作符=的隐式转换规律:

  1. 布尔值、字符串和数值进行比较,会先将其转换为数值再进行比较。
  2. null和undefined比较是相等的,但不是全等的。
  3. NaN与任何值都不相等,都会返回false。

布尔类型的隐式转换

流程控制语句会把后面的值隐式转换成布尔类型

转换为true   非空字符串  非0数字  true 任何对象
转换成false  空字符串  0  false  null  undefined NaN
// 结果是什么?
var a = !!'123';//true 相当于Boolean()
转换不同的数据类型时,相等和不相等操作符遵循下列基本规则:
  • 如果有一个操作数是布尔值,则在比较相等性之前先将其转换为数值——false 转换为 0,而true 转换为 1;
  • 如果一个操作数是字符串,另一个操作数是数值,在比较相等性之前先将字符串转换为数值;
  • 如果一个操作数是对象,另一个操作数不是,则调用对象的 valueOf()方法,用得到的基本类型值按照前面的规则进行比较;

这两个操作符在进行比较时则要遵循下列规则。

  • null 和 undefined 是相等的。

  • 要比较相等性之前,不能将 null 和 undefined 转换成其他任何值。

  • 如果有一个操作数是 NaN,则相等操作符返回 false,而不相等操作符返回 true。 重要提示:即使两个操作数都是 NaN,相等操作符也返回 false;因为按照规则, NaN 不等于 NaN。

  • 如果两个操作数都是对象,则比较它们是不是同一个对象。如果两个操作数都指向同一个对象,
    则相等操作符返回 true;否则,返回 false。

    表达式
    null==undefined true
    “NaN”==NaN false
    5==NaN false
    NaN==NaN false
    false==0 true
    true==1 true
    true==2 false
    undefined==0 false
    null==0 false

null == undefined 会返回 true,因为它们是类似的值;但 null === undefined 会返回 false,因为它们是不同类型的值。

=== 转换

字符串操作环境 数字运算环境 逻辑运算环境 对象操作环境
undefined “undefined” NaN false Error
null “null” 0 false Error
非空字符串 不转换 字符串对应的数字值 True
空字符串 不转换 0 false String
0 “0” 不转换 false Number
NaN “NaN” 不转换 false Number
Infinity “Infinity” 不转换 true Number
Number.POSITIVE_INFINITY “Infinity” 不转换 true Number
Number.NEGATIVE_INFINITY “-Infinity” 不转换 true Number
Number.MAX_VALUE “1.7976931348623157e+308” 不转换 true Number
Number.MIN_VALUE “5e-324” 不转换 true Number
其他所有数字 “数字的字符串值” 不转换 true Number
true “true” 1 不转换 Boolean
false “false” 0 不转换 Boolean
对象 toString() value()或toString()或NaN true 不转换

测试:

1 + true;//2
1 + 'true';//'1true'
1 + undefined;//NaN
1 + null;//1
NaN == NaN;//false
undefined == null;//true
null !== undefined;//true
2 + '5' - 3;//22
6 > '3' == 3;//false
undefined == '0';//false
null == 0;//false
null >= 0;//true
parseInt('13.33') === ~~'13.33';//true
false - 1 <= '0';//true

关于 null 在关系运算和相等运算中的坑:

null > 0   // null 尝试转型为number , 则为0 . 所以结果为 false, 
null >= 0  // null 尝试转为number ,则为0 , 结果为 true. 
null == 0  // null在设计上,在此处不尝试转型. 所以 结果为false. 
  1. 关系运算符 和 相等运算符 并不是一个类别的.
  2. 关系运算符,在设计上,总是需要运算元尝试转为一个number . 而相等运算符在设计上,则没有这方面的考虑.
  3. 最重要的一点, 不要把 拿 a > b , a == b 的结果 想当然的去和 a >= b 建立联系. 正确的符合最初设计思想的关系是 a > b 与 a >= b是一组 . a == b 和其他相等运算符才是一组. 比如 a === b , a != b, a !== b .

控制流程

编程的三种基本结构

顺序结构

从上到下执行的代码就是顺序结构

程序默认就是由上到下顺序执行的

分支结构

根据不同的情况,执行对应代码

循环结构

循环结构:重复做一件事情

分支结构

f语句

语法结构

if (/* 条件表达式 */) {
   
   
  // 执行语句
}

if (/* 条件表达式 */){
   
   
  // 成立执行语句
} else {
   
   
  // 否则执行语句
}

if (/* 条件1 */){
   
   
  // 成立执行语句
} else if (/* 条件2 */){
   
   
  // 成立执行语句
} else if (/* 条件3 */){
   
   
  // 成立执行语句
} else {
   
   
  // 最后默认执行语句
}

案例:
判断一个数是偶数还是奇数

var x = 21
var y 
if (x % 2 === 0) {
   
   
    y = x + '是偶数'
} else {
   
   
    y = x + '是奇数'
}
console.log(y)

​ 分数转换,把百分制转换成ABCDE <60 E 60-70 D 70-80 C 80-90 B 90 - 100 A

var x = 60
var y
if (x >= 90 && x <= 100) {
   
   
    y = 'A'
} else if(x >= 80 && x < 90) {
   
   
    y = 'B'
} else if(x >= 70 && x < 80) {
   
   
    y = 'C'
} else if(x >= 60 && x < 70) {
   
   
    y = 'D'
} else if (x < 60) {
   
   
    y = 'E'
}
console.log('分数为'+y)

​ 判断四季 3-5 春 6-8夏 9-11 秋 12-2 冬

var x = 4
var y
if (x === 3 || x === 4 || x === 5) {
   
   
    y = x + '月是春天'
} else if (x === 6 || x === 7 || x === 8) {
   
   
    y = x + '月是夏天'
} else if (x === 9 || x === 10 || x === 11) {
   
   
    y = x + '月是秋天'
} else if (x === 12 || x === 1 || x === 2) {
   
   
    y = x + '月是冬天'
}
console.log(y)

作业:
判断一个年份是闰年还是平年

var x = 2004
var y
if (x % 4 === 0 && x % 100 !== 0) {
   
   
    y = x + '年是闰年'
} else {
   
   
    y = x + '年是平年'
}
console.log(y)

​ 判断一个人的年龄是否满18岁(是否成年)

var x = 56
var y
if (x > 18) {
   
   
    y = '此人是成年人'
} else {
   
   
    y = '此人是未成年人'
}
console.log(y)

三元运算符

表达式1 ? 表达式2 : 表达式3
是对if……else语句的一种简化写法
i = i ? i < 0 ? Math.max(0, len + i) : i : 0;

说明:

//如果money >= 100 '买小汽车, 开心开心开心!' 如果不成立 什么都不发生

var result = money >= 100 ? '买小汽车, 开心开心开心!' : '';  //表达式为假时什么都不发生,不推荐使用三元表达式

result = money >= 100 && '买小汽车, 开心开心开心!'; //可以用与操作

//个位补零两种方式
var x = '1'
console.log(String(x)[1] && x || '0' + x)
//x = x < 10 ? '0' + x : x

switch语句

语法格式:

switch (expression) {
   
   
  case 常量1:
    语句;
    break;
  case 常量2:
    语句;
    break;
  case 常量3:
    语句;
    break;case 常量n:
    语句;
    break;
  default:
    语句;
    break;
}
break可以省略,如果省略,代码会继续执行下一个case
switch 语句在比较值时使用的是全等操作符, 因此不会发生类型转换(例如,字符串'10' 不等于数值 10)
// 如果num 是 1 或2 时都 打印 一 和哈哈,只要没有break就会把后面的代码都执行


  switch (num) {
   
   

   case 1:

     console.log('一');

   case 2:

     console.log('哈哈哈哈');

     break;

   case 3:

     console.log('三');

     break;

   case 4:

     console.log('四');

     break;

   default:

     console.log('请重新输入 1 - 4 阿拉伯数字');

     break;

  }

作业: switch 打印星期几 0为星期日 1-6分别对应星期一到星期六

    // switch 打印星期几 0为星期日 1 - 6分别对应星期一到星期六
    var day = 0;
    switch (day) {
   
   
      case 0:
        console.log('星期日');
        break;
      case 1:
        console.log('星期一');
        break;
      case 2:
        console.log('星期二');
        break;
    }

//数组替换switch实现根据0-6打印星期一到星期日 数组映射 
//空间换时间  switch是花大量时间去处理换成了数组,数组是从空间上去增加,效率较高,不存在判断,是直接取值,也不存在赋值流程
//分支值或者可能性较多并且有规律的时候可以用数组映射 成熟的开发者不会用大量的ifelse或者switch
var a = 1
var arr = ['日','一','二','三','四','五','六'] 
console.log('今天是星期' + arr[a])

交互与写入:alert、prompt 和 confirm / write

由于我们将使用浏览器作为我们的演示环境,让我们看几个与用户交互的函数:alertpromptconfirm

alert

这个我们前面已经看到过了。它会显示一条信息,并等待用户按下 “OK”。

例如:

alert("Hello");

弹出的这个带有信息的小窗口被称为 模态窗。“modal” 意味着用户不能与页面的其他部分(例如点击其他按钮等)进行交互,直到他们处理完窗口。在上面示例这种情况下 —— 直到用户点击“确定”按钮。

prompt

pormpt有文本消息的模态窗口,还有 input 框和确定/取消按钮。

result = prompt(title, [default]);

浏览器会显示一个带有文本消息的模态窗口,还有 input 框和确定/取消按钮。

  • title

    显示给用户的文本

  • default

    可选的第二个参数,指定 input 框的初始值。

语法中的方括号 [...]

上述语法中 default 周围的方括号表示该参数是可选的,不是必需的。

访问者可以在提示输入栏中输入一些内容,然后按“确定”键。然后我们在 result 中获取该文本。或者他们可以按取消键或按 Esc 键取消输入,然后我们得到 null 作为 result

prompt 将返回用户在 input 框内输入的文本,如果用户取消了输入,则返回 null

举个例子:

var age = prompt('How old are you?', 100);

alert("You are" + age +"ars old!"); 

IE 浏览器会提供默认值

第二个参数是可选的。但是如果我们不提供的话,Internet Explorer 会把 "undefined" 插入到 prompt。

我们可以在 Internet Explorer 中运行下面这行代码来看看效果:

var test = prompt("Test");

所以,为了 prompt 在 IE 中有好的效果,我们建议始终提供第二个参数:

var test = prompt("Test", ''); // <-- 用于 IE 浏览器

confirm

confirm取消两个按钮的模态窗口。

result = confirm(question);

confirm 函数显示一个带有 question 以及确定和取消两个按钮的模态窗口。

点击确定返回 true,点击取消返回 false

例如:

var isBoss = confirm("Are you the boss?");

alert( isBoss ); // 如果“确定”按钮被按下,则显示 true

document.write

在JavaScript中document.write()函数可以向文档写入HTML表达式或JavaScript代码,用法“document.write(exp1,exp2,exp3,…)”,该函数可接受任何多个参数,并将其写入文档中。

document.write('我被写入了BODY中','还有我');

循环结构

在javascript中,循环语句有三种,while、do…while、for循环。

我们经常需要重复执行一些操作。

例如,我们需要将列表中的商品逐个输出,或者运行相同的代码将数字 1 到 10 逐个输出。

循环 是一种重复运行同一代码的方法。

while语句

基本语法:

// 当循环条件为true时,执行循环体,
// 当循环条件为false时,结束循环。
while (循环条件) {
   
   
  //循环体
}

condition(条件) 为 true 时,执行循环体的代码。

例如,以下将循环输出当 i < 3 时的 i 值:

var i = 0;
while (i < 3) {
   
    // 依次显示 0、1 和 2
  console.log(i);
  i++;
}

//递减操作
var i = 10;
while (i) {
   
    // 10 9 8 7 6 5 4 3 2 1
   console.log(i);
   i--;
}

do…while语句

do…while循环和while循环非常像,二者经常可以相互替代,但是do…while的特点是不管条件成不成立,都会执行一次。

基础语法:

do {
   
   
  // 循环体;
} while (循环条件);

代码示例:

// 初始化变量
var i = 1;
var sum = 0;
do {
   
   
  sum += i;//循环体
  i++;//自增
} while (i <= 100);//循环条件

for语句

while和do…while一般用来解决无法确认次数的循环。for循环一般在循环次数确定的时候比较方便

for循环语法:

// for循环的表达式之间用的是;号分隔的,千万不要写成,
for (初始化表达式1; 判断表达式2; 自增or自减表达式3) {
   
   
  // 循环体4
}

执行顺序:1243 ---- 243 -----243(直到循环条件变成false)

  1. 初始化表达式
  2. 判断表达式
  3. 自增表达式
  4. 循环体

案例:

//求1-100之间所有数的乘积
var result = 1
for(i = 1 ; i <= 100 ; i++ ) {
   
   
    result *= i
}
console.log(result)

//求1-100之间所有奇数的和
var result = 0
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值