JavaScript的学习
文章目录
2 JavaScript的基础语法
2.2 JavaScript中的几个概念
2.2.3 直接量
直接量(Literal)就是具体的值,即能够直接参与运算或显示的值,如字符串、数值、布尔值、正则表达式、对象直接量、数组直接量、函数直接量等
//空字符串直接量
1 //数值直接量
true //布尔值直接量
/a/g //正则表达式直接量
null //特殊值直接量
{} //空对象直接量
[] //空数组直接量
function(){} //空函数直接量,也就是函数表达式
2.2.4 转义序列
转义序列就是字符的一种表示方式(映射)。由于各种原因,很多字符无法直接在代码中输入或输出,只能通过转义序列间接表示
- Unicode 转义序列方法:\u + 4位十六进制数字。
- Latin-1 转义序列方法:\x + 2位十六进制数字。
//对于字符“©” , Unicode 转义为 \u00A9,ASCII 转义为 \xA9
document.write("\xa9"); //Latin-1 显示字符©
document.write("\u00a9"); //Unicode 显示字符©
2.2.5 标识符
标识符(Identifier) 就是名称的专业术语。JavaScript 标识符包括变量名、函数名、参数名和属性名
合法的标识符应该注意以下强制规则:
- 第一个字符必须是字母、下划线(_)或美元符号($)。
- 除了第一个字符外,其他位置可以使用 Unicode 字符。一般建议仅使用 ASCII 编码的字母,不建议使用双字节的字符。
- 不能与 JavaScript 关键字、保留字重名。
- 可以使用 Unicode 转义序列。例如,字符 a 可以使用“\u0061”表示 (不推荐)。
var \u0061 = "字符 a 的 Unicode 转义序列是 \u0061";
var a = "a";
使用转义序列不是很方便,一般常用转义序列表示特殊字符或名称,如 JavaScript 关键字、程序脚本等
2.2.6 关键字
关键字就是 ECMA-262 规定的 JavaScript 语言内部使用的一组名称(或称为命令)。这些名称具有特定的用途,用户不能自定义同名的标识符。具体说明如表所示
break | delete | if | this | while |
---|---|---|---|---|
case | do | in | throw | with |
catch | else | instanceof | try | |
continue | finally | new | typeof | |
debugger | for | return | var | |
default | function | switch | void |
2.2.7 保留字
保留字就是 ECMA-262 规定的 JavaScript 语言内部预备使用的一组名称(或称为命令)。这些名称目前还没有具体的用途,是为 JavaScript 升级版本预留备用的,建议用户不要使用。具体说明如表所示
abstract | double | goto | native | static |
---|---|---|---|---|
boolean | enum | implements | package | super |
byte | export | import | private | synchronized |
char | extends | int | protected | throws |
class | final | interface | public | transient |
const | float | long | short | volatile |
补充:ECMAScript 3 将 Java 所有关键字都列为保留字,而 ECMAScript 5 规定较为灵活例如,在非严格模式下,仅规定 class、const、enums、export、extends、import、super 为保留字,其他 ECMAScript 3 保留字可以自由使用;在严格模式下,ECMAScript 5 变得更加谨慎,严格限制 implements、interface、let、package、private、protected、public、static、yield、eval(非保留字)、arguments(非保留字)的使用
2.2.8 对象、属性和方法
JavaScript 预定义了很多全局变量和函数,用户也应该避免使用它们。具体说明如表所示
arguments | encodeURL | Infinity | Number | RegExp |
---|---|---|---|---|
Array | encodeURLComponent | isFinite | Object | String |
Boolean | Error | isNaN | parseFloat | SyntaxError |
Date | eval | JSON | parseInt | TypeError |
decodeURL | EvalError | Math | RangeError | undefined |
decodeURLComponent | Function | NaN | ReferenceError | URLError |
不同的 JavaScript 运行环境都会预定义一些全局变量和函数,上表列出的仅针对 Web 浏览器运行环境
无论是在严格模式下还是在非严格模式下,都不要在定义变量名、函数名或者属性名时使用上面列举出的保留字,以免同学们入坑
2.2.9 空白符(分隔符)
分隔符(空白符)就是各种不可见字符的集合,如空格(\u0020)、水平制表符(\u0009)、垂直制表符(\u000B)、换页符(\u000C)、不中断空白(\u00A0)、字节序标记(\uFEFF)、换行符(\u000A)、 回车符(\u000D)、行分隔符(\u2028)、段分隔符(\u2029)等.
在 JavaScript 中,分隔符不被解析,主要用来分隔各种记号,如标识符、关键字、直接量等信息。 在JavaScript 脚本中,常用分隔符来格式化代码,以方便阅读.
function toStr(a){return a.toString();}
可以使用分隔符格式化显示:
function toStr(a){
return a.toString();
}
一般 JavaScript 编辑器都会提供代码格式化的功能。
分隔符使用时需要注意以下几点:
- 分隔符虽然无实际意义,但是在脚本中却不能缺少。如果在标识符与关键字之间不使用分隔符分隔,JavaScript 就会抛出异常
functiontoStr(a){returna.toString();} //错误写法
function toStr(a){return a.toString();} //正确写法
- JavaScript 解析器一般采用最长行匹配原则,不恰当地换行显示一句代码,容易引发异常或错误
function toStr(a){
return
a.toString(); //错误的换行
}
document.write(toStr("abc")); //实际返回 undefined,应该返回"abc"
解析:这是因为 return 作为一条独立语句,JavaScript 解析器可以正确解析它,虽然它后面没有分号,解析器在正确解析的前提下会自动为其补加一个分号,以表示该句已经结束。这样换行显示的 a.toString();就是下一句待执行的命令,而不是被返回的值
- 不能在标识符、关键字等内部使用分隔符
function toStr(a){
return a.to String(); //错误分隔符
}
- 在字符串或者正则表达式内,分隔符是有意义的,不能够随意省略或替换
var a = "空格";
var b = "空格 ";
document.write((a==b)); //返回 false,说明不相同
2.2.10 注释
语言 | 注释 |
---|---|
HTML | |
CSS | /* 注释 */ |
javaScript | // 单行注释 /*多行注释 */ |
//程序描述
function toStr(a){ //块描述
//代码段描述
return a.toString(); //语句描述
}
/*
* jQuery JavaScript Library v3.3.1
* https://jquery.com/
* Includes Sizzle.js
* https://sizzlejs.com/
* Copyright JS Foundation and other contributors
* Released under the MIT license
* https://jquery.org/license
* Date: 2019-08-21 T 17:24 Z
*/
2.2.11 转义字符
转义字符是字符的一种间接表示方式。在特殊语境中,无法直接使用字符自身。例如,在字符串中包含说话内容。
alert(""子曰:"学而不思则罔,思而不学则殆。""");
由于 JavaScript 已经赋予了双引号为字符串直接量的标识符,如果在字符串中包含双引号,就必须使用转义字符表示
alert("子曰:\"学而不思则罔,思而不学则殆。\"");
JavaScript 定义反斜杠加上字符可以表示字符自身。注意,一些字符加上反斜杠后会表示特殊字符,而不是原字符本身,这些特殊转义字符被称为转义序列,具体说明如表所示
序列 | 代表字符 |
---|---|
\0 | Null字符(\u0000) |
\b | 退格符(\u0008) |
\t | 水平制表符(\u0009) |
\n | 换行符(\u000A) |
\v | 垂直制表符(\u000B) |
\f | 换页符(\u000C) |
\r | 回车符(\u000D) |
\" | 双引号(\u0022) |
\' | 撇号或单引号(\u0027) |
\\ | 反斜杠(\u005C) |
\xXX | 由 2 位十六进制数值 XX 指定的 Latin-1 字符 |
\uXXXX | 由 4 位十六进制数值 XXXX 指定的 Unicode 字符 |
\XXX | 由 1~3 位八进制数值(000 到 377)指定的 Latin-1 字符,可表示 256个 字符。如 \251 表示版本符号。注意,ECMAScript 3.0 不支持,考虑到兼容性不建议使用。 |
提示:
如果在一个正常字符前添加反斜杠,JavaScript 会忽略该反斜杠
alert("子曰:\"学\而\不\思\则\罔\, \思\而\不\学\则\殆\。\"");
//等价于
alert("子曰:\"学而不思则罔,思而不学则殆。\"");
2.3 变量
变量相当于容器,值相当于容器内装的东西,而变量名就是容器上贴着的标签,通过标签可以找到 变量,以便读、写它存储的值
2.3.1 声明变量
常用的方式为:
var 变量名 = 初始化值;
在一个 var 语句中,可以声明一个或多个变量,也可以为变量赋值,未赋值的变量初始化为undefined(未定义)值。当声明多个变量时,应使用逗号运算符分隔
var a; //声明一个变量
var a,b,c; //声明多个变量
var b = 1; //声明并赋值
alert(a); //返回 undefined
alert(b); //返回 1
可以重复声明同一个变量,也可以反复初始化变量的值
var a = 1;
var a = 2;
var a = 3;
alert(a); //返回 3
注意:
在非严格模式下,JavaScript 允许不声明变量就直接为其赋值,这是因为 JavaScript 解释器能够自动隐式声明变量。隐式声明的变量总是作为全局变量使用。在严格模式下,变量必须先声明,然后才能使用。
"use strict";// 严格模式
a = 3; // 报错 (a 未定义)
2.3.2 关于JS的弱类型(动态类型)
JavaScript 是弱类型语言,对于变量类型的规范比较松散。具体表现如下:
- 变量的类型分类不严谨、不明确,带来使用的随意性。
- 声明变量时,不要求指定类型。
- 使用过程不严格,可以根据需要自动转换变量类型。
- 变量的转换和类型检查没有一套统一、规范的方法,导致开发效率低下。
由此带来的优缺点如下:
- 优点:使用灵活,简化了代码编写。
- 缺点:执行效率低,在开发大型应用时,程序性能会受到影响。
简单说就是,同一变量可以接受不同的数据类型。
var a = 1; //数值类型
a = "1"; //字符串类型
2.3.3 字符和字符串类型的说明
JavaScript中只有字符串类型,没有字符类型,字符串既可以使用双引号,也可以使用单引号。
var a = "1";
var a = '1';
2.3.4 变量定义的特点
- var关键字不是必须的,可以省略,但不建议省略
- 变量名可以重复定义
2.3.5 变量作用域
在 JavaScript 中, 对象和函数同样也是变量,变量作用域为可访问变量,对象,函数的集合。
变量作用域(Scope)是指变量在程序中可以访问的有效范围,也称为变量的可见性。
JavaScript 变量可以分为全局变量和局部变量:
- 全局变量:不是声明在函数体内部的变量,变量在整个页面脚本中都是可见的,可以被自由访问。
- 局部变量:变量仅能在声明的函数内部可见,函数外是不允许访问的。只要函数运行完毕,变量就会被删除
2.3.5.1 局部变量
只能在函数内部访问
// 此处不能调用 carName 变量
function myFunction() {
var carName = "Volvo";
// 函数内可调用 carName 变量
}
alert(carName); //error: carName is not defined
因为局部变量只作用于函数内,所以不同的函数可以使用相同名称的变量。
局部变量在函数开始执行时创建,函数执行完后局部变量会自动销毁
2.3.5.2 全局变量
网页中所有脚本和函数均可使用
var carName = " Volvo";
// 此处可调用 carName 变量
function myFunction() {
// 函数内可调用 carName 变量
}
向未声明的 javascript 变量来分配值
如果您把值赋给尚未声明的变量,该变量将被自动作为全局变量声明。如:
ycdl = "云创动力"; //注:前面没有var
这样声明一个全局变量,哪怕这个变量是声明在函数内部它也是一个全局变量。
全局变量是 window 对象,所有数据变量都属于 window 对象
myFunction();
// 此处可以使用 windwo.carName
document.getElementById("demo").innerHTML =
"我可以显示 " + window.carName;
function myFunction()
{
carName = "Volvo"; //全局变量
}
2.3.6 生命周期
JavaScript 变量生命周期在它声明时初始化。
局部变量在函数执行完毕后销毁。
全局变量在页面关闭后销毁。
2.3.7 变量污染
javaScript 可以随意定义保存所有应用资源的全局变量。但全局变量可以削弱程序灵活性,增大了模块之间的耦合性。在多人协作时,如果定义过多的全局变量有可能造成全局变量冲突。
var x = 10;
// 这里输出 x 为 10
{
var x = 2;
// 这里输出 x 为 2
}
// 这里输出 x 为 2
解决方式:
- 定义全局变量命名空间
只创建一个全局变量,并定义该变量为当前应用容器,把其他全局变量追加在该命名空间下
var MyAPP = {}; //定义 APP 访问接口
MyAPP.name = { //定义APP配置变量
"id" : "应用程序的ID编号"
};
MyAPP.work = {
num : 123, //APP计数器等内部属性
sub : { name : "sub_id"}, //APP应用分支
doing : function(){ //具体方法
//执行代码
}
};
- 使用函数体封装应用程序,这是最常用的一种方法
(function(){
var MyAPP = {}; //定义 APP 访问接口
MyAPP.name = { //定义APP配置变量
"id" : "应用程序的ID编号"
};
MyAPP.work = {
num : 123, //APP计数器等内部属性
sub : { name : "sub_id"}, //APP 应用分支
doing : function(){ //具体方法
//执行代码
}
};
window.MyAPP = MyAPP; //对外开放应用程序接口
})();
window.MyAPP; //外部调用
2.3.8 let&const
ES2015(ES6) 新增加了两个重要的 JavaScript 关键字: let 和 const。
let 声明的变量只在 let 命令所在的代码块内有效。
const 声明一个只读的常量,一旦声明,常量的值就不能改变。
在 ES6 之前,JavaScript 只有两种作用域: 全局变量 与 函数内的局部变量
var name = "张三"; //全局变量
function myFun(){
var age = 11; // 局部变量
}
2.3.8.1 JavaScript 块级作用域(Block Scope)
使用 var 关键字声明的变量不具备块级作用域的特性,它在 {} 外依然能被访问到。
{
var a = 10;
}
// 可以访问到 a
在 ES6 之前,是没有块级作用域的概念的。
ES6 可以使用 let 关键字来实现块级作用域。
let 声明的变量只在 let 命令所在的代码块 {} 内有效,在 {} 之外不能访问
{
let x = 2;
}
// 这里不能使用 x 变量
使用let定义变量也可以解决变量污染问题
注意:
- 在函数体外或代码块外使用 var 和 let 关键字声明的变量,它们的作用域都是 全局的
- 在函数体内使用 var 和 let 关键字声明的变量,它们的作用域都是 局部的
- 使用let声明的变量不能再次被声明
2.3.8.2 const 关键字
const 用于声明一个或多个常量,声明时必须进行初始化,且初始化后值不可再修改
const PI = 3.141592653589793;
PI = 3.14; // 报错
PI = PI + 10; // 报错
const
定义常量与使用 let
定义的变量相似:
- 二者都是块级作用域
- 都不能和它所在作用域内的其他变量或函数拥有相同的名称
两者还有以下两点区别:
- const 声明的常量必须初始化,而 let 声明的变量不用
- const 定义常量的值不能通过再赋值修改,也不能再次声明。而 let 定义的变量值可以修改。
2.4 数据类型
在JavaScript中,数据类型可以分为原始类型和引用类型。
2.4.1 五种原始数据类型:
类型 | 说明 |
---|---|
Number | 数值型:整数和浮点数 |
Boolean | 布尔类型:true/false |
String | 字符串类型:包含字符和字符串 |
Null | 只有一个值null |
Undefined | 未定义/变量未初始化时的类型,只有一个值undefined |
注: Symbol 是 ES6 引入了一种新的原始数据类型,表示独一无二的值
2.4.2 typeof操作符
- 作用:判断指定的变量数据类型
- 写法:
typeof(变量名) 或 typeof 变量名
- null 与 undefined 的区别:
null: 是一个object类型,但没有值,可以认为是引用类型的占位符
undefined:未初始化的类型,不知道是什么类型
2.4.3 示例
<script type="text/javascript">
var i = 5;
document.write("整数:" + typeof(i) + "<br/>");
var f = 3.14;
document.write("浮点数:" + typeof(f) + "<br/>");
var str = "abc";
document.write("字符串:" + typeof(str) + "<br/>");
var c = 'a';
document.write("字符:" + typeof(c) + "<br/>");
var b = true;
document.write("布尔类型:" + typeof(b) + "<br/>");
var u;
document.write("未定义的类型:" + typeof(u) + "<br/>");
var n = null;
document.write("null:" + typeof(n) + "<br/>");
</script>
2.4.4 引用类型
引用类型有:对象(Object)、数组(Array)、函数(Function)
2.5 常用运算符
2.5.1 算术运算符
算术运算符用于执行两个变量或值的运算。
以下表格将向你说明算术运算符的使用: y = 5
运算符 | 描述 | 例子 | y值 | x值 |
---|---|---|---|---|
+ | 加法 | x = y + 2 | 5 | 7 |
- | 减法 | x = y - 2 | 5 | 3 |
* | 乘法 | x = y * 2 | 5 | 10 |
/ | 除法 | x = y / 2 | 5 | 2.5 |
% | 余数 | x = y % 2 | 5 | 1 |
++ | 自增 | x = ++ y | 6 | 6 |
x = y ++ | 6 | 5 | ||
– | 自减 | x = - - y | 4 | 4 |
x = y - - | 4 | 5 |
任何类型的数据都可以使用算数运算符参与运算
var a = 10;
var b = false;
document.write(a + b);
2.5.2 赋值运算符
赋值运算符用于给 javascript 变量赋值。
下面的表格解释了赋值运算符: x = 10; y = 5
运算符 | 例子 | 类似于 | x值 |
---|---|---|---|
= | x = y | x = y | 5 |
+= | x += y | x = x + y | 15 |
-= | x -= y | x = x - y | 5 |
*= | x *= y | x = x * y | 50 |
/= | x /= y | x = x / y | 2 |
%= | x %= y | x = x % y | 0 |
2.5.3 比较运算符
比较运算符用于逻辑语句的判断,从而确定给定的两个值或变量是否相等。
下表展示了比较运算符的使用: x = 5
运算符 | 描述 | 例子 | 结果 |
---|---|---|---|
== | 等于 | x == 8 | false |
x == 5 | true | ||
=== | 值及类型均相等(恒等于) | x === “5” | false |
x === 5 | true | ||
!= | 不等于 | x != 8 | true |
!== | 值与类型均不等(不恒等于) | x !== “5” | true |
x !== 5 | false | ||
> | 大于 | x > 8 | false |
< | 小于 | x < 8 | true |
>= | 大于或等于 | x >= 8 | false |
<= | 小于或等于 | x <= 8 | true |
数字可以与字符串进行比较,字符串可以与字符串进行比较。字符串与数字进行比较的时候会先把字符串转换成数字然后再进行比较
var a = 125;
var b = "123";
document.write("字符串与数字比较的结果:"+ (a>b)+"<br/>");
2.5.4 逻辑运算符
逻辑运算符用来确定变量或值之间的逻辑关系。
下表展示了逻辑运算符的使用: x = 6 , y = 3
运算符 | 描述 | 例子 |
---|---|---|
&& | 与 | (x < 10 && y > 1) 为 true |
|| | 或 | (x == 5 | | y == 5) 为 false |
! | 非 | !(x == y) 为 true |
逻辑运算符不存在单与&、单或|
2.5.5 三目(元)运算符
var age = 24;
document.write("是成年人吗?"+ (age >= 18 ? "是":"不是")+"\<br/>");
2.6 流程控制语句
高级语言中的三种基本结构:顺序、分支、循环
2.6.1 if 判断
if 语句
在一个指定的条件成立时执行代码。
if(条件表达式) {
//代码块;
}
if…else 语句
在指定的条件成立时执行代码,当条件不成立时执行另外的代码。
if(条件表达式) {
//代码块;
}else {
//代码块;
}
if…else if…else 语句
使用这个语句可以选择执行若干块代码中的一个。
if (条件表达式) {
//代码块;
}else if(条件表达式) {
//代码块;
}else {
//代码块;
}
条件判断可以使用非逻辑运算符
数据类型 | true | false |
---|---|---|
number | 非0 | 0 |
string | 非空串 | 空串 |
undefined | false | |
NaN(Not a Number) | false | |
object | 非null | null |
2.6.2 循环
for 语句
循环指定次数
for (var i=0; i<10; i++) {
//需要执行的代码;
}
while语句:
当指定的条件为 true 时循环执行代码
while (条件表达式) {
// 需要执行的代码;
}
do-while语句:
最少执行1次循环
do {
// 需要执行的代码;
}
while (条件表达式)
break和continue
- break: 跳出整个循环
- continue:跳出本次循环
2.7 在浏览器中的调试
IE、Chrome、FireFox中调试的快捷键:F12
2.7.1 设置断点
2.7.2 语法错误
如果有语法错误,浏览器会出现提示,出现错误后,下面代码将不会被执行
练习:
使用js输出乘法口诀表
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
span{
margin: 10px;
}
</style>
</head>
<body>
<script type="text/javascript">
for(var i=1;i<=9;i++){
for(var j=1;j<=i;j++){
document.write("<span>"+i+" * "+j+" = "+i*j+" </span>");
}
document.write("<br />")
}
</script>
</body>
</html>
2.8 函数
JavaScript 使用关键字 function 定义函数。
函数可以通过声明定义,也可以是一个表达式。
通俗的讲,可以将函数理解为 用一堆JavaScript代码,来完成某个功能
2.8.1 函数的声明
一个函数的声明(或者说是函数的定义)包含:
- 关键字function
- 函数名称
- 参数列表,包围在括号中并由逗号分隔
- 定义函数的 JavaScript 语句,用大括号 {} 括起来
function functionName(parameters){
// 执行的代码
}
例如:定义一个 square
函数:
function square(number) {
return number * number;
}
函数 square
使用了一个参数,叫作 number
。这个函数只有一个语句,它说明该函数将函数的参数(即 number
)自乘后返回。函数的 return
语句确定了函数的返回值。
函数声明后不会立即执行,会在我们需要的时候调用。
原始参数(比如一个具体的数字)被作为值传递给函数;值被传递给函数,如果被调用函数改变了这个参数的值,这样的改变不会影响到全局或调用函数。
var num = 10;
var r1 = square(10); // 调用函数
num = 11;
如果你传递一个对象(即一个非原始值,例如 Array
或用户自定义的对象)作为参数,而函数改变了这个对象的属性,这样的改变对函数外部是可见的,如下面的例子所示:
function myFunc(theObject) {
theObject.make = "Toyota";
}
var mycar = {make: "Honda", model: "Accord", year: 1998};
var x, y;
x = mycar.make; // x获取的值为 "Honda"
myFunc(mycar);
y = mycar.make; // y获取的值为 "Toyota"
// (make属性被函数改变了)
分号是用来分隔可执行JavaScript语句。
由于函数声明不是一个可执行语句,所以不以分号结束
2.8.2 函数表达式
JavaScript 函数可以通过一个表达式定义。
函数表达式可以存储在变量中:
var square = function (number){ return number * number };
在函数表达式存储在变量后,变量也可作为一个函数使用:
var s = square(2);
以上函数实际上是一个 匿名函数 (函数没有名称)。
函数存储在变量中,不需要函数名称,通常通过变量名来调用。
上述函数以分号结尾,因为它是一个执行语句
2.8.3 Function() 构造函数
通过前边的内容我们学习了函数通过关键字 function 来定义。
函数同样可以通过内置的 JavaScript 函数构造器(Function())定义
new Function ([arg1[, arg2[, ...argN]],] functionBody)
- arg1, arg2, … argN
参数名称是一个有效的JavaScript标识符的字符串,或者一个用逗号分隔的有效字符串的列表;例如“×
”,“theValue
”,或“a,b
” - functionBody
一个含有包括函数定义的 JavaScript 语句的字符串
var sum = new Function("square", "return number * number");
上面的声明方式和我们之前函数表达式中声明的函数是一样的。
实际上,很多时候你不用使用构造函数来声明函数。而且要避免使用 new 关键字
使用Function
构造器生成的Function
对象是在函数创建时解析的。这比你使用函数声明或者函数表达式并在你的代码中调用更为低效,因为使用后者创建的函数是跟其他代码一起解析的
当一个函数是一个对象的属性时,称之为方法
2.8.4 调用函数
定义一个函数并不会自动的执行它。定义了函数仅仅是赋予函数以名称并明确函数被调用时该做些什么。调用函数才会以给定的参数真正执行这些动作。例如,一旦你定义了函数 square ,你可以如下这样调用它:
square(2)
上述语句通过提供参数 2 来调用函数。函数执行完它的语句会返回值 4
函数一定要处于调用它们的域(范围)中,但是函数的声明可以被提升(出现在调用语句之后)
提升(Hoisting)是 JavaScript 默认将当前作用域提升到前面去的的行为
console.log(square(5)); // 调用
function square(n) { return n*n } // 声明
函数域是指函数声明时的所在的地方,或者函数在顶层被声明时指整个程序
注意只有使用如上的语法形式(即
function funcName(){}
)才可以。而下面的代码是无效的。就是说,函数提升仅适用于函数声明,而不适用于函数表达式。
console.log(square); // square is hoisted with an initial value undefined.
console.log(square(2)); // TypeError: square is not a function
var square = function (n) {
return n * n;
}
函数可以被递归,就是说函数可以调用其本身。例如,下面这个函数就是用递归计算阶乘:
function factorial(n){
if ((n == 0) || (n == 1))
return 1;
else
return (n * factorial(n - 1));
}
2.8.5 函数作用域
在函数内定义的变量不能在函数之外的任何地方访问,因为变量仅仅在该函数的域的内部有定义。相对应的,一个函数可以访问定义在其范围内的任何变量和函数。换言之,定义在全局域中的函数可以访问所有定义在全局域中的变量。在另一个函数中定义的函数也可以访问在其父函数中定义的所有变量和父函数有权访问的任何其他变量。
// 下面的变量定义在全局作用域(global scope)中
var num1 = 20,
num2 = 3,
name = "Chamahk";
// 本函数定义在全局作用域
function multiply() {
return num1 * num2;
}
multiply(); // 返回 60
// 嵌套函数的例子
function getScore() {
var num1 = 2,
num2 = 3;
function add() {
return name + " scored " + (num1 + num2);
}
return add();
}
getScore(); // 返回 "Chamahk scored 5"
2.8.6 嵌套函数和闭包
你可以在一个函数里面嵌套另外一个函数。嵌套(内部)函数对其容器(外部)函数是私有的。它自身也形成了一个闭包。一个闭包是一个可以自己拥有独立的环境与变量的表达式(通常是函数)。
既然嵌套函数是一个闭包,就意味着一个嵌套函数可以”继承“容器函数的参数和变量。换句话说,内部函数包含外部函数的作用域。
可以总结如下:
- 内部函数只可以在外部函数中访问。
- 内部函数形成了一个闭包:它可以访问外部函数的参数和变量,但是外部函数却不能使用它的参数
和变量。
function addSquares(a, b) {
function square(x) {
return x * x;
}
return square(a) + square(b);
}
a = addSquares(2, 3); // returns 13
b = addSquares(3, 4); // returns 25
c = addSquares(4, 5); // returns 41
由于内部函数形成了闭包,因此你可以调用外部函数并为外部函数和内部函数指定参数:
function outside(x) {
function inside(y) {
return x + y;
}
return inside;
}
fn_inside = outside(3); // 可以这样想:给一个函数,使它的值加3
result = fn_inside(5); // returns 8
result1 = outside(3)(5); // returns 8
2.8.6.1 闭包
闭包是 JavaScript 中最强大的特性之一。JavaScript 允许函数嵌套,并且内部函数可以访问定义在外部函数中的所有变量和函数,以及外部函数能访问的所有变量和函数。
但是,外部函数却不能够访问定义在内部函数中的变量和函数。这给内部函数的变量提供了一定的安全性。
2.8.7 函数参数
从ECMAScript 6开始,有两个新的类型的参数:默认参数,剩余参数
2.8.7.1 默认参数
在JavaScript中,函数参数的默认值是 undefined
。然而,在某些情况下设置不同的默认值是有用的。这时默认参数可以提供帮助。
在过去,用于设定默认参数的一般策略是在函数的主体中测试参数值是否为 undefined
,如果是则赋予这个参数一个默认值。如果在下面的例子中,调用函数时没有实参传递给 b
,那么它的值就是undefined
,于是计算 a*b
得到、函数返回的是 NaN
。但是,在下面的例子中,这个已经被第二行获取处理:
function multiply(a, b) {
b = (typeof b !== 'undefined') ? b : 1;
return a*b;
}
multiply(5); // 5
使用默认参数,在函数体的检查就不再需要了。现在,你可以在函数头简单地把1设定为 b
的默认值:
function multiply(a, b = 1) {
return a*b;
}
multiply(5); // 5
2.8.7.2 剩余参数
剩余参数语法允许将不确定数量的参数表示为数组。在下面的例子中,使用剩余参数收集从第二个到最后参数。然后,我们将这个数组的每一个数与第一个参数相乘。这个例子是使用了一个箭头函数。
function multiply(multiplier, ...theArgs) {
return theArgs.map(x => multiplier * x);
}
var arr = multiply(2, 1, 2, 3);
console.log(arr); // [2, 4, 6]
注意的事项
- 形参的类型:在函数定义的时候不用指定类型,因为是可变类型
- 函数的返回值:如果一个函数中需要返回值,直接使用return返回,如果没有返回值,不写return。
- 关于函数的重载:在JS中没有函数的重载,同名的函数会覆盖原来的函数,调用的时候,只会调用最后声明的函数,而且实参的个数与形参数的个数没有关系。
- 所有函数的内部都有一个类数组对象,名字叫:arguments,用来接收调用时提交的所有的参数。
演示:定义一个函数,在函数的内部输出arguments的长度和其中的每个元素。
<script type="text/JavaScript">
function sum (a,b) {
//在函数的内部输出arguments的长度和其中的每个元素
document.write("arguments数组的长度:" + arguments.length + "<hr/>");
//输出每个元素
for (var i = 0; i < arguments.length; i++) {
document.write(arguments[i] + "<br/>");
}
document.write("a=" + a + "<br />");
document.write("b=" + b + "<br />");
}
//调用
sum(3,10,8);
//sum(3);
</script>
2.8.8 匿名函数
语法
var 变量名 = function(参数列表) {
函数体;
}
函数调用:
//匿名函数
var sayHi = function(name) {
alert("Hello, " + name);
};
//调用
sayHi("Tom");
2.8.9 箭头函数
箭头函数表达式(也称胖箭头函数)相比函数表达式具有较短的语法并以词法的方式绑定 this
。箭头函数总是匿名的.
有两个因素会影响引入箭头函数:更简洁的函数和 this
2.8.9.1 更简洁的函数
var a = [
"Hydrogen",
"Helium",
"Lithium",
"Beryllium"
];
var a2 = a.map(function(s){ return s.length });
console.log(a2); // logs [ 8, 6, 7, 9 ]
var a3 = a.map( s => s.length );
console.log(a3); // logs [ 8, 6, 7, 9 ]
map() 方法创建一个新数组,其结果是该数组中的每个元素都调用一次提供的函数后的返回值
2.8.9.2 this
的词法
在箭头函数出现之前,每一个新函数都重新定义了自己的 this 值(在构造函数中是一个新的对象;在严格模式下是未定义的;在作为“对象方法”调用的函数中指向这个对象;等等)。以面向对象的编程风格,这样着实有点恼人
function Person() {
// 构造函数Person()将`this`定义为自身
this.age = 0;
setInterval(function growUp() {
// 在非严格模式下,growUp()函数将`this`定义为“全局对象”,
// 这与Person()定义的`this`不同,
// 所以下面的语句不会起到预期的效果。
this.age++;
}, 1000);
}
var p = new Person();
在ECMAScript 3/5里,通过把 this
的值赋值给一个变量可以修复这个问题
function Person() {
var self = this; // 有的人习惯用`that`而不是`self`,
// 无论你选择哪一种方式,请保持前后代码的一致性
self.age = 0;
setInterval(function growUp() {
// 以下语句可以实现预期的功能
self.age++;
}, 1000);
}
箭头函数捕捉闭包上下文的 this
值,所以下面的代码工作正常
function Person(){
this.age = 0;
setInterval(() => {
this.age++; // 这里的`this`正确地指向person对象
}, 1000);
}
var p = new Person();
2.8.10 预定义函数
JavaScript语言有好些个顶级的内建函数:
函数 | 用法 |
---|---|
isNaN() | isNaN() 函数判断一个值是否是 NaN (Not-A-Number) |
parseFloat() | 解析字符串参数,并返回一个浮点数 |
parseInt() | 解析字符串参数,并返回指定的基数(基础数学中的数制)的整数 |
encodeURI() | 通过用以一个,两个,三个或四个转义序列表示字符的UTF-8编码替换统一资源标识符(URI)的某些字符来进行编码(每个字符对应四个转义序列,这四个序列组了两个”替代“字符) |
decodeURI() | 对先前经过 encodeURI 函数或者其他类似方法编码过的字符串进行解码 |
NaN值
的产生:
当算术运算返回一个未定义的或无法表示的值时,NaN
就产生了。但是,NaN
并不一定用于表示某些值超出表示范围的情况。将某些不能强制转换为数值的非数值转换为数值的时候,也会得到NaN
。
例如,0 除以0会返回NaN
—— 但是其他数除以0则不会返回NaN
。
2.9 js实现图片切换
实现每过3秒中切换一张图片的效果,一共5张图片,当显示到最后1张的时候,再次显示第1张。
步骤:
- 创建HTML页面,页面中有一个div标签,div标签内包含一个img标签。
- body的背景色为黑色;div的类样式为container:设置为居中,加边框,宽度为850px;img的id为pic,宽度850px;
- 五张图片的名字依次是0~4.jpg,放在项目的img文件夹下,图片一开始的src为第0张图片。
- 编写函数:changePicture(),使用setInterval()函数,每过3秒调用一次。
- 定义全局变量:num=1。
- 在changePicture()方法中,设置图片的src属性为img/num.jpg。
- 判断num是否等于4,如果等于4,则num=0;否则num++。
代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>图片切换</title>
<style type="text/css">
.container {
/*居中*/
margin: auto;
border: 1px solid black;
width: 850px;
}
img {
width: 850px;
}
</style>
</head>
<body>
<div class="container">
<img src="img/0.jpg" id="pic">
</div>
<script>
//全局变量
var num = 1;
//获取图片对象
var picObj = document.getElementById("pic");
//改变图片的src属性
function changePicture() {
//得到图片的src属性,替换它的值
picObj.src = "img/" + num + ".jpg";
//如果图片到了第4张图片,则重新变成第1张,否则就加1
if (num == 4) {
num = 0;
} else {
num++;
}
}
//每过3秒调用一次
setInterval("changePicture()", 3000);
</script>
</body>
</html>
3 实战
3.1 与对话框有关的方法
方法 | 作用 |
---|---|
alert() | 弹出一个确认按钮的信息框 |
string prompt(“提示信息”,"默认值”) | 弹出一个输入信息框,返回字符串类型 |
boolean confirm(“提示信息”) | 弹出一个信息框,有确定和取消按钮。如果点确定,返回true,点取消返回false |
console | 向控制台输出,有log,warn,error等类型 |
3.2 与计时有关的方法
方法 | 作用 |
---|---|
setTimeout(函数名, 间隔毫秒数) | 在指定的时间后调用1次函数,只执行1次,单位是毫秒。返回值:返回一个整数类型的计时器 函数调用有两种写法: 1) setTimeout(“函数名(参数)”,1000); 2) setTimeout(函数名,1000, 参数); 注意方式二:没有引号,没有括号 |
setInterval(函数名, 间隔毫秒数) | 每过指定的时间调用1次函数,不停的调用函数,单位是毫秒。 返回值:返回一个整数类型的计时器。 |
clearInterval(计时器) | 清除setInterval()方法创建的计时器 |
clearTimeout(计时器) | 清除setTimeout()方法创建的计时器 |
练习:
做一个网页时钟,每秒都在变化
3.3 查找节点的方法
方法 | 作用 |
---|---|
document.getElementById(“id”) | 通过id属性得到唯一的元素 如果页面上有多个同名的id,则得到第1个元素 |
document.getElementsByName(“name”) | 通过name属性得到一组标签,返回一个数组 |
document.getElementsByTagName (“标签名”) | 通过标签名字得到一组标签,返回一个数组 |
document.getElementsByClassName(“class名”) | 通过类名得到一组标签,返回一个数组 |
document.querySelector(CSS 选择器) | 返回文档中匹配指定 CSS 选择器的一个元素 |
document.querySelectorAll(CSS 选择器) | 返回文档中匹配指定 CSS 选择器的所有元素,是一个数组 |
数组是节点集合
3.4 字符串对象
String对象是JavaScript中最常见的对象之一。用户在表单中输入的数据默认都是String类型。
JavaScript中,单引号和双引号引起来的都是字符串。
JavaScript对String类型的数据提供了丰富的操作API。下表是String类常用的方法,字符串以“I Love This Game!!!”为例:
方法 | 作用 |
---|---|
charAt(pos) | 查找指定下标的字符。例如:charAt(5),返回“e” 等同于: str[5] |
indexOf | 查找匹配字符串第一次出现的位置 |
charCodeAt() | 返回指定索引位置字符的 Unicode 值 |
fromCharCode() | 将 Unicode 转换为字符串 |
match() | 找到一个或多个正则表达式的匹配 |
split() | 把字符串分割为子字符串数组 |
substr() | 从起始索引号提取字符串中指定数目的字符 |
substring() | 提取字符串中两个指定的索引号之间的字符 |
let str = "I Like That Girl !!!";
// charAt(n) 寻找字符串中下标为 n 的字符
// 如果n 小于0 或 大于 字符串的长度,返回空串
let c = str.charAt(5);
// indexOf() 寻找字符串第一次出现的位置,对大小写敏感
// 如果没有找到,返回 -1
let pos = str.indexOf("i");
// split() 将字符串分隔成字符串数组
let arr = str.split(" ");
// substr(n) 从 n 开始截取字符串,包含 n
// 如果 n 大于字符串的长度,返回空串
// 如果 n 小于 0,反向截取字符
let sub = str.substr(6);
// substring(s, e) 从字符串下标 s 开始截取到下标 e
// 包含下标为 s 的字符,不包含下标为 e 的字符(包左不包右)
let sub1 = str.substring(0, 6);
// 易错点
// 反斜杠\表示转义, 如 \n 表示换行 \t 制表符
let s = "a\\b\\c\\e\\f";
let warn = s.split("\\");
console.log(warn);
4 javascript的事件
4.1 事件的作用
我们刚才的案例中的script标签在不修改代码的基础上,就必须放在body标签之后 ; 原因是因为必须在页面加载完成之后,我们才可以获取到图片标签 ; 若想把script标签放在body标签之前,这里可以通过添加页面加载成功事件来处理。
事件是可以被 javascript 侦测到的行为(类似监听器)。
网页中的每个元素都可以产生某些可以触发 javascript 函数的事件。例如 : 页面加载成功的之后我们来触发某个函数,又如我们可以在用户点击某个标签时产生一个单击事件来触发某个函数。
注意:事件通常要与函数配合使用,当事件发生时函数才会执行。
4.2 事件的注册方式
4.2.1 使用命名函数
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<input type="button" id="b1" value="点我" onclick="output()"/>
<script type="text/JavaScript">
function output() {
alert("我是按钮,被点击了");
}
</script>
</body>
</html>
4.2.2 使用匿名函数
<input type="button" id="b2" value="再点我" />
<script type="text/JavaScript">
//匿名函数的写法
document.getElementById("b2").onclick = function () {
alert("我是按钮2,也被点击了");
}
</script>
4.3 常见的事件
属性 | 描述 |
---|---|
onblur | 元素失去焦点 |
onfocus | 元素获得焦点 |
onchange | 用户改变域的内容 |
onclick | 鼠标点击某个对象 |
ondblclick | 鼠标双击某个对象 |
onkeydown | 某个键盘的键被按下 |
onkeyup | 某个键盘的键被松开 |
onload | 某个页面或图像被完成加载 |
onmousedown | 某个鼠标按键被按下 |
onmouseout | 鼠标从某元素移开 |
onmouseover | 鼠标被移到某元素之上 |
onmouseup | 某个鼠标按键被松开 |
onsubmit | 提交按钮被点击 |
4.4 演示
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>事件演示</title>
</head>
<body onload="ready()">
<input type="button" value="点我啊" onclick="testClick()" /><br /><br />
<!-- this代表了当前的标签对象 -->
<img src="img/1.jpg" width="350px" ondblclick="changeImage(this)" /><br /><br />
<input type="text" value="请输入用户名..." onfocus="clearText(this)" onblur="setData(this)" />
<hr />
省份:
<select onchange="changeTest(this)">
<option value="bj">北京</option>
<option value="sh">上海</option>
<option value="sc">四川</option>
<option value="sx">陕西</option>
</select> <br />
<!-- 如果该表单需要根据触发函数的返回值决定是否可以提交,那么必须在触发方法之前加上return关键字-->
<form action="success.html" onsubmit=" return submitTest()">
用户名:<input type="text" name="userName" />
<input type="submit" value="提交" />
</form>
<script type="text/JavaScript">
//单击事件
function testClick() {
alert("哎呀被点了..");
}
//双击事件
function changeImage(imgObj) {
imgObj.src = "img/2.jpg";
}
//获取焦点
function clearText(inputObj) {
inputObj.value = "";
}
//失去焦点
function setData(inputObj) {
inputObj.value = "请输入用户名...";
}
//下拉框内容发生变化的时候会触发
function changeTest(selectObj) {
alert("当前改变后内容是:" + selectObj.value);
}
//表单提交的时候触发的方法
function submitTest() {
//如果表单提交的时候触发的方法返回的是false,那么该表单不允许提交,返回true才允许提交。
alert("表单马上要提交了..");
}
//加载事件
function ready() {
alert("页面的元素已经被加载完毕了..");
}
</script>
</body>
</html>
5. JavaScript的内置对象
5.1 数组对象
数组在JS中是一个类,通过构造方法创建对象。
5.1.1 数组的四种方式
方式 | 说明 |
---|---|
new Array() | 无参的构造方法,创建一个长度为0的数组 |
new Array(5) | 有参的构造方法,指定数组的长度 |
new Array(2,4,10,6,41) | 有参的构造方法,指定数组中的每个元素 |
[4,3,20,6] | 使用中括号的方式创建数组 |
5.1.2 JS中数组的特点
- 数组中的每个元素的类型是可以不同的。
- 数组的长度可以动态变化
- 数组中包含大量的方法,类似于Java中的集合,而Java中的数组没有方法。
示例
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>数组</title>
</head>
<body>
<script>
//1. 创建一个长度为0的数组
var arr = new Array();
//2. 有参的构造方法,指定数组的长度
var arr = new Array(5);
//3. 有参的构造方法,指定数组中的每个元素
var arr = new Array(2, 4, 10, 6);
//4. 使用中括号的方式创建数组
var arr = [4, 3, 20, 6];
//创建一个数组,每个元素都不相同
var arr = [4, 'a', true, 3.14];
arr[3] = 100;
arr[5] = 99;
arr[7] = true;
document.write("数组的长度是:" + arr.length + "<hr/>");
//输出每个元素
for (var i = 0; i < arr.length; i++) {
document.write(arr[i] + " ");
}
</script>
</body>
</html>
数组中没有设置的值是 undefined
5.1.3 常用方法
方法 | 作用 |
---|---|
concat() | 连接两个或更多的数组,并返回结果 |
reverse() | 将数组进行反转 |
join(separator) | 与split()功能相反,将数组通过分隔符,拼成一个字符串。 |
sort() | 对字符串数组进行排序 如果要对数字进行排序,还可以指定比较器函数。 sort(function(m,n)) 数字两两比较 注意:m-n则升序,n-m则降序 |
pop() | 删除数组最后一个元素 |
push() | 向数组最后加一个元素 |
5.1.4 示例
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>数组</title>
</head>
<body>
<script>
document.write("<hr />");
var a1 = [1, 1, 1];
var a2 = [2, 2];
//拼接,返回新的数组
var a3 = a1.concat(a2);
document.write("a3: " + a3 + "<br/>");
//添加元素
var a4 = a3.concat(33, 44);
document.write("a4: " + a4 + "<br />");
//反转
a4.reverse();
document.write("a4: " + a4 + "<br />");
//将数组使用分隔符拼成一个字符串,功能上与split相反
var str = a4.join("^_^");
document.write("字符串:" + str + "<br/>");
//排序
//a) . 给字符串数组排序
var arr = ['jack', 'Rose', "Tom", "Jerry", "Kate"];
document.write("排序前:" + arr + "<hr />");
arr.sort();
document.write("排序后:" + arr + "<hr />");
//b). 字符串类型的数字排序
var arr = ["200", "3", "1234", "89", "21"];
document.write("排序前:" + arr + "<hr />");
arr.sort();
document.write("排序后:" + arr + "<hr />");
//c). 数字排序,默认也是按字符串的字典顺序排序
var arr = [30, 26, 6, 110, 1234];
document.write("排序前:" + arr + "<hr />");
//排序器
arr.sort(function(m, n) {
return n - m;
});
document.write("排序后:" + arr + "<hr />");
//数组长度
document.write("数组长度是:" + arr.length + "<br />");
arr.pop();
document.write("删除最后一个元素后" + arr + "<br />");
arr.push("Adam")
document.write("末尾添加一个元素后" + arr);
</script>
</body>
</html>
5.2 日期对象
5.2.1 日期对象的创建
作用:Date 对象用于处理日期和时间。
创建 Date 对象的语法:
var myDate = new Date()
注:Date 对象会自动把当前日期和时间保存为其初始值
5.2.2 日期对象的方法
方法名 | 作用 |
---|---|
getFullYear() | 从 Date 对象以四位数字返回年份 |
getMonth() | 从 Date 对象返回月份 (0 ~ 11) |
getDate() | 从 Date 对象返回一个月中的某一天 (1 ~ 31) |
getDay() | 从 Date 对象返回一周中的某一天 (0 ~ 6)。其中:0表示周日,1~6周一到周六 |
getHours() | 返回 Date 对象的小时 (0 ~ 23) |
getMinutes() | 返回 Date 对象的分钟 (0 ~ 59) |
getSeconds() | 返回 Date 对象的秒数 (0 ~ 59) |
getMilliseconds() | 返回 Date 对象的毫秒(0 ~ 999) |
getTime() | 返回 1970 年 1 月 1 日至今的毫秒数。类似于Java中的System.currentTimeMillis() |
toLocaleString() | 根据本地时间格式,把 Date 对象转换为字符串 |
5.2.3 示例
在浏览器上输出现在的时间
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>日期</title>
</head>
<body>
<script>
document.write("现在时间是:" + new Date() + "<hr />")
document.write("现在时间是:" + new Date().toLocaleString() + "<hr />")
</script>
</body>
</html>
5.3 全局对象
全局属性和函数可用于所有内建的 javascript 对象
5.3.1 字符串转换成数字类型
函数 | 作用 |
---|---|
parseInt() | 将一个字符串转成整数,如果一个字符串包含非数字字符,那么parseInt函数会从首字母开始取数字字符,一旦发现非数字字符,马上停止获取内容。 |
parseFloat() | 将一个字符串转成小数,转换原理同上。 |
isNaN() | 转换前判断被转换的字符串是否是一个数字,非数字返回true |
示例
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<script>
var a = "123abc123"; //字符串类型
var i = parseInt(a);
document.write(i + "<br/>");
var b = "3.14abc123";
i = parseFloat(b);
document.write(i + "<br />");
//判断字符串是否为纯数字字符组成
var age = "1012";
document.write(isNaN(age)); //不是一个数字字符, 返回true.
</script>
</body>
</html>
5.3.2 编码解码函数
函数名称 | 作用 |
---|---|
encodeURI(URIstring) | 可把字符串作为 URI 进行编码 |
decodeURI(URIstring) | 可对 encodeURI() 函数编码过的 URI 进行解码。 |
示例
<script>
document.write("<hr/>");
var str = "http://www.baidu.com?wd=坚持就是胜利";
document.write(str + "<hr/>");
//模拟url编码
str = encodeURI(str);
document.write(str + "<hr/>");
//对编码后的url进行解码
str = decodeURI(str);
document.write(str + "<hr/>");
</script>