js前置知识

什么是 JavaScript 语言?
JavaScript 是一种轻量级的脚本语言。所谓“脚本语言”(script language),指的是它不具备开发操作系统的能力,而是只用来编写控制其他大型应用程序(比如浏览器)的“脚本”。
JavaScript 也是一种嵌入式(embedded)语言。它本身提供的核心语法不算很多,只能用来做一些数学和逻辑运算。JavaScript 本身不提供任何与 I/O(输入/输出)相关的 API,都要靠宿主环境(host)提供,所以 JavaScript 只合适嵌入更大型的应用程序环境,去调用宿主环境提供的底层 API。
目前,已经嵌入 JavaScript 的宿主环境有多种,最常见的环境就是浏览器,另外还有服务器环境,也就是 Node 项目。
从语法角度看,JavaScript 语言是一种“对象模型”语言。各种宿主环境通过这个模型,描述自己的功能和操作接口,从而通过 JavaScript 控制这些功能。但是,JavaScript 并不是纯粹的“面向对象语言”,还支持其他编程范式(比如函数式编程)。这导致几乎任何一个问题,JavaScript 都有多种解决方法。阅读本书的过程中,你会诧异于 JavaScript 语法的灵活性。
JavaScript 的核心语法部分相当精简,只包括两个部分:基本的语法构造(比如操作符、控制结构、语句)和标准库(就是一系列具有各种功能的对象比如Array、Date、Math等)。除此之外,各种宿主环境提供额外的 API(即只能在该环境使用的接口),以便 JavaScript 调用。以浏览器为例,它提供的额外 API 可以分成三大类。
o 浏览器控制类:操作浏览器
o DOM 类:操作网页的各种元素
o Web 类:实现互联网的各种功能
如果宿主环境是服务器,则会提供各种操作系统的 API,比如文件操作 API、网络通信 API等等。这些你都可以在 Node 环境中找到。
本书主要介绍 JavaScript 核心语法和浏览器网页开发的基本知识,不涉及 Node。全书可以分成以下四大部分。
o 基本语法
o 标准库
o 浏览器 API
o DOM
JavaScript 语言有多个版本。本书的内容主要基于 ECMAScript 5.1 版本,这是学习 JavaScript 语法的基础。
JavaScript 的基本语法
语句
JavaScript 程序的执行单位为行(line),也就是一行一行地执行。一般情况下,每一行就是一个语句。
语句(statement)是为了完成某种任务而进行的操作,比如下面就是一行赋值语句。

var a=1+3;

这条语句先用var命令,声明了变量a,然后将1 + 3的运算结果赋值给变量a。
1 + 3叫做表达式(expression),指一个为了得到返回值的计算式。语句和表达式的区别在于,前者主要为了进行某种操作,一般情况下不需要返回值;后者则是为了得到返回值,一定会返回一个值。凡是 JavaScript 语言中预期为值的地方,都可以使用表达式。比如,赋值语句的等号右边,预期是一个值,因此可以放置各种表达式。
语句以分号结尾,一个分号就表示一个语句结束。多个语句可以写在一行内。

var a=1+3; var b='abc';

分号前面可以没有任何内容,JavaScript 引擎将其视为空语句。

;;;

上面的代码就表示3个空语句。
表达式不需要分号结尾。一旦在表达式后面添加分号,则 JavaScript 引擎就将表达式视为语句,这样会产生一些没有任何意义的语句。

1+3;
'abc';

上面两行语句只是单纯地产生一个值,并没有任何实际的意义。
变量
概念
变量是对“值”的具名引用。变量就是为“值”起名,然后引用这个名字,就等同于引用这个值。变量的名字就是变量名。

var a=1;

上面的代码先声明变量a,然后在变量a与数值1之间建立引用关系,称为将数值1“赋值”给变量a。以后,引用变量名a就会得到数值1。最前面的var,是变量声明命令。它表示通知解释引擎,要创建一个变量a。
注意,JavaScript 的变量名区分大小写,A和a是两个不同的变量。
变量的声明和赋值,是分开的两个步骤,上面的代码将它们合在了一起,实际的步骤是下面这样。

