文章目录
- 一、什么是JavaScript
- JavaScript的组成
- 二、HTML中的JavaScript
- 2.1 <script>元素
- 2.1.1 标签位置
- 2.1.2推迟执行脚本
- 2.1.3 异步执行脚本
- 2.1.4 动态记载脚本
- 2.2 行内代码和外部文件
- 2.3 文档模式
- 2.4 <noscript>元素
- 三、JavaScript的语言基础
- 3.1 变量
- 3.1.1 var关键字
- 1. var 声明作用域
- var声明提升
- 3.1.2 let 声明
- 1. 暂时性死区
- 2. 全局声明
- 3. 条件声明
- 4. for循环中的let
- 3.1.3 const 声明
- 3.1.4 声明风格
- 3.2 数据类型
- 3.2.1 typeof 操作符
- 3.2.2 Undefined 类型
- 3.2.3 Null 类型
- 3.2.4 Boolean 类型
- 3.2.5 Number类型
- 1. 浮点数
- 2. 值的范围
- 3. NaN
本文是作者在学习《JavaScript高级程序设计》第四版时所总结的笔记。
一、什么是JavaScript
JavaScript是由网景公司和Sun公司联合开发的。
JavaScript的组成
JavaScript包含三部分:ECMAScript、DOM和BOM。
二、HTML中的JavaScript
2.1 <script>元素
将JavaScript插入HTML中主要方法就是使用<script>
元素,它具有下来8个属性
- async:可选。表示应该立即开始下载脚本,但不阻止其他页面当作。只对外部脚本文件有效。
- charset:可选。表示字符集。很少使用。
- crossorigin:可选。配置相关请求的CORS(跨资源共享)设置。
crossorigin="anonymous"
配置文件请求不必设置凭据标志。crossorigin="use-credentials"
设置凭据标志,意味着出站请求会包含凭据。- defer:可选。表示脚本可以延迟到文档完全被解析和显示之后在执行。只对外部脚本有效。
- integrity:可选。允许对比接收到的资源和指定的加密签名以验证子资源完整性。这个属性可以用于确保内容分发网络(CDN)不会提供恶意内容。
- language:已被弃用。
- scr:可选。鸟事包含要执行的外部文件。
- type:可选。代替language,表示代码中块中的脚本语言的内容类型。按照惯例这个值是text/javascript,但其实已被废弃。如果这个值是
module
,则代码会被挡车ES6模块,而且只有这个时候代码中才能出现import
和export
关键字。
使用JavaScript有两种方式,一种是在网页内,一种是通过外部文件的方式。
在使用行内代码是注意在代码中不能出现<\script>
,如果需要使用则使用</\script>
转义即可。
若使用外部脚本,在scr中写入外部文件的地址即可,这时会网页会被阻塞(阻塞时间也办好下载外部脚本的时间)。
<script src="test.js"><\script>
另外需要注意的是,若使用src属性则<script>
中则不能再包含行内代码,若两者都有,那么浏览器只会下载并执行外部脚本,而忽略行内代码。
在没有使用defer
和async
属性的情况下,所有浏览器都会按照<script>
在页面中出现的顺序依次去解释他们。
2.1.1 标签位置
在过去所有的<script>
都放在<head>
标签内,这主要是为了把外部CSS和JavaScript文件放在一起。但是这样做意味着必须把所有的JavaScript代码都下载、解析和解释完成后,才能开始渲染页面(从body开始),从而造成页面延迟和空白。所以现在大多都将JavaScript引用放在了<body>
元素中内容之后。
<body>
content
<script scr='test.js'></script>
</body>
2.1.2推迟执行脚本
使用defer属性可以是<script>
依旧可以放在head里面而被推迟在解析完</html>
后才会被解析。
需要注意的是对于XHTML来说应该写为defer="defer"
。
但是对于其他浏览器可能会忽略这个属性,所以还是按照把外部脚本文件放在底部较好。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script defer></script>
</head>
<body>
</body>
</html>
2.1.3 异步执行脚本
H5为<script>加入了async属性。在加入这个属性后,脚本并不能保证他们可以按照出现的次序执行。
浏览器会不必等待脚本下载和执行完后载加载页面,同同样也不必等到该该异步脚本下载和执行完后再加载其他的脚本。
异步脚本会保证在页面的load事件前执行,但可能会在DOMContentLoaded之前或后加载。
使用async也告诉浏览器你不会使用document.write。
总的来说异步执行脚本不被推荐。
2.1.4 动态记载脚本
let script = document.createElement('script');
script.src = 'test.js';
document.head.appendChild(script);
在默认情况下,这种方式是以异步加载的,但不是所以浏览器都支持async
属性。因此可以设置同步加载
let script = document.createElement('script');
script.src = 'test.js';
script.async = false;
document.head.appendChild(script);
以这种方式获取的资源对浏览器预加载器是不可见的。这会严重影响他们在资源获取队列的优先级。想让预加载器知道这些动态请求的存在,可以在文档头部声明他们。
<link rel='preload' href='test.js'>
2.2 行内代码和外部文件
在具体开发中推荐使用的外部文件的方法引入JavaScript。具体有三个理由:
- 可维护性。
- 缓存。浏览器会根据特定的设置缓存所有的外部链接的JavaScript文件,这意味着当两个页面使用的是同样的JavaScript文件则只需要下载一次,这大大加快了网页记载的速度。
- 适应未来
2.3 文档模式
待更新
2.4 <noscript>元素
这是对于那些禁用JavaScript的浏览器来说的。对于这个标签中的内容若满足:浏览器不支持脚本和浏览器对脚本的支持被关闭,则不会渲染其中的内容。
三、JavaScript的语言基础
3.1 变量
在Javascript中的变量是松散型的,也就意味着它可以保存任何类型的数据。有3个关键字可以声明变量:var
、let
和 const
。其中const
和 let
只能在ECMAScript6及其以后的版本中使用。
3.1.1 var关键字
var message;//定义了一个message变量,不初始化的情况下会保存一个特殊的值undefined
message = "hi"; //这对message赋值一个hi字符串。
message = 123; // 合法但不推荐使用。
在如今的js中已经不推荐使用var来声明变量了。
1. var 声明作用域
使用var操作定义的变量会变成为包含它的函数的局部变量。比如:
function test(){
var message = "hi";
}
test();
console.log(message);
可以看到message这个变量并没有被定义。因为在执行完了test()这函数以后,message就会被销毁。可以在定义的时候不加var来实现定义全局变量。
function test(){
message = "hi";
}
test();
console.log(message);
只要调用一次test()函数,那么就会定义一个message全局变量。但是并不推荐这么做,因为很难维护,也无法判断省略var到底是有意为之还是忘记了加。在严格模式下。如果像给这样给未声明的变量赋值,而会抛出ReferenceError。
多个变量定义
var message = "hi",
found = false,
age = 29;
在严格模式下,不能定义名为eval和arguments的变量
var声明提升
使用var定义变量时会自动提升到函数作用域的顶部
function test(){
console.log(age);
var age = 26;
}
test();
上面的代码相当于:
function test(){
var age;
console.log(age);
age = 26;
}
test();
所以不会出错,且输出的是undefined。这就是所谓的hoist(提升)。此外使用var重复声明一个变量也是没问题的。
3.1.2 let 声明
let
声明是的范围是块作用域,而var
是函数作用域。
对于var
来说,它是函数作用域,所以下面的代码是正确的。
if(true){
var name = "fujiaxu";
}
console.log(name);
但是对于let
来说,它是块作用域,所以在 if 外部是不能被引用的。块级作用域是函数作用域的子集。
if(true){
let Name = "fujiaxu";
}
console.log(Name);
1. 暂时性死区
let
和var
的另一重要区别就是,let
不会被提升声明。
console.log(age);// ReferenceError:age没有定义
let age = 26;
在解析代码时,JavaScript引擎会注意到出现在后面的let
声明,但是在之前不能以任何方式引用未声明的任何变量。在let执行之前的瞬间被称为“暂时性死区(temporal dead zone)”,在此阶段引用任何后面才声明的变量都会抛出ReferenceError。
2. 全局声明
与var
关键字不同,使用let
在全局作用域中声明的变量不会成为window的对象属性(使用var
就会)。
var name = "ddd";
console.log(window.name);// ddd
let age = 26;
console.log(window.age);// undefined
为了避免出现SyntaxError,避免在一个页面中重复定义同一个变量。
3. 条件声明
4. for循环中的let
在let
出现之前,for循环定义的迭代变量会渗透到循环体外部。
for(var i =0;i<5;i++){
setTimeout(() => console.log(i),0);//这里会输出5个5,这是为什么呢?
}
之所以会这样,是因为在退出循环时,i
保存的是5.在之后执行超时时,所有的i
都是同一个变量。而若使用let
定义变量,那么每次迭代都会声明一个新的变量,所以就可以得到我们想要的结果:0,1,2,3,4。
3.1.3 const 声明
const
声明的变量无法被修改。需要的注意的是:const
声明的限制只是适用于它指向的变量的引用。换句话说,当const
变量引用的是一个对象时,那么修改这个对象内部的属性并不违反const
的限制。const
对于for-of 和 for-in 循环具有特别有意义。
因为每次都创建了一个新变量。
for (const key in {a:1,b:2}){
console.log(key);
}
//a,b
for (const value of [1,2,3,4,5]){
console.log(value);
}
//1,2,3,4,5
3.1.4 声明风格
- 不使用var
- const优先,let次之
3.2 数据类型
ECMAScript由6种简单数据类型:Undefined、Null、Boolean、String、Number和Symbol。其中Symbol(符号)是在ES6新增的。还有一种复杂的数据类型是Object(对象)是一种无须名值对的集合。在ECMAScript不等自己定义数据类型,所以所有的值都需要靠上述7中数据类型来实现。
3.2.1 typeof 操作符
对一个变量使用哦个typeof会返回它数据类型的字符串
“undefined”表示值未定义
“boolean”表示值为布尔值
“number”表示值为数值
“string”表示值为字符串
“object”表示值为对象(而不是函数)或null。
“function”表示值为函数
“symbol”表示值为符号
let m = "hello";
console.log(typeof m);//"string"
console.log(typeof (m));//"string"
console.log(typeof 22);//"number"
需要注意的是null的类型是object,会被认为是一个空对象的引用。
3.2.2 Undefined 类型
当使用let
或者var
声明了变量但没有初始化时,就相当于给变量赋予了undefined
值。
但我们需要注意的时对于未声明的变量,使用typeof也会给出"undefined"的结果,所以在声明变量的时候还是进行初始化,这就是为了区别给出"undefined"的结果是因为未初始化还是未声明。
3.2.3 Null 类型
逻辑上来讲,null值表示一个空对象的指针,这也是给typeof传一个null会返回“object”的原因。
let car = null;
console.log(typeof car);//"object"
在定义将来要保存对象值的变量时,建议使用null来初始化。
undefined的值是由null衍生出来的,所以使用==
来判断时他们是相等的,当然这是表面上的相等,若使用===
他们就不相等了。
3.2.4 Boolean 类型
需要注意的是要严格区分大小写,true 和 false 才代表真假,而True和 False只是标识符而已。
要将一个其他类型的值转为布尔值,可以使用Boolean() 转换函数。
let m = "Hello World";
let mB = Boolean(m);
数据类型 | 转化为true的值 | 转换为false的值 |
Boolean | true | false |
String | 非空字符串 | “”(空字符串) |
Number | 非0字符(包括无穷) | 0、NaN |
Object | 任意对象 | null |
Undefined | N/A(不存在) | undefined |
3.2.5 Number类型
对于Number不同进制的表达:
let x = 0b0101;//二进制
let x = 123;// 十进制
let x = 07;//八进制
let x = 09;//无效八进制,这里转换为9
let x = 0xA;//十六进制,转换为十进制是10
// 需要特别注意的是在所有的数学操作中,八进制和十六进制都被视为十进制数值
1. 浮点数
浮点值的精度最高可达17位小数,但是在计算中不如那么准确。比如0.1+0.2 !=
0.3。
这是因为使用了IEEE 754,0.1在计算中存储中无法有效的转换为二进制数(因为会无限循环),所以会舍弃一部分。这样就导致结果出现了误差。
2. 值的范围
let x = Number.MAX_VALUE;
let y = Number.MIN_VALUE;
isFinite();//判断是否为有限值
3. NaN
这代表不是数值(Not a Number),用于表示本来要返回数值的操作失败了(不是抛出异常)。
需要注意的是:NaN不等于包含NaN在内的任意值,对于下列操就会返回false
console.log(NaN == NaN);// fasle
所以有一个isNaN()
函数来判断是否为数值。把一个值传给它之后,该函数会尝试将其转变换为数值,如字符串和布尔值。对于任意无法转换为数值的值会导致这个函数返回false。
console.log(isNaN(NaN));
console.log(isNaN(10));
console.log(isNaN("10"));
console.log(isNaN("blue"));
console.log(isNaN(true));