《JavaScript高级程序设计》学习笔记(一)

本文是作者在学习《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模块,而且只有这个时候代码中才能出现importexport关键字。

使用JavaScript有两种方式,一种是在网页内,一种是通过外部文件的方式。
在使用行内代码是注意在代码中不能出现<\script>,如果需要使用则使用</\script>转义即可。
若使用外部脚本,在scr中写入外部文件的地址即可,这时会网页会被阻塞(阻塞时间也办好下载外部脚本的时间)。

<script src="test.js"><\script>

另外需要注意的是,若使用src属性则<script>中则不能再包含行内代码,若两者都有,那么浏览器只会下载并执行外部脚本,而忽略行内代码。

在没有使用deferasync属性的情况下,所有浏览器都会按照<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个关键字可以声明变量:varletconst。其中constlet只能在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. 暂时性死区

letvar的另一重要区别就是,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 声明风格

  1. 不使用var
  2. 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的值
Booleantruefalse
String非空字符串“”(空字符串)
Number非0字符(包括无穷)0、NaN
Object任意对象null
UndefinedN/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));

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值