var a;
a=1;

如果只是声明变量而没有赋值,则该变量的值是undefined。undefined是一个特殊的值,表示“无定义”。

var a;
a// undefined

如果变量赋值的时候,忘了写var命令,这条语句也是有效的。

var a=1;
// 基本等同
a=1;

但是,不写var的做法,不利于表达意图,而且容易不知不觉地创建全局变量,所以建议总是使用var命令声明变量。
如果一个变量没有声明就直接使用,JavaScript 会报错,告诉你变量未定义。

x
// ReferenceError: x is not defined

上面代码直接使用变量x,系统就报错,告诉你变量x没有声明。
可以在同一条var命令中声明多个变量。

var a, b;

JavaScript 是一种动态类型语言,也就是说,变量的类型没有限制,变量可以随时更改类型。

var a=1;
a='hello';

php/python/js灵活的动态语言
(java/c++静态语言)
上面代码中,变量a起先被赋值为一个数值,后来又被重新赋值为一个字符串。第二次赋值的时候,因为变量a已经存在,所以不需要使用var命令。
如果使用var重新声明一个已经存在的变量,是无效的。

var x=1;
var x;
x// 1

上面代码中,变量x声明了两次,第二次声明是无效的。
但是,如果第二次声明的时候还进行了赋值,则会覆盖掉前面的值。

var x=1;
var x=2;
// 等同于
var x=1;
var x;
x=2;

变量提升
JavaScript 引擎的工作方式是,先解析代码,获取所有被声明的变量,然后再一行一行地运行。这造成的结果,就是所有的变量的声明语句,都会被提升到代码的头部,这就叫做变量提升(hoisting)。

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

上面代码首先使用console.log方法,在控制台(console)显示变量a的值。这时变量a还没有声明和赋值,所以这是一种错误的做法,但是实际上不会报错。因为存在变量提升,真正运行的是下面的代码。

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

最后的结果是显示undefined,表示变量a已声明,但还未赋值。
标识符
标识符(identifier)指的是用来识别各种值的合法名称。最常见的标识符就是变量名,以及后面要提到的函数名。JavaScript 语言的标识符对大小写敏感,所以a和A是两个不同的标识符。
标识符有一套命名规则,不符合规则的就是非法标识符。JavaScript 引擎遇到非法标识符,就会报错。
简单说,标识符命名规则如下。
o 第一个字符,可以是任意 Unicode 字母(包括英文字母和其他语言的字母),以及美元符号($)和下划线(_)。
o 第二个字符及后面的字符,除了 Unicode 字母、美元符号和下划线,还可以用数字0-9。
下面这些都是合法的标识符。

arg0
_tmp
$elem
π

下面这些则是不合法的标识符。

1a // 第一个字符不能是数字
23 // 同上
*** // 标识符不能包含星号
a+b // 标识符不能包含加号
-d // 标识符不能包含减号或连词线

中文是合法的标识符,可以用作变量名。

var 临时变量=1;

JavaScript 有一些保留字,不能用作标识符: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。
注释
源码中被 JavaScript 引擎忽略的部分就叫做注释,它的作用是对代码进行解释。JavaScript 提供两种注释的写法:一种是单行注释,用//起头;另一种是多行注释,放在/和/之间。

// 这是单行注释
/*
这是
多行
注释
*/
此外,由于历史上 JavaScript 可以兼容 HTML 代码的注释,所以<!---->也被视为合法的单行注释。
x=1; <!--x=2;
-->x=3;

上面代码中,只有x = 1会执行,其他的部分都被注释掉了。
需要注意的是,–>只有在行首,才会被当成单行注释,否则会当作正常的运算。

functioncountdown(n) {
while(n-->0) console.log(n);
}
countdown(3)
// 2
// 1
// 0
上面代码中,n --> 0实际上会当作n-- > 0,因此输出210。
区块
JavaScript 使用大括号,将多个相关的语句组合在一起,称为“区块”(block)。
对于var命令来说,JavaScript 的区块不构成单独的作用域(scope)。
{
var a=1;
}
a// 1

