前言
在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?
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的结果。 在这个过程中我们其实做了两件事情:
-
我们声明变量 是通过 关键字var 创建了变量 num
-
我们将 表达式
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 语言的标识符对大小写敏感,所以
a和A是两个不同的标识符。
命名规则
必须遵守的命名规则 如果不遵守 就会报错
- 由字母、数字、下划线、$符号组成,不能以数字以及其他符号开头
- 不能是保留关键字,例如: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;
上面的这个代码中 我们书写语句的顺序是
- 调用了 num 进行打印
- 声明了变量 num 并且 赋值 10
实际在javascript引擎解释后 顺序为
var num;
console.log(num); //undefined
num = 10;
- 先声明 num 这一步称为 变量提升
- 调用console.log() 打印 num的值 这时因为没有给num赋值 num的值还是 初始默认值 undefined
- 给num 赋值 为 10
注释
在javascript也会频繁用到注释 注释形式有 两种
// 这是单行注释 ctrl+/
/*
这是
多行
注释
ctrl+shift+/
*/
数据类型
JavaScript 语言的每一个值,都属于某一种数据类型。JavaScript 的数据类型,共有七种。
- 数值(number):整数和小数(比如
1和3.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
- NaN:not a number 表示“非数字”(Not a Number),主要出现在将字符串解析成数字出错的场合。
数值精度问题
根据国际标准 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);- 两边只要有一个是字符串,那么+就是字符串拼接功能
- 两边如果都是数字,那么就是算术功能。
-
字符串换行
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
null与undefined都可以表示“没有”,含义非常相似。将一个变量赋值为undefined或null,老实说,语法效果几乎没区别。
- undefined表示一个声明了没有赋值的变量,变量只声明的时候值默认是undefined
- 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]'
总结
- toString为Object的原型方法,而Array 、Function等类型作为Object的实例,都重写了toString方法。
- 不同的对象类型调用toString方法时,根据原型链的知识,调用的是对应的重写之后的toString方法(Function类型返回内容为函数体的字符串,Array类型返回元素组成的字符串…),而不会去调用Object上原型toString方法(返回对象的具体类型)所以采用obj.toString()不能得到其对象类型,只能将obj转换为字符串类型;因此,在想要得到对象的具体类型时,应该调用Object上原型toString方法。
- Object对象本身就有一个toString()方法,返回的是当前对象的字符串形式,原型上的toString()返回的才是我们真正需要的包含对象数据类型的字符串。
- 为什么需要call?
由于Object.prototype.toString()本身允许被修改,像Array、Boolean、Number的toString就被重写过,所以需要调用Object.prototype.toString.call(arg)来判断arg的类型,call将arg的上下文指向Object,所以arg执行了Object的toString方法。 - 至于call,就是改变对象的this指向,当一个对象想调用另一个对象的方法,可以通过call或者apply改变其this指向,将其this指向拥有此方法的对象,就可以调用该方法了。
isNaN
isNaN方法可以用来判断一个值是否为NaN。
isNaN(NaN) // true
isNaN(123) // false
但是,isNaN只对数值有效,如果传入其他值,会被先转成数值。比如,传入字符串的时候,字符串会被先转成NaN,所以最后返回true,这一点要特别引起注意。也就是说,isNaN为true的值,有可能不是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、-Infinity、NaN和undefined这几个值会返回false,isFinite对于其他的数值都会返回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
隐式转换
递增递减运算符(前置、后置)
-
如果包含的是有效数字字符串或者是有效浮点数字符串,则会将字符串转换(Number())为数值,再进行加减操作,返回值的类型是:number类型。
-
如果不包含有效数字字符串,则会将字符串的值转换为NaN,返回值的类型是:number类型。
-
如果是boolean类型,则先会把true或者false转换为1或者0,再进行加减操作,返回值的类型是:number类型。
-
如果是null类型,则先会把null转换为0,在进行加减操作,返回值的类型是:number类型。
-
如果是undefined,则先会把undefined转换为NaN,再进行加减操作,返回值的类型是:number类型。
-
如果是对象,则先会通过对象的valueOf()方法,进行转换,如果返回的是NaN,调用toString()方法,在进行前面的操作,返回值的类型是:number类型。(注:空数组[]会返回0,在进行加减操作,空对象则会返回NaN)。
逻辑操作符中的隐式转换规律
注:只有undefined、null、NaN、0、空字符串会被转换为false,其余都为true
逻辑操作符一般用于语句判断中。通过判断结果返回的值进行后面的语句操作。
- 逻辑非(!)操作符:首先会通过Boolean()函数将其操作值转换为布尔值,然后求反。
- 逻辑与(&&)操作符:如果第一个值经过Boolean()函数转换后为true,则返回第二个操作值,否则返回第一个操作值。
- 逻辑或(||)操作符:如果第一个值经过Boolean()函数转换为false,则返回第二个操作值,否则返回第一个操作值。
(注:逻辑操作符的运算为短路逻辑运算:前一个条件已经能够得出结果后续条件不再执行!)
短路技巧 个位补0
function padLeft(num = 0) {
return String(num)[1] && String(num) || '0' + num;
}
关系操作符的隐式转换规律
(关系操作符的操作值也可以是任意类型):
- 如果两个操作值都是数值,则直接比较大小。
- 如果两个操作值都是字符串,则字符串进行其Unicode编码进行比较。
- 如果一个操作值是数值,则另一个值转换为数值进行比较。
- 如果一个操作值是对象,则调用对象的valueOf()和toString()方法,然后再进行上述比较。
- 如果一个操作值是布尔值,则将布尔值转换为数值再进行比较。
(注:NaN和任何值都不相等,包括自己,同时它与任何类型比较都会返回false。)
相等操作符和=的隐式转换规律:
- 布尔值、字符串和数值进行比较,会先将其转换为数值再进行比较。
- null和undefined比较是相等的,但不是全等的。
- 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.
- 关系运算符 和 相等运算符 并不是一个类别的.
- 关系运算符,在设计上,总是需要运算元尝试转为一个number . 而相等运算符在设计上,则没有这方面的考虑.
- 最重要的一点, 不要把 拿 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
由于我们将使用浏览器作为我们的演示环境,让我们看几个与用户交互的函数:alert,prompt 和confirm。
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-100之间所有数的乘积
var result = 1
for(i = 1 ; i <= 100 ; i++ ) {
result *= i
}
console.log(result)
//求1-100之间所有奇数的和
var result = 0

最低0.47元/天 解锁文章
2万+

被折叠的 条评论
为什么被折叠?



