JavaScript入门必备基础
1、什么是语言
计算机就是一个有人来控制的机器,它的所有行为都是有人来进行指定的;我们学习的语言就是人和计算机交流的工具,人类通过语言来控制、操作计算机;计算机的发展史如下:
- 纸带机,机器语言;
- 汇编语言,符号语言;
- 现代语言,高级语言;
学习一个完整的JavaScript实现包含三个部分,ECMAScript、DOM、BOM。 其中ECMAScript又称为ES,一般情况下说的ES又可以理解为JS。
JS特点:
- 解释型语言;
- 类似于C和Java的语法结构;
- 动态语言;
- 基于原型的面向对象;
2、js 的 helloworld
js的代码需要编写在html标签的script标签中,type的值可以写成text/javascript。
js的三个输出语句:
- alert();
- document.write();
- console.log();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>js helloworld</title>
<!-- js代码需要编写到script标签中! -->
<script type="text/javascript">
// 控制浏览器窗口弹出一个警告框
// alert("js");
// 让计算机在页面中输出一个内容
// documen.write()可以向body中输出一个内容。这里的document表示文档,一个网页就表示一个文档。
// document.write("你看我出来吗~~~");
// 向控制台输出一个内容
// console.log()向控制台输出一段内容。
// console.log("你猜我在哪里?")
// js代码的执行顺序是从上往下执行,即要等到上一条语句执行结束后才开始执行下一条语句。
alert("js");
document.write("你看我出来吗~~~");
console.log("你猜我在哪里?")
</script>
</head>
<body>
</body>
</html>
3、js代码编写位置
3.1、写在样式的标签中
可以将js代码编写到标签的onclick属性中,当我们点击按钮时,js代码才会执行,如:
<button onclick="alert('点击了按钮');">点我一下</button>
但是如果是在超链接中,js代码则要卸载 href 属性中,这样当点击超链接时,会执行js代码,格式如下:
<a href="javascript:alert('超链接被点击了!');">超链接</a>
在编写前端网页时,我们有时希望超链接被点击后什么都不要做,我们就可以使用这一特点来实现,如:
<a href="javascript:;">超链接</a>
虽然可以将js代码编写在标签的属性中,但是他们属于结构与行为耦合,不方便维护,不推荐使用。
3.2、写在script标签中
这种方式在练习的时候使用的较多,格式如下:
<script type="text/javascript">
alert(1);
</script>
3.3、单独写在js文件中
写到外部文件中可以在不同的页面中同时引用,也可以利用到浏览器的缓存机制,推荐使用。我们编写好js文件后,可以通过以下方式引入js文件:
<!-- 引入外部js文件 -->
<script type="text/javascript" src="./js/script.js">
// 当script标签用于引入js文件时,这里不能编写js代码
</script>
script标签一旦用于引入外部文件了,就不能再编写代码了,即使编写了代码浏览器也会忽略。如果需要则可以在创建一个新的script标签用于编写内部代码。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>js代码编写位置</title>
<!-- 引入外部js文件 -->
<script type="text/javascript" src="./js/script.js">
// 当script标签用于引入js文件时,这里不能编写js代码
</script>
<!--
<script>
alert("我是内部的js代码");
</script>
-->
</head>
<body>
<button onclick="alert('标签中的js代码');">点我一下</button>
<a href="javascript:alert('超链接被点击了!');">超链接</a>
</body>
</html>
4、js的基本语法
- /* */,js的多行注释;
- //,js的单行注释;
- js严格区分大小写;
- js中每一条语句以分号结尾,如果不写分号,浏览器会自动添加,但是会消耗一些系统资源,而且有些时候浏览器也会加错分号,所以在开发中必须写分号;
- js中会忽略多个空格和换行,所以我们可以利用空格和换行对代码进行格式化;
5、字面量和变量
字面量: 都是一些不可改变的值,如:1,2,3,4,5等;字面量都是可以直接使用的,但是我们一般不会直接使用字面量,因为相对比较难记。
变量: 变量可以用来保存字面量,而且变量的值可是可以任意改变的,变量更加方便我们使用,所以在开发中都是通过变量保存一个字面量,而很少直接使用字面量;如:x=13343542542545;可以通过变量对字面量进行一个描述,这样能够更好的记住字面量。
在js中使用var关键字来声明变量,如:var a;如果我们没有给变量指定初始值,在刚定义的变量的默认值为undefined;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>字面量和变量</title>
<script>
// 定义变量a,并赋值为123
var a = 123;
document.write(a); // 123
var b; // 没有给变量b赋值,则b变量的初始值为 undefined
document.write(b); // undefind
</script>
</head>
<body>
</body>
</html>
6、标识符
在js中所有的可以有我们自主命名的都可以称为是标识符,如变量名、函数名、属性名都属于标识符。标识符命名规则如下:
- 标识符中可以含有字母、数字、下划线、$;
- 标识符不能以数字开头;
- 标识符不能是ES中的关键字或保留字;
- 标识符一般采用驼峰命名法,即首字母小写,其他每个单词开头的字母大写,其余字母小写,如:xxxYyyZzz;
js底层保存标识符时,实际上是采用的Unicode编码,所以理论上,所有的utf-8中含有的内容都可以作为标识符。如:js可以以中文作为变量名,但是不推荐使用。
7、数据类型
数据类型指的就是字面量的类型,在js中一共有六种数据类型,分别如下:
- String,字符串;
- Number,数值;
- Boolean,布尔值;
- Null,空值;
- Undefined,未定义;
- Object,对象;
其中String、Number、Boolean、Null、Undefined属于基本数据类型,而Object属于引用数据类型。
7.1、String
在js中字符串需要使用引号引起来,使用双引号或单引号都可以,但是不要混着用(单引号里面不能直接放单引号,双引号里面也不能直接放双引号);
在字符串中我们可以使用\作为转义字符,党表示一些特殊符号时可以使用\进行转义,如:
\" 表示";
\' 表示';
\n 表示换行;
\t 表示制表符;
\\ 表示\
<script>
var str = 'hello';
str = '我说:"今天天气真不错!"';
// 输出str变量
console.log(str); // 我说:"今天天气真不错!"
// 输出str字面量,即在js中使用单引号括起来的都是字面量
console.log("str"); // str
</script>
7.2、Number
在js中所有的数值都是Number类型,包括整数和浮点数;在js中我们可以使用运算符typeof来检查一个变量的类型,语法为:typeof 变量名;
js中可以表示数字的最大值,Numbe.MAX_VALUE,如果使用Number表示的数字超过了最大值,则会返回一个Infinity(一个字面量,也属于number类型),表示正无穷。-Infinity表示负无穷。
NaN: 是一个特殊的数字,表示Not a Number,使用typeof检查一个NaN也会返回number。
在js中整数的运算基本可以保证精确,如果使用js进行浮点运算,可能得到一个不精确的结果,所以不要使用js进行对精确度要求较高的数值进行运算。
<script>
// 数字类型
var a = 123;
// 字符串类型
var b = "123";
// 使用typeof可以检测一个变量的类型,输出的结果全是小写的。
console.log(typeof a); // number
console.log(typeof b); // string
// js的最大数值
console.log(Number.MAX_VALUE); // 1.7976931348623157e+308
console.log(Number.MIN_VALUE); // 5e-324,大于零的最小值
a = "abc" * "def";
// 返回NaN,NaN也表示的是一个字面量
console.log(a);
// number
console.log(typeof a);
var c = 123 + 456;
console.log(c); // 579
var d = 0.1 + 0.2;
console.log(d); // 0.30000000000000004,结果出现失真
</script>
7.3、Boolean
布尔值只有两个,true(真)与false(假),主要用来逻辑判断。使用typeof检查一个布尔值时,会返回boolean。
7.4、Null与Undefined
Null: 表示空值,其值只有一个null,null这个值专门用来表示一个为空的对象,使用typeof检查一个null值时,会返回一个object。
Undefined: 表示未定义,其值也只有一个undefined,当声明一个变量,但是不给变量赋值时,它的值就是undefined,使用typeof检查一个undefined时也会返回undefined。
7.5、Object
后面介绍
8、强制数据类型转换
指将一个数据类型强制转换为其他数据类型,类型转换主要指的是将其它的数据类型转换为:String、Number、Boolean。
8.1、强制转换为String
将其它的数据类型转换为String,方式如下:
方式一 ,调用被转换数据类型的**toString()**方法,该方法不会影响到原变量,它会将转换的结果返回。但是注意:Null和Undefined这两个类型的变量没有toString()方法,如果这两个数据类型的变量在调用toString()方法时会报错。
方式二 ,调用**String()**函数,并将被转换的数据作为参数传递给函数。使用String()函数做强制类型转换时,对于Number和Boolean实际上就是调用了toString()方法;但是对于null和undefined,就不会调用toString()方法,而是将null和undefined直接转换为"null"和"undefined";
<script>
// 方式一:使用toString()方法进行转换,存在缺陷,null与undefined类型不能调用该方法。
// 将数字转换为字符串
var a = 123;
console.log(typeof a); // number
console.log(typeof a.toString()); // string
// 将布尔值转换为字符串
var bool = true;
console.log(typeof bool); // boolean
console.log(typeof bool.toString()); // string
// 方式二:使用String()函数,解决了方式一的不足。
console.log(typeof String(a)); // string
// 将Null转换为字符串
var c = null;
console.log(String(c)); // null
console.log(typeof String(c)); // string
</script>
8.2、强制转换为Number
方式一 ,使用**Number()**函数。
字符串转数字:
- 如果是纯数字的字符串,则直接将其转换为数字;
- 如果字符串中有非数字的内容,则转换为NaN;
- 如果字符串是一个空串或是一个全是空格的字符串,则转换成0;
布尔转数字:
- true转成1;
- false转成0;
Null转数字 就直接是0。
undefined转数字 就直接是NaN。
方式二 ,使用parseInt()与parseFloat()函数;如果对非String使用parseInt()和parseFloat(),它会先将其转换为String,然后在进行操作。
<script>
// 方式一,使用Number()函数,不足之处是对含有字母的数字字符串只能返回NaN。
var str = "helloworld";
console.log(Number(str)); // NaN
console.log(typeof Number(str)); // number
// 处理含有字母的数字字符串
var a = "123px";
console.log(typeof a); // string
console.log(Number(a)); // NaN
console.log(typeof Number(a)); // number
/*
方式二:
使用parseInt()函数、parseFloat()函数。
这两个函数专门用来处理含有字母的数字字符串。弥补了Number()函数的不足。
parseInt()可以将一个字符串中有效的整数内容取出来,然后转换为Number。如:
parseInt('123px'); // 返回123
parseInt('123p456px'); // 返回123
parseInt('123.456px'); // 返回123
parseInt('p123x'); // 返回NaN
当然也可以使用parseInt函数将浮点数转换为整数,如:
parseInt(123.456); // 返回123
parseFloat()的作用和parseInt()类似,不同的是它可以获得有效的小数。
parseFloat('123.456px'); // 返回123.456
parseFloat('123.456.789px'); // 返回123.456
*/
console.log(parseInt(a)); // 123
console.log(typeof parseInt(a)); // number
console.log(parseFloat('123.456.789px'));
// 对parseInt()与parseFloat()传递非String的值
console.log(parseInt(true)); // NaN
console.log(parseFloat(undefined)); // NaN
</script>
8.3、其他进制的数字
在js中,如果需要表示16进制,则需要以0x开头;如果要表示8进制,则以0开头;如果要表示2进制,则以0b开头,但是并不是所有的浏览器都支持2进制。
<script>
// 十六进制
var a = 0x10;
console.log(a); // 16(十进制)
// 八进制
var b = 07;
console.log(b); // 7(十进制)
// 二进制,使用的很少。
var c = 0b10;
console.log(c); // 2(十进制)
// 像"070"这种字符串,有些浏览器会当成8进制解析,有些则会当成10进制进行解析
// 为了解决这个问题,我们通常会在parseInt()函数中指定第二个参数,第二个参数表示解析的进制数。
console.log(parseInt("070", 10)); // 将八进制"070"装换为10进制
</script>
8.4、强制转换为Boolean
将其它数据类型转换为Boolean,使用Boolean()函数。
数字转布尔: 除了0和NaN,其余的都是true。
字符串转布尔: 除了空串(即类似"",表示空串,但是" ",则不表示空串),其余都是true。
**null和undefined: ** 都会转换成false;
对象也会转换为true;
<script>
var str = ""; // 空串
console.log(Boolean(str)); // false
str = " "; // 不是空串
console.log(Boolean(str)); // true
</script>
9、运算符
运算符也叫操作符,通过运算符可以对一个或多个值进行运算,并获取运算结果。如:typeof就是运算符,可以来获得一个值的类型,它将该值的类型以字符串的形式返回。
9.1、算术运算符
操作符: +、-、*、/、%,这些运算符都可以对两个值进行算术运算,并将结果返回(注意:一般不会影响原来变量的结果);
如果对两个字符串进行加法运算, 则会做拼串,即将两个字符串拼接为一个字符串并返回。任何的值和字符串做加法运算,都会先转换为字符串,然后在做拼串操作。
两个undefined的变量相加时,等于NaN。
当对非Number类型的值进行运算时,会将这些值转换为Number然后在进行运算。任何数和NaN做运算结果都是NaN。
<script>
// 任何值和字符串相加都会转换为字符串,并做拼串操作。这种方式通常使用的比较多。
// 我们可以利用这一特点将任意数据类型转换为String。我们只需要将其数据类型后面加上一个空字符串即可。
// 这是一种隐式的类型转换,有浏览器自动完成,实际上它也是调用了String()函数。
var c = 123;
console.log("c = " + c); // c = 123
console.log(typeof c); // number
c = c + "";
console.log(typeof c); // string
// 小练习
console.log(1 + 2 + "3"); // 33,算术运算法是从左向右运算的。
console.log("1" + 2 + 3); // 123
// 任何值与算术运算符中的:-、*、/做运算时都会自动转换为 Number。
// 我们可以利用这一特点做隐式的类型转换,可以通过为一个值 -0、*1、/1来将其转换为 number。原理和Number()函数一样。
// 在算术运算符中,除了加法会进行拼串外,其余都会将纯数字的字符串转换为number
console.log(100 - "1"); // 99
console.log(100 - true); // 99
console.log(100 - null); // 100
console.log(100 - undefined); // NaN
// 乘法
console.log(2 * "2"); // 4
console.log(2 * undefined); // NaN
console.log(2 * null); // 0
console.log(2 * true); // 2
</script>
9.2、一元运算符
一元运算符,只需要一个操作数,如:
- +表示正号,正号不会对数字产生任何影响;
- -表示负号,负号可以对数字进行正号的取反。
<script>
// - 负号,对于非Number类型的值,它会先转换为Number,然后在进行运算。
console.log(-true); // -1
// 可以对一个其他的任意数据类型使用+,来将其转换为number,它的原理和Number()函数一样。
console.log(1 + +"2" + 3); // 6
</script>
9.3、自增和自减
自增(++): 通过自增可以是变量在自身的基础上增加1;对于一个变量自增后,原变量的值会立即自增1;
自减(–): 与则增差不多。
<script>
// 自增
var a = 1;
// a++,先取值在自增
console.log(a++); // 1
console.log(a++); // 2
// ++a,先自增再取值
console.log(++a); // 4
// 小练习
var d = 20;
console.log(d++ + ++d + d); // 20 + 22 + 22 = 64
var e = 1;
e = e++;
console.log(e); // 1
// 自减,和自增差不多
var num = 10;
console.log(num--); // 10,此时的num的值为9
console.log(--num); // 8,此时的num的值8
</script>
9.4、逻辑运算符
js中提供了三种逻辑运算符,分别为:
- 与(&&),可以对符号两侧的值进行与运算并返回结果,注意与和或运算符都会出现短路的情况。
- 或(||),和逻辑与差别不大,只是在短路的时候是相反的。
- 非(!),可以用来对一个boolean值进行取反操作,如果对非布尔值进行取反,则会先将其转换为布尔值,然后在取反,所以我们可以利用这一特点将其他的数据类型转换为Boolean值。可以为任意一个数据类型做两次取反操作,来将其转换为布尔值,原理和Boolean()函数一样。
<script>
var a = 1;
// 与运算
// 左边的表达式运算结果为false,与运算发生短路,整个表达式返回false
console.log(1 > 2 && a++); // false
console.log(a); // 1,这里由于与运算发生短路,操作符右边的a++并没有得到执行。
// 左边的表达式运算结果为true时,与运算会执行右边的表达式。
console.log(2 > 1 && 5 > 3); // true
// 或运算
// 同样的或运算也是一种短路的运算。
// 即左边的值为true时,会直接返回表达式的值true;如果左边的值为false,才回去判断右边表达式的值。
console.log(true || alert(1)); // 这里的alert(1)不会被执行
console.log(false || alert(2)); // 浏览器窗口弹出警告窗口
// 非运算
// 对任意一个值取反两次,得到的Boolean值不变
var flag = true;
console.log(!!flag); // true
</script>
与、或运算符的补充:
<script>
/**
* &&, ||, ! 布尔值的情况:
* 对于非布尔值进行与、或、非运算时,会先将器转换为布尔值,然后在运算,并返回“原值”。
* 与运算:
* 1. 如果第一个值为true,则必然返回第二个值;
* 2. 如果第一个值为false,则必然返回第一个值。
* 或运算:
* 1. 如果第一个值为true,则直接返回第一个值。
* 2. 如果第一个值为false,则直接返回第二个值。
*/
// 与运算符:如果两个值都为true,则返回后边的。
console.log(5 && 6); // 6
console.log(2 && 1); // 1
// 如果两个值中有false,则返回false。
console.log(0 && 2); // 0
console.log(2 && 0); // 0
// 如果两个值都是false,则返回靠前的false。
console.log(NaN && 0); // NaN
console.log(0 && NaN); // 0
// 或运算符:如果第一个值为true,则直接返回第一个值。
console.log(1 || 0); // 1
console.log(1 || NaN); // 1
console.log(1 || null); // 1
// 如果第一个值为false,则直接返回第二个值。
console.log(NaN || 0); // 0
console.log(0 || null); // null
console.log("" || "hello"); // hello,注意""表示空串,转换为Boolean值时是false。
</script>
9.5、赋值运算符
可以将符号右侧的值赋值给符号左侧的变量。运算符分别为:=、+=、-=、*=、/=、%=。
9.6、关系运算符
通过关系运算符可以比较两个值之间的大小关系,如果关系成立它会返回true,如果关系不成立则返回false。运算符分别为:>、<、>=、<=、==、!=。
<script>
// 关系运算符,如果等式成立则返回true,否则返回false。
console.log(5 > 10); // false
console.log(5 > 4); // true
// >=表示大于或等于
console.log(5 >= 5); // true
// 非数字情况:
// 对于非数字和数字进行比较时,会先将其转换为数字,然后在进行比较。
// 如果符号两侧的值都是字符串时,则不会将其转换为数字进行比较,而是分别比较字符串中字符的Unicode编码。
console.log(1 > true); // false;
console.log(1 >= true); // true;
console.log(1 > "0"); // true;
console.log(1 > null); // true;
// 任何数字与NaN进行比较时,都会返回false
console.log(10 <= "hello"); // false
console.log(10 < "hello"); // false
console.log(10 > "hello"); // false
console.log(10 >= "hello"); // false
console.log(10 == "hello"); //false
// NaN与NaN进行比较
console.log("hello" == "hello"); // true,其他关系运算符作比较返回都是false。
// 两个字符串比较,当两个字符串比较的时候实际上是比较两个字符串的Unicode编码
// 比较字符编码时是一位一位的进行比较,如果两个一样,则比较下一位,直到出现不相等的情况(或者最后一位)。
// 可以借助上面的方式来对英文名进行排序。对中文来说没有意义。
console.log("a" < "b"); // true,即 97 < 98
console.log("11" < "5"); // true,这里是先拿1与5比较,1<5成立,表达式直接返回true。
// 如果比较的是两个字符串型的数字,可能会得到不可预期的结果。
// 注意:在比较两个字符串型的数字时,一定要转型。如:可以在数字字符串的前面加上一个正号。
console.log("11" < +"5"); // false, 这里的+"5"表示将字符串5转变为数字5.
</script>
9.10、Unicode编码
在js中,字符串使用转义字符后再输入Unicode编码,格式为:\u四位编码。这里的编码是16进制。
// js中使用Unicode编码
document.write("\u2620");
在网页中使用Unicode编码,格式为:&#编码; ,这里的编码需要10进制。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>document</title>
<script>
// js中使用Unicode编码
document.write("\u2620");
</script>
</head>
<body>
<!-- 在网页中使用Unicode编码 -->
<h1 style="font-size: 100px;">☠</h1>
</body>
</html>
9.11、相等与不相等运算符
相等与不相等运算符也都属于关系运算符,相等运算符用来比较两个值是否相等,如果相等会返回true,否则返回false。使用 == 来做相等运算。不相等运算符用来判断两个值是否不相等,如果不相等返回true,否则返回false,使用**!=**来做不相等运算。
当使用==来比较两个值时,如果值的类型不同,则会自动进行类型的转换,将其转换为相同的类型,然后在进行比较。
不相等运算符也会对变量进行自动的类型转换,如果转换后相等它也会返回false。
全等(===): 用来判断两个值是否全等,它和相等类似,不同的是它不会进行类型的转换,如果两个值的类型不同,则直接返回false。
不全等(!==): 用来判断两个值是否不全等,他和不相等类似,不同的是它也不会进行类型的转换,如果两个值的类型相同,则直接返回true。
<script>
// 相等运算符
console.log(1 == 1); // true
// 当比较的类型不同时,会先进行转型,大多数情况下都是转为数字。
console.log(1 == "1"); //true,即字符串1先转为数字1,然后在比较。
console.log(true == 1); // true
console.log(true == "1"); // true
// 以下一行的代码需要注意。
console.log(null == 0); // false
// undefined衍生自null,所以这两个值做相等判断时,会返回true。
console.log(undefined == null); // true
// NaN不和任何值相等,包括它本身。
console.log(NaN == NaN); // false
// 这里就会衍生出一个问题,既然NaN不与任何值相等且包括它本身,那么如何判断一个变量的值是否为NaN?
// 为了解决这个问题,js中给我们中给我们提供了一个专门用于检测变量是否为NaN的函数。
// boolean isNaN(); 该函数返回一个Boolean类型的值,如果变量是NaN,则返回true,否则返回false。
var a = NaN;
console.log(isNaN(a)); // true
// 不相等运算符
// 不相等运算符也会对变量进行自动的类型转换,如果转换后相等它也会返回false.
console.log(1 != "1"); // false
// 全等
console.log("123" === 123); // false
console.log(null === undefined); //false
// 不全等
console.log("123" !== 123); // true
</script>
9.12、条件运算符
条件运算符也叫三元运算符。三元,即三个操作数。语法如下:
条件表达式?语句1:语句2;
执行流程,条件表达式在执行时,首先对条件表达式进行求值;如果该值为true,则执行语句1,并返回执行结果;如果该值为false,则执行false,则执行语句2,并返回执行结果。当条件表达式不是布尔值时,会先将其转换为布尔值,在进行运算。
"hello" ---> true; // 字符串转换为布尔值时,只要字符串不为空,都会转为true。
"" ---> false; // 字符串为空,转为false。
<script>
// 条件表达式
var a = 10, b = 20;
// 获取最大值
var max = a > b ? a : b;
console.log(max); // 20
// 获取三个变量中的最大值
var c = 30;
// 这种方式不推荐使用。
max = a > b ? (a > c ? a : c) : (b > c ? b : c);
console.log(max); // 30
</script>
9.13、逗号运算符
逗号运算符(,): 可以分割多个语句,一般可以在声明多个变量时使用。
// 使用逗号运算符同时定义多个变量
var a, b, c;
9.14、运算符的优先级
在js中有一个运算符优先级的表,在表中位置越靠上,优先级越高,越优先计算。如果优先级一样,则从左向右运算。但是我们可以通过**括号()**来改变一个表达式的优先级。
// 运算符的优先级
// 这里&&的优先级比||的优先级高,所以先执行2 && 3;返回3,然后在执行1 || 3,返回1.
console.log(1 || 2 && 3); // 1
10、代码块
程序是由一条一条语句构成的,语句是按照自上向下的顺序一条一条执行。在js中可以使用{}来为语句进行分组,同一个{}中的语句我们称为是一组语句,它们要么都执行,要么都不执行。一个{}中的语句我们也称为一个代码块。在代码块后面就不再需要编写分号了。
js中的代码块,只具有分组的作用,没有其他的用途。代码块内部的内容在外部是完全可见的。
<script>
// 定义代码块
{
// 定义在代码块中的变量在代码块的外面也能够访问到
var test_var = 10;
console.log(1);
document.write(1);
}
// 访问代码块中的变量
console.log(test_var); // 10
</script>
11、流程控制语句
js中的程序是从上往下一行一行的执行,通过流程控制语句可以控制程序执行的流程,使程序可以根据一定的条件来选择执行。
语句的分类:
- 条件判断语句;
- 条件分支语句;
- 循环语句,通过循环语句可以反复的执行一段代码多次。
11.1、条件判断语句
使用条件判断语句可以在执行某个语句之前进行判断,如果条件成立才会执行语句,条件不成立则语句不会成立。
if语句: if语句执行时,会先对条件表达式进行求值判断,如果条件表达式的值为true,则执行if后的语句;如果条件表达式的值为false,则不会执行if后的语句。
语法一: 这种if语句只能控制紧随其后的那一条语句。如果希望if语句可以控制多条语句,可以将这些语句统一放到代码块中。
if(条件表达式){
语句;
}
语法二:
if(条件表达式){
语句...;
}else{
语句...;
}
语法三: 该语句中,只会有一个代码块会被执行,一旦代码块被执行了,则直接跳出整个判断语句。
if(条件表达式){
语句...;
} else if(条件表达式){
语句...;
} else{
语句...;
}
补充: prompt()可以弹出一个提示框,该提示框中会带有一个文本框,用户可以在文本框中输入一段内容,该函数需要一个字符串作为参数,该字符串将会作为提示框的提示文字。用户输入的内容就会作为函数的返回值(String类型,可以在函数的前面加上一个正号将其转换为number类型)返回,可以定义一个变量来接收该内容。
11.2、条件分支语句
条件分支语句也叫switch语句 。语法为:
switch(条件表达式){
case 表达式 :
语句...;
[break]; // 可以省略,省略后这个case里面的语句执行完毕后还会执行后面的case,知道case中有break或者default语句,即可以使用break语句来退出switch语句。
case 表达式 :
语句...;
[break];
... // 中间的case可以有很多个
default:
语句...;
}
执行流程: 在执行时依次将case后面的表达式的值和switch后的条件表达式的值进行全等比较。如果比较为true,则从当前的case处开始执行代码。default后面的break语句可以省略。
switch语句和if语句的功能实际上有重复的,使用switch可以实现if的功能,同样使用if也可以实现switch的功能,所以我们使用时,可以根据自己的习惯进行选择。
js的专属,类似if:
<script>
// 成绩是否合格的判断
var secore = 1;
switch(secore > 0 && secore <= 100){ // true,case后面的表达式执行结果如果为true,则与这里的true全等。
case secore >= 60: document.write("pass");break;
default: document.write("no pass");break;
}
</script>
switch语句的写法:
<script>
// 成绩是否合格的判断,60分以上合格,60分以下不合格。
var secore = 59;
// 先将成绩除10,注意,在js中99/10=9.9,而不是9
secore = parseInt(secore/10);
document.write("secore = " + secore + "<br>");
switch(secore){
case 10:
case 9:
case 8:
case 7:
case 6: document.write("pass");break;
default: document.write("no pass");break;
}
</script>
11.3、while循环
先判断,后执行。
语法:
while(条件表达式){
语句... // / 注意,while循环中使用{}括起来的也叫作循环体
}
执行流程: 先对条件表达式进行求值判断,如果值为true,则执行循环体,循环体执行完毕以后,继续对表达式进行判断;如果为true,则继续执行循环体,依次类推,直到条件表达式为false才跳出循环。
当然也可以在循环语句中使用break语句来跳出循环。即当循环体中的代码执行时遇到break,则执行跳出循环。
<script>
// 如果while中的条件表达式直接写成true,则会出现死循环。
// 死循环不会停止,除非浏览器关闭。
// var n = 1;
// while(true){
// alert(n++);
// // 判断n等于10的时候使用break语句强制退出循环
// if(n == 10){
// break;
// }
// }
// 另外一种常见的写法为
// 创建一个循环体的三个步骤:
// 1. 初始化一个变量
var i = 0;
// 在循环体中设置一个条件表达式
while(i < 10){
alert(i);
// 3. 定义一个更新表达式,每次更新初始化变量
i ++;
// 以上的两行代码可以写成:alert(i++);
}
</script>
11.4、do…while循环
先执行,后判断。
语法:
do{
语句... // / 注意,循环中使用{}括起来的也叫作循环体
}while(条件表达式);
执行流程: do…while语句在执行时,会先执行循环体,循环体执行完毕以后,在对while后的条件表达式进行判断;如果为true,进行执行循环体;如果为false,则跳出循环体。do…while可以保证循环体至少执行一次,而while循环不能。
<script>
// 假如投资的年利率为5%,试求从1000块增长到5000块,需要花费多少年
// 本金
var capital = 1000;
// 年
var year = 0;
do{
capital = capital + capital*0.05;
// alert(capital);
year++;
if (capital >= 5000){
capital = parseInt(capital);
}
}while(capital < 5000);
console.log("year = " + year);
</script>
11.5、for循环
在for循环中,为我们提供了专门的位置用来放置循环的三个表达式,即:
- 初始化表达式;
- 条件表达式;
- 更新表达式;
for循环的语法如下:
for(初始化表达式;条件表达式;更新表达式){
语句... // 注意,for循环中使用{}括起来的也叫作循环体
}
for循环执行流程:
- 执行初始化表达式,初始化变量(初始化表达式只会执行一次);
- 执行条件表达式,判断是否执行循环体,如果条件表达式为true,则执行循环体,如果为false,则跳出for循环;
- 执行更新表达式,更新表达式执行完毕后继续执行条件判断语句。
for循环中的三个部分都可以省略,也可以写在外部。如果在for循环中不写任何的表达式,只写两个分号,此时循环体是一个死循环会一直执行下去,慎用!!!
// for循环的初始化变量写在外面
var i = 0;
for(;i<5;i++){
document.write(i);
}
// 死循环
for(;;){
alert("死循环!!!");
}
<script>
// 在页面中接收一个用户输入的数字,并判断该数是否是质数。
// 质数:只能被1和它自身整除的数,1不是质数也不是合数,质数必须是大于1的自然数。
// 获取从客户端输入的数字
var num = prompt("输入一个数字");
// 创建一个变量用来保存当前数的状态,默认当前num是质数
var flag = true;
// 判断num是否是质数,只需要判断2~num之间的数能够将num整除即可。
for(var i = 2; i < num; i++){
if(num % i == 0){
// 如果num能被i整除,则说明num一定不是质数。
flag = false;
break;
}
}
if(flag){
alert(num + " 是质数!");
}else{
alert(num + " 不是质数!");
}
</script>
11.6、break与continue
break: 关键字可以用来退出switch预计或循环语句。不能再if语句中使用break和continue语句。break关键字会立即终止离它最近的那个循环语句。
continue: 关键字可以用来跳过本次循环。
<script>
// 可以为循环体创建一个loop_name,用来标识当前的循环。格式为:loop_name:循环语句。
// 使用break语句时,可以在break后面跟着一个loop_name,这样break将会结束指定的循环,而不是最近的。
// 同样,continue语句也可以指定一个loop_name,用来指定跳过本次循环的循环体。
test:for(var i = 0; i < 5; i++){
// document.write("outer.<br>");
for(var j = 0; j < 5; j++){
document.write("inner.<br>");
if(j == 2){
// 通过循环体的标识,在内层循环里面直接结束外层循环。
break test;
}
}
}
</script>
11.7、程序性能的测试
在程序执行前,开启定时器。console.time(“定时器的名字”)可以用来开启一个计时器,它需要一个字符串作为参数,这个字符串将会作为计时器的标识。
// 开启计时器
console.time("timer");
Some code...
// 结束计时器的计时
console.timeEnd("timer");
<script>
// 开启计时器
console.time("timer");
// 输出1~100所有的质数
var flag = true;
for(var i = 2; i <= 10000; i ++, flag = true){
// 通过Math.sqrt()函数对i进行开方
for(var j = 2; j <= Math.sqrt(i); j++){
if(i % j == 0){
flag = false;
// 不加break, 204.427978515625 ms
// 加break, 69.3349609375 ms
// 使用Math.sqrt()函数后, 6.3232421875 ms
}
}
if(flag){
// document.write(i + ",");
}
}
// 结束计时器的计时
console.timeEnd("timer");
</script>
12、对象
js中的数据类型:
- String,字符串;
- Number,数值;
- Boolean,布尔值;
- Null,空值;
- Undefined,未定义;
- Object,对象;
其中前面的5种都属于基本数据类型,以后我们看到数据类型的值只要不是上边的5种,那么肯定是对象。基本数据类型都是单一的值,值与值之间没有任何的联系。如果使用基本数据类型的数据,我们所创建的变量都是独立的,不能成为一个整体。
12.1、对象的简介
对象属于一种复合的数据类型 ,在对象中可以保存多个不同类型的属性。
对象的分类:
- 内建对象,有ES标准中定义的对象,在任何的ES的实现中都可以使用,如:Math、String、Number、Boolean、Function。
- 宿主对象,有js的运行环境提供的对象,目前来讲主要指的是由浏览器提供的对象,如:BOM(浏览器对象模型)、DOM(文档对象模型)。
- 自定义对象,有开发人员自己创建的对象。
12.2、对象的基本操作
创建对象: 使用new关键字调用的函数,是构造函数constructor,构造函数是专门用来创建对象的函数。语法为:
var object_name = new Object();
使用typeof检查一个对象时,会返回object。
向对象中添加属性,语法:
对象.属性名 = 属性值;
读取对象中的属性,如果读取对象中没有的属性,不会报错而是会返回undefined。语法:
对象.属性名;
修改属性值,语法:
对象.属性名 = 新的属性值;
删除对象的属性 ,语法:
delete 对象.属性名;
<script>
// 创建对象
// 使用new关键字
// 创建一个名为obj的对象
var obj = new Object();
// 在对象中添加属性
obj.name = "alice";
obj.gender = "male"
obj.age = 19;
// 查看对象
console.log(obj); // {name: 'alice', gender: 'male', age: 19}
// 读取对象属性值
// 如果读取对象中没有的属性,不会报错而是会返回undefined。
console.log(obj.name); // alice
// 删除对象中的name属性
delete obj.name;
console.log(obj.name); // undefined
</script>
12.3、属性名和属性值
属性名: 对象的属性名不强制要求遵守标识符的规范,什么乱七八糟的名字都可以使用。但是我们使用的时候尽量还是按照标识符的规范去做。
// 创建obj对象
var obj = new Object();
// 使用关键字作为属性名,不推荐使用。
obj.var = "hello";
// 访问var属性值
console.log(obj.var); // hello
如果要使用特殊的属性名,不能采用点的方式进行操作,需要使用另一种方式。语法为:
对象["属性名"] = 属性值; // 使用这种方式添加的属性,在取属性值的时候也要使用:对象["属性名"];这种方式来取值。
// 创建obj对象
var obj = new Object();
// 使用关键字作为属性名,不推荐使用。
obj.var = "hello";
// 访问var属性值
console.log(obj.var); // hello
// 特殊方式定义属性名
obj["123"] = 567; // 这里如果使用obj.123会报错。
// 获取特性定义的属性值
console.log(obj["123"]); // 567 ,同样这里也不能使用obj.123进行访问。
// 使用[]这种形式去操作属性更加灵活,在[]中可以直接传递一个变量,这样变量值是多少就会读取那个属性。
var n = "test";
obj["123"] = 789;
obj["test"] = "测试";
// 使用变量来作为属性名
console.log(obj[n]); // 测试
属性值: js对象的属性值可以是任意的数据类型,甚至也可以是一个对象。
in运算符: 通过该运算符可以检测一个对象中是否含有指定的对象,如果有则返回true,否则返回false。语法为:
"属性名" in 对象
// 属性值
// 创建交通工具对象
var vehicle = new Object();
// 创建摩托车对象
var motor = new Object();
// 在交通工具中添加种类属性,并将一个对象赋值给category属性
vehicle.category = motor;
// 创建摩托车的车名
motor.name = "yamaha";
// 访问交通工具种类中车名为yahama的车子
console.log(vehicle.category.name); // yamaha
// 检查vehicle对象中是否有name属性
console.log("name" in vehicle); // false
console.log("name" in motor); // true
12.4、基本和引用数据类型
基本数据类型: String、Number、Boolean、Null、Undefined。
引用数据类型: Object。
js中的变量都是保存到栈内存中的,基本数据类型的值直接在栈内存中存储,值与值之间是独立存在,修改一个变量的值不会影响到另一个变量的值。对象是保存到堆内存中的,每创建一个新的对象,就会在堆内存中开辟一个新的空间,而变量保存的是对象的内存地址(对象的引用),如果两个变量保存的是同一个对象引用,当通过一个变量修改对象的属性值时,另一个也会受到影响。
当比较两个基本数据类型的值时,就是直接比较的值,而比较两个引用数据类型时,他比较的是对象的内存地址。
<script>
// 基本数据类型
var a = 10;
// 这里用于a和b都是基本数据类型,所以变量a的值的改变并不会影响的b的值。
var b = a;
a = 20;
console.log("a = " + a); // a = 20
console.log("b = " + b); // b = 10
// 引用数据类型
// 创建对象
var obj1 = new Object();
var obj2 = new Object();
obj1.name = "alice";
// 引用数据类型
obj2 = obj1;
console.log(obj1.name);
console.log(obj2.name);
// 更新obj1 name的属性值
obj1.name = "mary";
console.log(obj1.name);
console.log(obj2.name);
// 基本数据类型的比较
var c = 10;
var d = 10;
console.log(c == d); // true
// 引用数据类型的比较
var obj3 = new Object();
var obj4 = new Object();
obj3.name = "lily";
obj4.name = "lily";
console.log(obj3 == obj4); // false
</script>
12.5、对象字面量{}
使用对象字面量,可以在创建对象时,直接指定对象中的属性,语法如下:
{属性名: 属性值, 属性名: 属性值...}
对象字面量的属性名可以加引用也可以不加,建议不加;如果要使用一些特殊的名字,则必须加引号。
属性名和属性值是一组一组的键值对结构,键和值之间使用冒号连接,多个键值对之间使用逗号隔开,如果一个属性之后没有其它属性了,就不要写逗号了。
<script>
// 创建对象
// var obj = new Object();
// 使用对象字面量来创建一个对象
var obj = {};
console.log(typeof obj); // object
// 给对象添加name属性
obj.name = "alice";
console.log(obj.name); // alice
// 使用字面量在创建对象时指定对象的属性
var obj2 = {
name: "Lily",
age: 28,
gender: "male",
// 将一个对象赋值给一个属性
test: {
name: 'zbj'
}
};
console.log(obj2.name); // Lily
</script>
12.6、方法
函数也可以成为对象的属性,如果一个函数作为一个对象的属性保存,那么我们称这个函数为对象的方法 。调用这个函数就是说调用对象的方法(method)。
// 创建一个对象
var obj = new Object();
// 向对象中添加属性
obj.name = 'alice';
obj.age = 18;
// 对象的属性值可以是任何的数据类型,包括对象、函数。
// 将一个匿名函数赋值给对象的属性
obj.sayName = function(){
console.log(obj.name);
};
// 调用函数中的函数,也称为调用对象的方法
obj.sayName(); // alice
// 使用字面量创建对象
var obj2 = {
name: "zbj",
age: 18,
// 创建对象的方法
sayName: function(){
console.log(obj2.name);
}
};
obj2.sayName(); // zbj
12.7、枚举对象的所有属性
使用for…in语句可以枚举对象的所有属性,语法如下:
for(var var_name in obj_name){
语句...
}
for…in语句,对象中有几个属性,循环体就会执行几次。每次执行时,会将对象中的一个属性名赋值给变量。
// 枚举对象的所有属性名即属性值
// 使用字面量定义对象
var user = {
u_id: '1234',
name: 'zhangsan',
age: 18,
gender: 'male',
address: 'china',
hobby: 'basketball',
// 定义打游戏打方法
playGame: function(){
console.log("play game...");
}
};
// 枚举user对象的所有属性
for(var attr in user){
console.log(attr); // 输出对象的所有属性名,包括playGame方法。
}
// 获取对象所有的属性值
for(var attr in user){
// 这里必须使用方括号而不能使用点,因为点不支持变量的操作。
console.log(user[attr]);
}
12.8、使用工厂方法创建对象
使用工厂方法创建对象,通过该方法可以大批量的创建对象。
// 创建工厂
function createPerson(name, age, gender){
// 创建一个对象
var obj = new Object();
// 向对象中添加属性
obj.name = name;
obj.age = age;
obj.gender = gender;
obj.sayName = function(){
alert(obj.name);
}
// 将对象返回
return obj;
}
// 通过工厂创建对象
var obj1 = createPerson("alice", 18, "famale");
obj1.sayName(); // alice
使用工厂方法创建的对象,使用的构造函数都是Object(),所以创建的对象都是Object类型,就导致我们无法区分出多种不同类型的对象。
12.9、构造函数
构造函数(也称为“类”): 就是一个普通函数,创建方式和普通函数没有区别,不同的是构造函数习惯上首字母大写。构造函数和普通函数的调用方式也有所不同,普通函数是直接调用,而构造函数需要使用new关键字来调用。
构造函数的执行流程:
- 立刻创建一个新的对象;
- 将新建的对象设置为构造函数中的this,在构造函数中可以使用this来引用新建的对象;
- 逐行执行函数中的代码;
- 将新建的对象作为构造函数的返回值返回;
使用同一个构造函数创建的对象,我们称为一类对象,也将一个构造函数称为一个类 ,我们将通过构造函数创建的对象,称为该类的实例 。
/*
this的情况:
1. 当以函数的形式调用时,this是window。
2. 当以方法的形式调用时,this就是方法的调用者。
3. 当以构造函数的形式调用时,this就是新创建的那个对象。
4. 使用call()和apply()调用时,this是指定的那个对象。
*/
// 创建构造函数,也称类。
function Person(name, age, gender){
this.name = name;
this.age = age;
this.gender = gender;
// 为了节约内存空间,我们可以将这里的函数function提取到全局作用域下。
// this.sayName = fun;
}
/*
将函数定义在全局作用域中,污染了全局作用域的命名空间(即导致函数名可能会重复)。
而且定义在全局作用域中也很不安全。使用原型对象可以解决这个问题。
*/
// function fun(){
// alert(this.name);
// };
// 使用原型对象解决以上问题
Person.prototype.sayName = function(){
alert(this.name);
};
function Dog(){
this.sleep = function(){
alert("狗狗能睡觉");
}
}
// 创建Person类的实例
// 这里如果加上一个关键字new,就表示为构造函数,不加new就表示为普通函数。
var per = new Person('swk', 13, 'male');
per.sayName(); // swk
var dog = new Dog();
dog.sleep(); // 狗狗能睡觉
// 使用instanceof可以检查一个对象是否是一个类的实例,语法为:对象 instanceof 类(构造函数);
// 如果是则返回true,否则返回false。
console.log(per instanceof Person); // true
console.log(dog instanceof Person); // false
// 所有的对象都是object的后代,所以任何对象与object做instanceof检查时都会返回true。
console.log(per instanceof Object); // true
console.log(dog instanceof Object); // true
12.10、原型对象
原型(prototype): 我们所创建的每一个函数,解析器都会向函数中添加一个属性prototype,这个属性对应着一个对象,这个对象就是我们所谓的原型对象。
如果函数作为普通函数调用,prototype没有任何作用;当函数以构造函数的形式调用时,它所创建的对象中都会有一个隐含的属性,指向该构造函数的原型对象,我们可以通过__ proto__来访问该属性。
原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象。我们可以将对象中共有的内容统一设置到原型对象中。
当我们访问对象的一个属性或方法时,它会先在对象自身中寻找,如果有则直接使用,如果没有则回去原型对象中寻找,如果找到了则直接使用。
以后我们创建构造函数时,可以将这些对象共有的属性和方法,统一添加到构造函数的原型对象中,这样不用分别为每一个对象添加,也不会影响到全局作用域命,就可以是每一个对象都具有这些属性和方法。
// 创建函数
function MyClass(){
}
// 向MyClass的原型中添加属性
MyClass.prototype.a = 123;
// 添加方法
MyClass.prototype.sayHello = function(){
console.log("我是MyClass中的sayHello方法!");
};
// 创建mc实例
var mc = new MyClass();
// 构造函数访问原型对象
console.log(MyClass.prototype);
// 实例访问原型对象
console.log(mc.__proto__);
console.log(MyClass.prototype == mc.__proto__); // true
// 访问原型对象中的a属性
console.log(MyClass.prototype.a); // 123
// 实例mc访问属性a,此时mc实例中没有属性a,则回去MyClass的原型对象中去找。
console.log(mc.a); // 123
// 向mc实例中添加属性a
mc.a = "我是实例mc中的a";
console.log(mc.a); // 我是实例mc中的a
// 访问sayHello方法
mc.sayHello(); // 我是MyClass中的sayHello方法!
12.11、原型对象的补充
// 创建构造函数
function MyClass(){
}
// 向MyClass原型对象中添加name属性
MyClass.prototype.name = "我是原型中的属性name";
var mc = new MyClass();
// 在实例mc中添加属性
mc.age = 20;
// 使用in检查对象中是否含有某个属性时,如果对象中没有但是原型对象中有,这是也会返回true。
console.log("name" in mc); // true
// 可以使用对象的hasOwnProperty()函数来检查对象自身中是否含有某个属性或方法。
//
console.log(mc.hasOwnProperty("name")); // false
console.log(mc.hasOwnProperty("age")); // true
// 查看实例mc的原型中是否存在hasOwnProperty()函数。
console.log(mc.__proto__.hasOwnProperty("hasOwnProperty")); // false
// 原型对象也是对象,所以它也有原型对象,当我们使用一个对象的属性或者方法时,会先在自身中寻找,
// 自身中如果有,则直接使用,如果没有则去原型对象中寻找,如果原型对象中有,则使用。
// 如果没有则去原型的原型对象中去寻找,直到找到Object对象的原型,Object对象的原型没有原型对象,
// 如果在Object中依然没有找到,则返回undefined。
// mc实例的原型的原型
console.log(mc.__proto__.__proto__.hasOwnProperty("hasOwnProperty")); // true
13、函数
函数(Function): 函数也是一个对象 ,函数中可以封装一些功能(代码),在需要时可以执行这些功能(代码)。函数中可以保存一些代码在需要的时候进行调用。使用typeof检查一个函数对象时,会返回function。
13.1、函数的简介
js可以在函数的内部再次声明另一个函数,创建函数的方式。
方式一 ,通过对象的形式来进行函数的创建,这种方式使用的很少。语法如下:
var function_name = new Function();
// 创建函数对象。这种方式不推荐使用。
// var fun = new Function();
// console.log(fun); // function anonymouse{}
// 可以将要封装的代码以字符串的形式传递给构造函数。
var fun = new Function("console.log('helloworld');");
// 封装到函数中的代码不会立即执行,函数中的代码会在函数调用的时候执行,函数调用的语法为:函数对象();
// 当调用函数时,函数中封装的代码会按照顺序执行。
fun(); // 调用了fun()函数,函数执行输出语句,输出helloworld
// 在函数中也可以添加属性,如:
// fun.create_time = '2022/2/21';
// console.log(fun.create_time); // 2022/2/21
方式二 ,使用函数声明来创建一个函数,推荐使用。语法:
function funtion_name([形参1, 形参2...形参n]){
语句...
}
// 使用函数声明来创建函数
function fun2(){
console.log("这是我的第二个函数");
}
// 函数调用
fun2(); // 这是我的第二个函数
方式三 ,使用函数表达式来创建一个函数,这种方式创建的函数也叫匿名函数,与方式二差不多,语法如下:
var funciton_name = function([形参1, 形参2...形参n]){
语句...
};
// 使用函数表达式来创建一个匿名函数
var fun3 = function(){
console.log("我是匿名函数中封装的代码");
};
// 调用匿名函数
fun3(); // 我是匿名函数中封装的代码
13.2、函数的参数
在函数的()中可以指定一个或多个形参(形式参数),多个形参之间使用逗号隔开,声明形参就相当于在函数内部声明了对应的变量(这里的变量直接写变量名即可,不用写变量的类型),但是并不赋值。调用函数时解析器不会检查实参的类型,所以要注意,是否有可能会接收到非法的参数,如果有可能,则可以对参数的类型做一个检查 ,函数的实参可以是任意的数据类型 。调用函数时,解析器也不会检查实参的数量,多余的实参不会被赋值。如果实参的数量少于形参的数量,则没有对应的实参的形参将会是undefined。
function funtion_name([形参名1,形参名2...形参名n]){
语句...
}
// 定义一个用来求两个数和的函数。
function sum(a, b){
console.log(a + b);
}
// 在调用函数时指定实参(实际参数),实参会将赋值给对应的形参。
sum(3, 4); // 7
// 传递了一个非数字的值,导致出现了字符串的拼接
sum(123, "hello"); // 123hello
// 定义一个用来求两个数和的函数。
function sum(a, b){
console.log(a + b);
}
// 在调用函数时指定实参(实际参数),实参会将赋值给对应的形参。
sum(3, 4); // 7
sum(123, "hello"); // 123hello
// 传递很多的实参,多余的实参自动被舍弃
sum(123, 456, "hello", "world"); // 579
// 实参数量过少,没有值的形参的默认值为undefined。
sum(123); // NaN , 123 + undefined = NaN
13.3、函数的返回值
使用return来设置一个函数的返回值,语法为:
return 值;
return后的值将会作为函数的执行结果返回,在函数中return后面的语句都不会被执行(即函数直接执行结束)。如果return语句后不跟任何值就像当于返回一个undefined,同样,如果函数中不写return,则也会返回undefined。
返回值的类型可以是任意的数据类型,包括对象、函数。
// 函数的返回值
// 返回一个对象
function returnObj(){
return {
name: "匿名对象"
}
}
// 调用函数
console.log(typeof returnObj()); // object
console.log(returnObj().name); // 匿名对象
// 返回一个函数
function f1(){
// 定义f2函数
function f2(){
alert("我是f2");
}
// f2函数对象作为f1函数的返回值
return f2; // 这里返回的是函数对象,如果是f2(),则是调用函数。
}
// 调用f1函数
console.log(f1()); // ƒunction f2(){ alert("我是f2"); }
f1()(); // 相当于直接调用f2函数,浏览器弹出“我是f2”。
13.4、实参可以是任意值
// 实参可以是任意的数据类型,也可以是一个对象.
// 当我们的形参过多时,可以将参数封装到一个对象中,然后通过对象传递。
// 创建一个对象
var user = {
username: "alice",
age: 16,
address: '中国'
}
// 定义一个打印用户信息的函数
function printUserInfo(user_obj){
console.log("大家好,我叫"+user_obj.username+", 今年"+user_obj.age+"岁了,我居住在"+user_obj.address+"。");
}
// 我们给函数传递一个用户对象。
printUserInfo(user); // 大家好,我叫alice, 今年16岁了,我居住在中国。
// 也可以给函数的形参传递另外一个函数
function hellowrold(){
console.log("helloworld");
}
function fun(a){
// 在函数中调用另外一个函数
a();
}
// 调用fun()函数
fun(hellowrold); // helloworld
// 开发中常用的,将一个匿名函数传递个另一个函数
fun(function(){
console.log("我是一个匿名函数");
});
/*
使用函数调用函数的注意点:
printUserInfo(),表示在调用函数,相当于使用函数的返回值。
printUserInfo(),表示函数对象,相当于直接使用函数对象。
*/
13.5、立即执行函数
立即执行函数: 函数定义完,立即被调用,这种函数叫做立即执行函数,立即执行函数只会执行一次。
// 定义立即执行函数
// 必须在函数的外面加上一个括号,表示一个整体。
// 这里定义的这个函数就表示的是一个函数对象,可以在函数对象的后面加上一个括号来对函数进行调用。
(function(){
alert("我是立即执行函数");
})(); // 定义匿名函数,并调用
// 立即执行函数还可以指定参数
(function(a, b){
alert(a+b);
})(123, 456);
13.6、变量/函数的提前声明
变量的提前声明: 使用var关键字声明的变量,会在所有的代码执行之前被声明(但是不会赋值),如果声明变量是不用var关键字,则变量不会被提前声明。
函数的提前声明: 使用函数声明的形式创建的函数function function_name(){};它会在所有的代码执行之间就被创建,所以我们可以在函数声明前来调用函数。使用表达式创建的函数,不会在声明提前,所以不能再声明前调用。
console.log("a = " + a); // a = undefined
// console.log(b); // Uncaught ReferenceError: b is not defined
// 使用var关键字定义变量可以实现提前声明变量,即可以在变量定义之间使用变量,但是没有赋值。
var a = 10;
// 不使用var关键字创建变量
b = 20;
console.log("a = " + a); // a = 10
console.log(b); // 20
// 调用函数
fun(); // 我是一个fun函数
// fun2(); // 报错,Uncaught TypeError: fun2 is not a function
// 使用函数的提前声明创建函数
function fun(){
console.log("我是一个fun函数");
}
// 使用表达式创建函数
var fun2 = function(){
console.log("我是fun2函数");
};
fun2(); // 我是fun2函数
13.7、call()与apply()
这两个方法都是函数对象的方法,需要通过函数对象来调用,当对函数调用call()和apply()时,调用函数都会立即执行。在调用call()和apply()可以将一个对象指定为第一个参数 ,此时这个对象将会成为函数执行时的this。
call()方法可以将实参在对象之后依次传递,apply()方法需要将实参封装到一个数组中统一传递。
// call()与apply()方法
var obj = {name: 'obj'};
var obj2 = {name: 'obj2'};
// 定义函数
function fun(){
console.log(this.name);
}
fun.call(obj); // obj
fun.call(obj2); // obj2
// 定义另外一个类
var obj3 = {
name: 'obj3',
saySelf: function(){
console.log(this);
}
};
// 通过apply()方法与call方法可以改变调用函数是的this。
obj3.saySelf.apply(obj); // {name: 'obj'}
// 定义第二个函数
function fun2(a, b){
console.log(this);
console.log("a -> " + a);
console.log("b -> " + b);
}
// 通过call()与apply()给调用函数传递实参
fun2.call(obj3, 2, 3);
fun2.apply(obj3, [2, 3]);
13.8、arguments
在调用函数时,浏览器每次都会传递今两个隐含的参数:
- 函数上下文对象this;
- 封装实参的对象arguments;
arguments是一个类数组对象(不是数组,也不属于数组,只是和数组比较像),它也可以通过索引来操作数据,也可以获取长度,在调用函数时,我们所传递的实参都会在arguments中保存,arguments.length可以用来获取实参的长度,我们即使不定义形参(即arguments不受形参的影响 ),也可以通过arguments来使用实参,只是比较麻烦。
arguments[0],表示第一个实参;
arguments[1],表示第二个实参;等
arguments中有一个属性叫做callee,这个属性对应一个函数对象,即当前正在指向的函数对象。
// 定义函数
function fun(){
// 检查arguments的类型,以下这两种方法是等效的。
console.log(arguments instanceof Array); // false
console.log(Array.isArray(arguments)); // false
// 通过arguments获取实参
console.log(arguments[0]); // 2
console.log(arguments[1]); // 3
// 获取arguments的长度,即实参的长度
console.log(arguments.length); // 2
console.log(arguments.callee); // 输出fun()函数本身的源代码
}
fun(2, 3);
14、作用域
作用域(scope): 指一变量的作用范围,在js中一共有两种作用域:
- 全局作用域;
- 函数作用域;
14.1、全局作用域
在HTML文件中直接编写在script标签中的js代码,都在全局作用域。全局作用域在页面打开时创建,在页面关闭时销毁。在全局作用域中有一个全局对象window,它代表的是浏览器的窗口,有浏览器创建,我们可以直接使用。全局作用域中定义的变量都是全局变量,在页面中的任何位置都能够访问到。
在全局作用域中,我们创建的所有变量都会作为window对象的属性保存。创建的所有函数都会作为window对象的方法保存。
// 定义全局变量,全局变量默认作为window的属性保存。
var a = 10;
console.log(a); // 10
// 使用这种方式程序不容易报错,假如没有定义变量a,如果我们使用这种方式,只会输出undefined,而不会报错。
console.log(window.a); // 10
// 创建全局函数
var test = function(){
console.log("我是全局函数,是window对象中的方法!");
};
// 这两个函数的输出一样
test();
window.test();
14.2、函数作用域
调用函数时,创建函数的作用域,函数执行完毕以后,函数作用域便会销毁。每调用一次函数就会创建一个新的函数作用域,它们之间是互相独立的。在函数作用域中可以访问到全局作用域的变量,在全局作用域中无法访问到函数作用域的变量。
当在函数作用域操作一个变量时,它会先在自身作用域中寻找,如果有就直接使用,如果没有则向上一级作用域中寻找,知道找到全局作用域。如果在全局作用域中依然没有找到,则会报ReferenceError。
在函数中如果想要访问全局变量,可以使用window对象。在函数中,对于没有使用var关键字声明的变量都会转变为全局变量 。
var a = 10;
function fun(){
var a = "我是fun函数中的变量a";
var b = 20;
console.log(a);
function fun2(){
// 访问全局变量
console.log(window.a);
}
fun2(); // 10
}
fun(); // 我是fun函数中的变量a
// 在函数作用域中也有声明提前的特性。
// 使用var关键字声明的 变量 与 函数声明的函数 都会在函数中所有的代码执行之前被声明。
function fun3(){
console.log(a);
fun4(); // I'm the fun4.
// 局部变量的提前声明
var a = 30;
// 函数声明
function fun4(){
alert("I'm the fun4.");
}
}
fun3(); // undefined
// 在函数中声明变量不使用var关键字
var c = 100;
function fun5(){
console.log(c); // 100
// 没有使用var关键声明变量c,则c会变为全局变量,相当于window.c
c = 30;
}
// 调用fun5函数
fun5(); // 100
console.log(c); // 30
// 定义形参就相当于在函数作用域中声明了变量
var e = 200;
function fun6(e){
// 函数的声明指定了形参e,就相当于在函数中使用var关键字定义了变量e。
console.log(e);
}
fun6(); // undefined
15、this
解析器在调用函数时,每次都会向函数的内部传递一个隐含的参数,这个隐含的参数就是this。this指向的是一个对象,这个对象我们称为函数的上下文对象 ,根据函数的调用方式的不同,this会指向不同的对象。
- 以函数的形式调用时,this永远都是window;
- 以方法的形式调用时,this就是调用方法的那个对象;
// 创建函数
function fun(){
console.log(this);
}
// fun(); // window
// 创建一个对象
var obj = {
name: 'swk',
sayHello: fun
}
obj.sayHello(); // {name: 'swk', sayHello: ƒ}
fun(); // Window {window: Window, self: Window, document: document, name: '', location: Location, …}
16、toString()
当我们直接在页面中输出一个对象时,实际上是输出对象的toString()方法的返回值,如果我们希望在输出对象是不输出[object Object],可以为对象添加一个toString()方法。
// 创建一个类
function Person(name, age){
this.name = name;
this.age = age;
}
// 创建Person实例
var person = new Person("zbj", 20);
console.log(person.toString()); // [object Object]
// 重写Person类原型对象的toString()方法
Person.prototype.toString = function(){
return "Person{name = " + this.name + "age = "+ this.age +"}";
};
console.log(person); // Person {name: 'zbj', age: 20}
17、垃圾回收(GC)
程序在运行过程中会产生垃圾,这些垃圾积攒过多以后,会导致程序运行的速度变慢,所以我们需要一个垃圾回收的机制来处理程序运行过程中产生的垃圾。
当一个对象没有任何的变量或属性对它进行引用时,此时我们将永远无法操作该对象,此时这种对象就变成了一个垃圾,这种对象过多会严重影响系统的运行,所以必须进行清除。
在js中拥有自动的垃圾回收机制,会自动将这些垃圾对象从内存栈中销毁,我们不需要也不能进行垃圾回收的操作。所以我们需要做的只是将不再使用的对象设置为null即可。
18、数组
数组(Array): 数组也是对象,它和普通对象功能类似,也是用来存储一些值的,不同的是普通对象是使用字符串作为属性名,而数组是使用数字来作为索引操作元素。
索引(index): 从0开始的整数。
数组的存储性能比普通对象要好,在开发中我们经常使用数组来存储一些数据。数组中可以存储任意的数据类型,如对象。
18.1、数组的简介
创建数组,语法:
var array_name = new Array();
向数组中添加元素,语法:
array_name[index] = value;
读取数组中的元素,语法:
array_name[index];
如果读取不存在的索引,不会报错而是返回undefined。
通过数组的length属性来获取数组的长度(元素个数),语法:
array_name.length;
对于连续的数组,使用length可以获取到数组的长度(元素个数),而对于非连续的数组,使用length会获取到数组的最大元素的索引+1,所以尽量不要创建非连续的数组,当然我们也可以通过length属性向数组中最后一个位置添加元素。
修改length,如果修改的length大于原长度,则多出来的部分会空出来,如果小于length的长度,则多出来的元素会被删除。
// 创建数组
var arr = new Array();
// 使用typeof关键检查一个元素时返回object。
console.log(typeof arr); // object
// 向数组中添加元素
arr[0] = 123;
arr[1] = 456;
// 读取数组中的元素
console.log(arr[0]); // 123
console.log(arr[1]); // 456
// 获取数组的长度
console.log(arr.length); // 2
// 向数组的最后一个位置添加元素
arr[arr.length] = 30
console.log(arr); // [123, 456, 30]
arr[arr.length] = 50;
console.log(arr); // [123, 456, 30, 50]
// 使用构造函数创建数组时,也可以同时添加元素,将要添加的元素作为构造函数的参数进行传递,元素之间使用逗号隔开。
var arr2 = new Array(10,20,30,40);
// 但是如果Array()构造函数中直接有个数字时,这时表示的是数组的长度,如:
var arr3 = new Array(10); // 定义了一个长度为10的数组
18.2、数组的字面量
使用字面量来创建数组,语法为:
var array_name = [];
使用字面量创建数组时,可以同时指定数组中的元素。
// 使用字面量创建数组
var arr = [1,2,3,4,5];
// 创建一个数组,数组中只有一个元素为10
var test = [10];
// 创建一个长度为10的数组
var test2 = new Array(10);
// 数组中的元素可以是任意的数据类型,包括对象
arr = ['hello', 1, true, null, undefined];
console.log(arr[3]); // null
// 创建对象
var obj = {name: 'swk'};
// 向数组中添加对象
arr[arr.length] = obj;
console.log(typeof arr[5]); // object
console.log(arr[5].name); // swk
// 在数组中直接添加三个对象
arr = [{name: 'swk'},{name: 'abj'}, {name: "shs"}];
// 直接添加两个匿名函数
arr = [function(){alert(1)}, function(){}];
// 调用函数
// arr[0](); // 浏览器弹出1
// 二维数组
arr = [[1,2,3],[4,5,6],[7,8,9]];
console.log(arr[0]); // [1, 2, 3]
18.3、数组的方法
- push(),该方法可以向数组的末尾添加一个或多个元素,并返回数组新的长度,可以将要添加的元素作为方法的参数进行传递,这样这些元素将会自动添加到数组的末尾;
- pop(),删除数组的最后一个元素,并返回该元素;
- unshift(),将新元素添加到数组的开头,并返回新的长度;
- shift(),删除数组的第一个元素,并返回该元素;
- splice(),会影响到原数组;
- concat(),可以连接两个或多个数组,并将新的数组返回,该方法不会对原数组产生影响;
- join(),方法将数组作为字符串返回且不会对原数组产生影响,在join()中可以指定一个字符串作为参数,这个字符串将会成为数组被转为字符串时个元素之间的连接符,如果不指定,默认使用逗号分割。
- reverse(),方法反转数组中元素的顺序,该方法会对原数组产生影响;
- sort(),方法对数组的项目进行排序,默认按照Unicode编码进行排序,该方法会对原数组产生影响;
更多的方法及使用,直接去W3CSchool官网查看即可。
// 创建一个数组
var arr = ['alice', 'mary', 'lily'];
// 通过 int push()函数向数组中添加元素,该方法返回数组的新的长度返回。
console.log(arr.push("margin")); // 4
console.log(arr); // ['alice', 'mary', 'lily', 'margin']
// 往元素的开头添加元素
// 向前边插入元素以后,其他的元素索引依次调整。
arr.unshift("zbj");
console.log(arr); // ['zbj', 'alice', 'mary', 'lily', 'margin']
// splice()方法
/*
可以使用删除数组中的指定元素,使用splice()会影响到原数组,会将指定的元素从原数组中删除,并将被删除的元素返回。
参数:
第一个,表示开始位置的索引;
第二个,表示删除元素的数量;
第三个及以后,可以传递一些新的元素,这些元素将会自动插入到开始位置(及第一个参数)索引的前边。
*/
var test = ['java', 'python', 'javascript', 'android'];
test.splice(1, 2, "c", "c++"); // [java, c, c++, android]
console.log(test);
// concat()方法
var test = ['java', 'python', 'javascript', 'android'];
var language = ['spring', 'springmvc'];
console.log(test.concat(language));
// concat()函数里面不仅可以传递数组,也可以传递元素
// 以下代码输出:['java', 'c', 'c++', 'android', 'spring', 'springmvc', 'mybatis', 'html']
console.log(test.concat(language, 'mybatis', 'html'));
// sort()方法
/*
sort()方法即使对于纯数字的数组,使用sort()排序时,也会按照Unicode编码进行排序,所以这里可能就会得到一个不正确的结果。
我们可以自己指定排序规则,在sort()中添加一个回调函数,用来指定排序规则,回调函数需要两个形参,浏览器将会分别使用数组
中的元素作为实参去调用回调函数,使用那个元素调用是不确定的,但是可以肯定的是回到函数中的a一定在b的前面。
浏览器会根据回调函数的返回值来决定元素的顺序,下面是升序的情况,如果是降序,则1和2交换即可:
1. 如果返回一个大于零的值,则元素会交换位置;
2. 如果返回一个小于零的值,则元素位置不变;
3. 如果返回一个0,则认为两个元素相等,也不交换位置。
*/
var nums = [5,3,2,4,11,6,8,7];
nums.sort(function(a, b){
return a - b;
});
18.4、数组的遍历
所谓的遍历数组,就是将数组中所有的元素都取出来。可以通过for循环来进行遍历数组中的元素。
// 创建数组
var arr = ['a', 'b', 'c', 'd'];
// 遍历数组
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
18.5、forEach
一般都是使用for循环去遍历数组,js中还为我们提供了另一种方法用来遍历数组,forEach()。forEach()函数需要一个函数作为参数,这个函数称为回调函数,即这个函数有我们创建但是不由我们调用(浏览器调用)。数组中有几个元素就会执行几次,每次执行时,浏览器会将遍历到的元素以实参的形式传递进来,我们可以在回调函数中定义形参,来读取这些内容。
浏览器会在回到函数中传递三个参数:
- 第一个,就是当前正在遍历的元素;
- 第二个,就是当前正在遍历的元素的索引;
- 第三个,即使正在遍历的数组;
这个方法只支持IE8以上的浏览器,IE8及以下的浏览器的不支持该方法,所以如果需要兼容IE8,则不要使用forEach()来遍历,而是使用for循环进行遍历。
// 创建数组
var arr = ['a', 'b', 'c', 'd'];
// 遍历数组
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
// 使用forEach遍历数组
arr.forEach(function(value, index, obj){
console.log(value); // abcd
console.log(index); // 0123
console.log(obj); // ['a', 'b', 'c', 'd']
});
影响;
更多的方法及使用,直接去W3CSchool官网查看即可。
// 创建一个数组
var arr = ['alice', 'mary', 'lily'];
// 通过 int push()函数向数组中添加元素,该方法返回数组的新的长度返回。
console.log(arr.push("margin")); // 4
console.log(arr); // ['alice', 'mary', 'lily', 'margin']
// 往元素的开头添加元素
// 向前边插入元素以后,其他的元素索引依次调整。
arr.unshift("zbj");
console.log(arr); // ['zbj', 'alice', 'mary', 'lily', 'margin']
// splice()方法
/*
可以使用删除数组中的指定元素,使用splice()会影响到原数组,会将指定的元素从原数组中删除,并将被删除的元素返回。
参数:
第一个,表示开始位置的索引;
第二个,表示删除元素的数量;
第三个及以后,可以传递一些新的元素,这些元素将会自动插入到开始位置(及第一个参数)索引的前边。
*/
var test = ['java', 'python', 'javascript', 'android'];
test.splice(1, 2, "c", "c++"); // [java, c, c++, android]
console.log(test);
// concat()方法
var test = ['java', 'python', 'javascript', 'android'];
var language = ['spring', 'springmvc'];
console.log(test.concat(language));
// concat()函数里面不仅可以传递数组,也可以传递元素
// 以下代码输出:['java', 'c', 'c++', 'android', 'spring', 'springmvc', 'mybatis', 'html']
console.log(test.concat(language, 'mybatis', 'html'));
// sort()方法
/*
sort()方法即使对于纯数字的数组,使用sort()排序时,也会按照Unicode编码进行排序,所以这里可能就会得到一个不正确的结果。
我们可以自己指定排序规则,在sort()中添加一个回调函数,用来指定排序规则,回调函数需要两个形参,浏览器将会分别使用数组
中的元素作为实参去调用回调函数,使用那个元素调用是不确定的,但是可以肯定的是回到函数中的a一定在b的前面。
浏览器会根据回调函数的返回值来决定元素的顺序,下面是升序的情况,如果是降序,则1和2交换即可:
1. 如果返回一个大于零的值,则元素会交换位置;
2. 如果返回一个小于零的值,则元素位置不变;
3. 如果返回一个0,则认为两个元素相等,也不交换位置。
*/
var nums = [5,3,2,4,11,6,8,7];
nums.sort(function(a, b){
return a - b;
});
18.4、数组的遍历
所谓的遍历数组,就是将数组中所有的元素都取出来。可以通过for循环来进行遍历数组中的元素。
// 创建数组
var arr = ['a', 'b', 'c', 'd'];
// 遍历数组
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
18.5、forEach
一般都是使用for循环去遍历数组,js中还为我们提供了另一种方法用来遍历数组,forEach()。forEach()函数需要一个函数作为参数,这个函数称为回调函数,即这个函数有我们创建但是不由我们调用(浏览器调用)。数组中有几个元素就会执行几次,每次执行时,浏览器会将遍历到的元素以实参的形式传递进来,我们可以在回调函数中定义形参,来读取这些内容。
浏览器会在回到函数中传递三个参数:
- 第一个,就是当前正在遍历的元素;
- 第二个,就是当前正在遍历的元素的索引;
- 第三个,即使正在遍历的数组;
这个方法只支持IE8以上的浏览器,IE8及以下的浏览器的不支持该方法,所以如果需要兼容IE8,则不要使用forEach()来遍历,而是使用for循环进行遍历。
// 创建数组
var arr = ['a', 'b', 'c', 'd'];
// 遍历数组
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
// 使用forEach遍历数组
arr.forEach(function(value, index, obj){
console.log(value); // abcd
console.log(index); // 0123
console.log(obj); // ['a', 'b', 'c', 'd']
});