上面代码在区块内部,使用var命令声明并赋值了变量a,然后在区块外部,变量a依然有效,区块对于var命令不构成单独的作用域,与不使用区块的情况没有任何区别。在 JavaScript 语言中,单独使用区块并不常见,区块往往用来构成其他更复杂的语法结构,比如for、if、while、function等。
条件语句
JavaScript 提供if结构和switch结构,完成条件判断,即只有满足预设的条件,才会执行相应的语句。
if 结构
if结构先判断一个表达式的布尔值,然后根据布尔值的真伪,执行不同的语句。所谓布尔值,指的是 JavaScript 的两个特殊值,true表示“真”,false表示“伪”。
if(布尔值)
语句;
// 或者
if(布尔值) 语句;
上面是if结构的基本形式。需要注意的是,“布尔值”往往由一个条件表达式产生的,必须放在圆括号中,表示对表达式求值。如果表达式的求值结果为true,就执行紧跟在后面的语句;如果结果为false,则跳过紧跟在后面的语句。

if(m===3)
m=m+1;

上面代码表示,只有在m等于3时,才会将其值加上1。
这种写法要求条件表达式后面只能有一个语句。如果想执行多个语句,必须在if的条件判断之后,加上大括号,表示代码块(多个语句合并成一个语句)。

if(m===3) {
m+=1;
}

建议总是在if语句中使用大括号,因为这样方便插入语句。
注意,if后面的表达式之中,不要混淆赋值表达式(=)、严格相等运算符(=)和相等运算符()。尤其是赋值表达式不具有比较作用。

var x=1;
var y=2;
if(x===y) {
console.log(x);
}
// "2"

上面代码的原意是,当x等于y的时候,才执行相关语句。但是,不小心将严格相等运算符写成赋值表达式,结果变成了将y赋值给变量x,再判断变量x的值(等于2)的布尔值(结果为true)。
这种错误可以正常生成一个布尔值,因而不会报错。为了避免这种情况,有些开发者习惯将常量写在运算符的左边,这样的话,一旦不小心将相等运算符写成赋值运算符,就会报错,因为常量不能被赋值。

if(x=2) { // 不报错
if(2=x) { // 报错

三元运算符 ?:
JavaScript 还有一个三元运算符(即该运算符需要三个运算子)?:,也可以用于逻辑判断。
(条件) ?表达式1: 表达式2
上面代码中,如果“条件”为true,则返回“表达式1”的值,否则返回“表达式2”的值。

var even=(n%2===0) ?true: false;
上面代码中,如果n可以被2整除,则even等于true,否则等于false。它等同于下面的形式。
var even;
if(n%2===0) {
even=true;
} else{
even=false;
}

这个三元运算符可以被视为if…else…的简写形式,因此可以用于多种场合。

var myVar;
console.log(
myVar?
'myVar has a value':
'myVar does not have a value'
)
// myVar does not have a value

上面代码利用三元运算符,输出相应的提示。

var msg='数字'+n+'是'+(n%2===0?'偶数': '奇数');

在这里插入图片描述

上面代码利用三元运算符,在字符串之中插入不同的值。

window 对象
概述
浏览器里面,window对象(注意,w为小写)指当前的浏览器窗口。它也是当前页面的顶层对象,即最高一层的对象,所有其他对象都是它的下属。一个变量如果未声明,那么默认就是顶层对象的属性。

a=1;
window.a// 1

上面代码中,a是一个没有声明就直接赋值的变量,它自动成为顶层对象的属性。
window有自己的实体含义,其实不适合当作最高一层的顶层对象,这是一个语言的设计失误。最早,设计这门语言的时候,原始设想是语言内置的对象越少越好,这样可以提高浏览器的性能。因此,语言设计者 Brendan Eich 就把window对象当作顶层对象,所有未声明就赋值的变量都自动变成window对象的属性。这种设计使得编译阶段无法检测出未声明变量,但到了今天已经没有办法纠正了。
window.self**,window.window**
window.self和window.window属性都指向窗口本身。这两个属性只读。

window.self===window// true
window.window===window// true

window.frames**,window.length**
window.frames属性返回一个类似数组的对象,成员为页面内所有框架窗口,包括frame元素和iframe元素。window.frames[0]表示页面中第一个框架窗口。
如果iframe元素设置了id或name属性,那么就可以用属性值,引用这个iframe窗口。比如可以用frames[‘myIFrame’]或者frames.myIFrame来引用。
frames属性实际上是window对象的别名。

frames===window// true

因此,frames[0]也可以用window[0]表示。但是,从语义上看,frames更清晰,而且考虑到window还是全局对象,因此推荐表示多窗口时,总是使用frames[0]的写法。更多介绍请看下文的《多窗口操作》部分。
window.length属性返回当前网页包含的框架总数。如果当前网页不包含frame和iframe元素,那么window.length就返回0。
window.frames.length===window.length// true
上面代码表示,window.frames.length与window.length应该是相等的。
window.frameElement
window.frameElement属性主要用于当前窗口嵌在另一个网页的情况(嵌入、或元素),返回当前窗口所在的那个元素节点。如果当前窗口是顶层窗口,或者所嵌入的那个网页不是同源的,该属性返回null。

// HTML 代码如下
// <iframe src="about.html"></iframe>
// 下面的脚本在 about.html 里面
var frameEl=window.frameElement;
if(frameEl) {
frameEl.src='other.html';
}

上面代码中,frameEl变量就是元素。
window 对象的方法
window.alert(),window.prompt(),window.confirm()
window.alert()、window.prompt()、window.confirm()都是浏览器与用户互动的全局方法。它们会弹出不同的对话框,要求用户做出回应。注意,这三个方法弹出的对话框,都是浏览器统一规定的式样,无法定制。
1)window.alert()
window.alert()方法弹出的对话框,只有一个“确定”按钮,往往用来通知用户某些信息。

window.alert('Hello World');

在这里插入图片描述

用户只有点击“确定”按钮,对话框才会消失。对话框弹出期间,浏览器窗口处于冻结状态,如果不点“确定”按钮,用户什么也干不了。
window.alert()方法的参数只能是字符串,没法使用 CSS 样式,但是可以用\n指定换行。
alert(‘本条提示\n分成两行’);
2)window.prompt()
window.prompt()方法弹出的对话框,提示文字的下方,还有一个输入框,要求用户输入信息,并有“确定”和“取消”两个按钮。它往往用来获取用户输入的数据。

var result=prompt('您的年龄?', 25)

在这里插入图片描述

上面代码会跳出一个对话框,文字提示为“您的年龄?”,要求用户在对话框中输入自己的年龄(默认显示25)。用户填入的值,会作为返回值存入变量result。
window.prompt()的返回值有两种情况,可能是字符串(有可能是空字符串),也有可能是null。具体分成三种情况。
10. 用户输入信息,并点击“确定”,则用户输入的信息就是返回值。
11. 用户没有输入信息,直接点击“确定”,则输入框的默认值就是返回值。
12. 用户点击了“取消”(或者按了 ESC 按钮),则返回值是null。
window.prompt()方法的第二个参数是可选的,但是最好总是提供第二个参数,作为输入框的默认值。
3)window.confirm()
window.confirm()方法弹出的对话框,除了提示信息之外,只有“确定”和“取消”两个按钮,往往用来征询用户是否同意。

varresult=confirm('你最近好吗?');

在这里插入图片描述

上面代码弹出一个对话框,上面只有一行文字“你最近好吗?”,用户选择点击“确定”或“取消”。
confirm方法返回一个布尔值,如果用户点击“确定”,返回true;如果用户点击“取消”,则返回false。

var okay=confirm('Please confirm this message.');
if(okay) {
// 用户按下“确定”
} else{
// 用户按下“取消”
}
confirm的一个用途是,用户离开当前页面时,弹出一个对话框,问用户是否真的要离开。
window.onunload=function() {
returnwindow.confirm('你确定要离开当面页面吗?');
}

这三个方法都具有堵塞效应,一旦弹出对话框,整个页面就是暂停执行,等待用户做出反应。
Location 对象
Location对象是浏览器提供的原生对象,提供 URL 相关的信息和操作方法。通过window.location和document.location属性,可以拿到这个对象。
属性
Location对象提供以下属性。
o Location.href:整个 URL。
o Location.protocol:当前 URL 的协议,包括冒号(:)。
o Location.host:主机。如果端口不是协议默认的80和433,则还会包括冒号(:)和端口。
o Location.hostname:主机名,不包括端口。
o Location.port:端口号。
o Location.pathname:URL 的路径部分,从根路径/开始。
o Location.search:查询字符串部分,从问号?开始。
o Location.hash:片段字符串部分,从#开始。
o Location.username:域名前面的用户名。
o Location.password:域名前面的密码。
o Location.origin:URL 的协议、主机名和端口。

// 当前网址为
// http://user:passwd@www.example.com:4097/path/a.html?x=111#part1
document.location.href
// "http://user:passwd@www.example.com:4097/path/a.html?x=111#part1"
document.location.protocol
// "http:"
document.location.host
// "www.example.com:4097"
document.location.hostname
// "www.example.com"
document.location.port
// "4097"
document.location.pathname
// "/path/a.html"
document.location.search
// "?x=111"
document.location.hash
// "#part1"
document.location.username
// "user"
document.location.password
// "passwd"
document.location.origin
// "http://user:passwd@www.example.com:4097"
这些属性里面,只有origin属性是只读的,其他属性都可写。
注意,如果对Location.href写入新的 URL 地址,浏览器会立刻跳转到这个新地址。
// 跳转到新网址
document.location.href='http://www.example.com';
这个特性常常用于让网页自动滚动到新的锚点。
document.location.href='#top';
// 等同于
document.location.hash='#top';
直接改写location,相当于写入href属性。
document.location='http://www.example.com';
// 等同于
document.location.href='http://www.example.com';

另外,Location.href属性是浏览器唯一允许跨域写入的属性,即非同源的窗口可以改写另一个窗口(比如子窗口与父窗口)的Location.href属性,导致后者的网址跳转。Location的其他属性都不允许跨域写入。
方法
1)Location.assign()
assign方法接受一个 URL 字符串作为参数,使得浏览器立刻跳转到新的 URL。如果参数不是有效的 URL 字符串,则会报错。

// 跳转到新的网址
document.location.assign('http://www.example.com')

2)Location.replace()
replace方法接受一个 URL 字符串作为参数,使得浏览器立刻跳转到新的 URL。如果参数不是有效的 URL 字符串,则会报错。
它与assign方法的差异在于,replace会在浏览器的浏览历史History里面删除当前网址,也就是说,一旦使用了该方法,后退按钮就无法回到当前网页了,相当于在浏览历史里面,使用新的 URL 替换了老的 URL。它的一个应用是,当脚本发现当前是移动设备时,就立刻跳转到移动版网页。

// 跳转到新的网址
document.location.replace('http://www.example.com')

3)Location.reload()
reload方法使得浏览器重新加载当前网址,相当于按下浏览器的刷新按钮。
它接受一个布尔值作为参数。如果参数为true,浏览器将向服务器重新请求这个网页,并且重新加载后,网页将滚动到头部(即scrollTop === 0)。如果参数是false或为空,浏览器将从本地缓存重新加载该网页,并且重新加载后,网页的视口位置是重新加载前的位置。

// 向服务器重新请求当前网址
window.location.reload(true);

4)Location.toString()
toString方法返回整个 URL 字符串,相当于读取Location.href属性。
URL 的编码和解码
网页的 URL 只能包含合法的字符。合法字符分成两类。
o URL 元字符:分号(;),逗号(,),斜杠(/),问号(?),冒号(:),at(@),&,等号(=),加号(+),美元符号($),井号(#)
o 语义字符:a-z,A-Z,0-9,连词号(-),下划线(_),点(.),感叹号(!),波浪线(~),星号(*),单引号(’),圆括号(())
除了以上字符,其他字符出现在 URL 之中都必须转义,规则是根据操作系统的默认编码,将每个字节转为百分号(%)加上两个大写的十六进制字母。
比如,UTF-8 的操作系统上,http://www.example.com/q=春节这个 URL 之中,汉字“春节”不是 URL 的合法字符,所以被浏览器自动转成http://www.example.com/q=%E6%98%A5%E8%8A%82。其中,“春”转成了%E6%98%A5,“节”转成了%E8%8A%82。这是因为“春”和“节”的 UTF-8 编码分别是E6 98 A5和E8 8A 82,将每个字节前面加上百分号,就构成了 URL 编码。
JavaScript 提供四个 URL 的编码/解码方法。
o encodeURI()
o encodeURIComponent()
o decodeURI()
o decodeURIComponent()
encodeURI()
encodeURI()方法用于转码整个 URL。它的参数是一个字符串,代表整个 URL。它会将元字符和语义字符之外的字符,都进行转义。

encodeURI('http://www.example.com/q=春节')
// "http://www.example.com/q=%E6%98%A5%E8%8A%82"

在这里插入图片描述

encodeURIComponent()
encodeURIComponent()方法用于转码 URL 的组成部分,会转码除了语义字符之外的所有字符,即元字符也会被转码。所以,它不能用于转码整个 URL。它接受一个参数,就是 URL 的片段。

encodeURIComponent('春节')
// "%E6%98%A5%E8%8A%82"
encodeURIComponent('http://www.example.com/q=春节')
// "http%3A%2F%2Fwww.example.com%2Fq%3D%E6%98%A5%E8%8A%82"

上面代码中,encodeURIComponent()会连 URL 元字符一起转义,所以如果转码整个 URL 就会出错。
decodeURI()
decodeURI()方法用于整个 URL 的解码。它是encodeURI()方法的逆运算。它接受一个参数,就是转码后的 URL。

decodeURI('http://www.example.com/q=%E6%98%A5%E8%8A%82')
// "http://www.example.com/q=春节"

decodeURIComponent()
decodeURIComponent()用于URL 片段的解码。它是encodeURIComponent()方法的逆运算。它接受一个参数,就是转码后的 URL 片段。

decodeURIComponent('%E6%98%A5%E8%8A%82')
// "春节"

URL接口实例属性
URL 实例的属性与Location对象的属性基本一致,返回当前 URL 的信息。
o URL.href:返回整个 URL
o URL.protocol:返回协议,以冒号:结尾
o URL.hostname:返回域名
o URL.host:返回域名与端口,包含:号,默认的80和443端口会省略
o URL.port:返回端口
o URL.origin:返回协议、域名和端口
o URL.pathname:返回路径,以斜杠/开头
o URL.search:返回查询字符串,以问号?开头
o URL.searchParams:返回一个URLSearchParams实例,该属性是Location对象没有的
o URL.hash:返回片段识别符,以井号#开头
o URL.password:返回域名前面的密码
o URL.username:返回域名前面的用户名

varurl=newURL('http://user:passwd@www.example.com:4097/path/a.html?x=111#part1');
url.href
// "http://user:passwd@www.example.com:4097/path/a.html?x=111#part1"
url.protocol
// "http:"
url.hostname
// "www.example.com"
url.host
// "www.example.com:4097"
url.port
// "4097"
url.origin
// "http://www.example.com:4097"
url.pathname
// "/path/a.html"
url.search
// "?x=111"
url.searchParams
// URLSearchParams {}
url.hash
// "#part1"
url.password
// "passwd"
url.username
// "user"

这些属性里面,只有origin属性是只读的,其他属性都可写,并且会立即生效。

varurl=newURL('http://example.com/index.html#part1');
url.pathname='index2.html';
url.href// "http://example.com/index2.html#part1"
url.hash='#part2';
url.href// "http://example.com/index2.html#part2"

上面代码中,改变 URL 实例的pathname属性和hash属性,都会实时反映在 URL 实例当中。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值