javascript基础
初识JS
- 控制浏览器弹出一个警告框,
alert("哥,你真帅啊!!"); - 让计算机在页面中输出一个内容,
document.write()可以向 body 中输出一个内容document.write("<h1>我出不出来~~~</h1>"); - 向控制台输出一个内容,
console.log()的作用是向控制台输出一个内容console.log("你猜我在哪出来呢?"); prompt()弹出一个提示框,框中会带有一个文本框,用户可以在文本框中输入一段内容。该函数需要一个字符串作为参数,该字符串将会作为提示框的提示文字。用户输入的内容将会作为函数的返回值返回,可以定义一个变量来接收该内容。
js 代码需要编写到 script 标签中,js 语句都是一条一条执行的。
代码引入方式
- 推荐使用的方式:将js代码编写到外部js文件中,然后通过script标签引入。写到外部文件中可以在不同的页面中同时引用,也可以利用到浏览器的缓存机制。
script 标签一旦用于引入外部文件了,就不能在编写代码了,即使编写了浏览器也会忽略
如果需要则可以在创建一个新的 script 标签用于编写内部代码
<script type="text/javascript" src="js/script.js"></script>将js代码编写到script标签
可以将js代码编写到标签的onclick属性中,当我们点击按钮时,js代码才会执行;可以将js代码写在超链接的href属性中,这样当点击超链接时,会执行js代码
<button onclick="alert('讨厌,你点我干嘛~~');">点我一下</button>
<a href="javascript:alert('让你点你就点!!');">你也点我一下</a>虽然可以写在标签的属性中,但是他们属于结构与行为耦合,不方便维护 —— 不推荐使用
注释
/* 多行注释 */
//单行注释基本语法
- JS 中严格区分大小写
- JS 中每一条语句以分号(;)结尾
- 如果不写分号,浏览器会自动添加,但是会消耗一些系统资源,而且有些时候,浏览器会加错分号,所以在开发中分号必须写
- JS 中会忽略多个空格和换行,所以我们可以利用空格和换行对代码进行格式化
变量与数据类型
字面量和变量
- 字面量,都是一些不可改变的值,比如:1 2 3 4。字面量都是可以直接使用,但是我们一般都不会直接使用字面量。
- 变量,变量可以用来保存字面量,而且变量的值是可以任意改变的。变量更加方便我们使用,所以在开发中都是通过变量去保存一个字面量,而很少直接使用字面量。可以通过变量对字面量进行描述。
//声明变量,在 js 中使用 var 关键字来声明一个变量
var a;
//为变量赋值
a = 123;
//声明和赋值同时进行
var b = 789;
var age = 80;标识符
- 在 JS 中所有的可以由我们自主命名的都可以称为是标识符
- 例如:变量名、函数名、属性名都属于标识符
- 命名一个标识符时需要遵守如下的规则:
- 标识符中可以含有字母、数字、_ 、$
- 标识符不能以数字开头
- 标识符不能是 ES 中的关键字或保留字
- 标识符一般都采用驼峰命名法
- 首字母小写,每个单词的开头字母大写,其余字母小写
helloWorld xxxYyyZzz
- 首字母小写,每个单词的开头字母大写,其余字母小写
JS 底层保存标识符时实际上是采用的 Unicode 编码,所以理论上讲,所有的 utf-8 中含有的内容都可以作为标识符。但是一般不使用中文作为标识符的命名。
数据类型
数据类型的指的就是字面量的类型,在 JS 中一共有六种数据类型:
String 字符串 Number 数值 Boolean 布尔值 Null 空值 Undefined 未定义 Object 对象
其中 String Number Boolean Null Undefined 属于基本数据类型,而 Object 属于引用数据类型
- String 字符串
- 在 JS 中字符串需要使用引号引起来
- 使用双引号或单引号都可以,但是不要混着用
- 引号不能嵌套,双引号里不能放双引号,单引号里不能放单引号
在字符串中我们可以使用 \ 作为转义字符,当表示一些特殊符号时可以使用 \ 进行转义:
\" 表示 "
\' 表示 '
\n 表示换行
\t 制表符
\\ 表示 \输出字面量:alert("str");
输出变量 str:alert(str);
- Number 数值
- 在 JS 中所有的数值都是 Number 类型,包括整数和浮点数(小数)
JS 中可以表示的数字的最大值 Number.MAX_VALUE → 1.7976931348623157e+308, Number.MIN_VALUE → 大于 0 的最小值 5e-324
如果使用 Number 表示的数字超过了最大值,则会返回一个 Infinity 表示正无穷;-Infinity 表示负无穷。注意 Infinity 是一个字面量。
在 JS 中整数的运算基本可以保证精确;如果使用 JS 进行浮点运算,可能得到一个不精确的结果,所以千万不要使用 JS 进行对精度要求比较高的运算
- Boolean 布尔值
布尔值只有两个,主要用来做逻辑判断:
true - 表示真
false - 表示假
- Null 空值
Null(空值) 类型的值只有一个,就是 nullnull 这个值专门用来表示一个为空的对象
- Undefined 未定义
Undefined(未定义) 类型的值只有一个,就 undefined
当声明一个变量,但是并不给变量赋值时,它的值就是 undefined
typeof运算符
typeof 检查一个变量的类型
语法:typeof 变量
- 检查字符串时,会返回
string;检查数值时,会返回number - 使用
typeof检查 Infinity 也会返回number NaN是一个特殊的数字,表示 Not A Number,使用typeof检查一个 NaN 也会返回number- 使用
typeof检查一个布尔值时,会返回boolean - 使用
typeof检查一个null值时,会返回object - 使用
typeof检查一个undefined时也会返回undefined
类型转换
强制类型转换: 指将一个数据类型强制转换为其他的数据类型,类型转换主要指,将其他的数据类型,转换为 String Number Boolean。
将其他的数据类型转换为String
方式一:调用被转换数据类型的
toString()方法- 该方法不会影响到原变量,它会将转换的结果返回
- 但是注意:null 和 undefined 这两个值没有
toString()方法,如果调用他们的方法,会报错
方式二:调用
String()函数,并将被转换的数据作为参数传递给函数- 使用
String()函数做强制类型转换时,对于Number和Boolean实际上就是调用的toString()方法,但是对于null和undefined,就不会调用toString()方法,它会将null直接转换为"null",将undefined直接转换为"undefined"。
- 使用
a = a.toString(); // 调用方法
a = String(a); // 调用函数调用 xxx 的 yyy() 方法,就是
xxx.yyy()
将其他的数据类型转换为 Number
方式一:使用
Number()函数- 字符串 → 数字
- 如果是纯数字的字符串,则直接将其转换为数字
- 如果字符串中有非数字的内容,则转换为
NaN - 如果字符串是一个空串或者是一个全是空格的字符串,则转换为
0
- 布尔 → 数字
true转成1,false转成0 - null → 数字 →
0 - undefined → 数字 →
NaN
- 字符串 → 数字
方式二:使用
parseInt()和parseFloat()函数- 这种方式专门用来对付字符串
parseInt()把一个字符串转换为一个整数parseFloat()把一个字符串转换为一个浮点数parseInt()可以将一个字符串中的有效的整数内容丢出去,然后转换为NumberparseFloat()作用和parseInt()类似,不同的是它可以获得有效的小数- 如果对非 String 使用
parseInt()或parseFloat(),它会先将其转换为 String 然后在操作
在 js 中,如果需要表示 16 进制的数字,则需要以
0x开头。
如果需要表示 8 进制的数字,则需要以0开头。
如果要表示 2 进制的数字,则需要以0b开头,但是不是所有的浏览器都支持。
向"070"这种字符串,有些浏览器会当成 8 进制解析,有些会当成 10 进制解析。
可以在parseInt()中传递一个第二个参数,来指定数字的进制。
将其他的数据类型转换为 Boolean
- 使用
Boolean()函数- 数字 → 布尔
- 除了
0和NaN,其余的都是true
- 除了
- 字符串 → 布尔
- 除了空串,其余的都是
true
- 除了空串,其余的都是
null和undefined都会转换为false- 对象也会转换为
true
- 数字 → 布尔
运算符
运算符也叫操作符
通过运算符可以对一个或多个值进行运算,并获取运算结果。
比如:typeof 就是运算符,可以获得一个值的类型,它会将该值的类型以字符串的形式返回:number string boolean undefined object
算数运算符
当对非 Number 类型的值进行运算时,会将这些值转换为 Number 然后在运算
任何值和 NaN 做运算都得 NaN
加减乘除取模 +-*/%
加法 +
- 可以对两个值进行加法运算,并将结果返回
- 如果对两个字符串进行加法运算,则会做拼接。会将两个字符串拼接为一个字符串,并返回。任何的值和字符串做加法运算,都会先转换为字符串,然后再和字符串做拼接的操作
减法 -
- 可以对两个值进行减法运算,并将结果返回
乘法 *
- 可以对两个值进行乘法运算
除法 /
- 可以对两个值进行除法运算
取模 %
- 取模运算(取余数)
⚠️ 注意
任何值和字符串相加都会转换为字符串,并做拼接操作
为任意的数据类型 + 一个 '' 即可将其转换为 String。这是一种隐式的类型转换,实际上它也是调用 String() 函数。
任何值做 - * / 运算时都会自动转换为 Number。可以通过为一个值
-0 *1 /1来将其转换为 Number,原理和 Number() 函数一样。
一元运算符(+,-,++,--)
- 正负号 +,-
- + 正号,正号不会对数字产生任何影响
- - 负号,负号可以对数字进行负号的取反
对于非 Number 类型的值,它会先将转换为 Number,然后在运算。可以对一个其他的数据类型使用 +,来将其转换为 number,它的原理和 Number() 函数一样。
- 自增 自减 ++ --
- 通过自增可以使变量在自身的基础上增加 1
- 对于一个变量自增以后,原变量的值会立即自增 1
- 自增分成两种:后++ (
a++) 和前++ (++a)
自减 同理
无论是
a++还是++a,都会立即使原变量的值自增 1
不同的是a++和++a的值不同:
a++的值等于原变量的值(自增前的值)++a的值等于新值 (自增后的值)
逻辑运算符
! 非
- 可以用来对一个值进行非运算,所谓非运算就是对一个布尔值进行取反操作,
true变false,false变true。如果对一个值进行两次取反,它不会变化。如果对非布尔值进行非运算,则会将其转换为布尔值,然后再取反,所以我们可以利用该特点,来将一个其他的数据类型转换为布尔值。 - 可以为一个任意数据类型取两次反,来将其转换为布尔值,原理和
Boolean()函数一样
&& 与
&& 可以对符号两侧的值进行与运算并返回结果
运算规则:
- 两个值中只要有一个值为
false就返回false,只有两个值都为true时,才会返回true - JS 中的 “与” 属于短路的与,如果第一个值为
false,则不会看第二个值
|| 或
|| 可以对符号两侧的值进行或运算并返回结果
运算规则:
- 两个值中只要有一个
true,就返回true,如果两个值都为false,才返回false - JS 中的 “或” 属于短路的或,如果第一个值为
true,则不会检查第二个值
⚠️ 注意
&& || 非布尔值的情况
对于非布尔值进行与或运算时,会先将其转换为布尔值,然后再运算,并且返回原值
与运算:
- 如果第一个值为
true,则必然返回第二个值 - 如果第一个值为
false,则直接返回第一个值
- 如果第一个值为
或运算:
- 如果第一个值为
true,则直接返回第一个值 - 如果第一个值为
false,则返回第二个值
- 如果第一个值为
赋值运算符
可以将符号右侧的值赋值给符号左侧的变量
+= a += 5 等价于 a = a + 5
-= a -= 5 等价于 a = a - 5
*= a *= 5 等价于 a = a * 5
/= a /= 5 等价于 a = a / 5
%= a %= 5 等价于 a = a % 5关系运算符
通过关系运算符可以比较两个值之间的大小关系,如果关系成立它会返回 true,如果关系不成立则返回 false
大于小于
大于号
- 判断符号左侧的值是否大于右侧的值
>= 大于等于
- 判断符号左侧的值是否大于或等于右侧的值
< 小于号
- 判断符号左侧的值是否小于右侧的值
<= 小于等于
- 判断符号左侧的值是否小于或等于右侧的值
⚠️ 注意:非数值的情况
- 对于非数值进行比较时,会将其转换为数字然后在比较
- 如果符号两侧的值都是字符串时,不会将其转换为数字进行比较,而会分别比较字符串中字符的 Unicode 编码
任何值和 NaN 做任何比较都是 false
比较字符编码时是一位一位进行比较,如果两位一样,则比较下一位,所以通常用它来对英文进行排序。
比较中文时没有意义。
如果比较的两个字符串型的数字,可能会得到不可预期的结果。
在比较两个字符串型的数字时,一定一定要转型!
在 JS 字符串中使用转义字符输入 Unicode 编码 \u 四位编码
console.log("\u2620");在网页中使用 Unicode 编码,&#编码;这里的编码需要的是 10 进制
等于不等于
相等运算符:比较两个值是否相等,如果相等会返回true,否则返回false
- 使用
==来做相等运算,如果值的类型不同,则会自动进行类型转换,将其转换为相同的类型,然后在比较 - 使用
===来做全等运算,和相等类似,但不会自动进行类型转换
- 使用
不相等运算符:判断两个值是否不相等,如果不相等返回true,否则返回false
- 使用
!=来做不相等运算,会对变量进行自动的类型转换,如果转换后相等它也会返回false - 使用
!==来做不全等运算,和不等类似,不同的是它不会做自动的类型转换
- 使用
⚠️ 注意:特殊情况
console.log(null == 0); // falseundefined 衍生自 null,所以这两个值做相等判断时,会返回 true
NaN 不和任何值相等,包括他本身
可以通过 isNaN() 函数来判断一个值是否是 NaN,如果该值是 NaN 则返回 true,否则返回 false
条件运算符
条件运算符也叫三元运算符
语法:条件表达式 ? 语句1 : 语句2;
执行的流程:
条件运算符在执行时,首先对条件表达式进行求值。
- 如果该值为
true,则执行语句 1,并返回执行结果 - 如果该值为
false,则执行语句 2,并返回执行结果
如果条件的表达式的求值结果是一个非布尔值,会将其转换为布尔值然后在运算
运算符优先级
在 JS 中有一个运算符优先级的表,在表中越靠上优先级越高,优先级越高越优先计算,如果优先级一样,则从左往右计算。
[]、new
0
++, --
!, ~, +(单目), -(单目), typeof, void, delete
%, *, /
+(双目), -(双目)
<<, >>, >>>
<, <=, >, >=
==, !=, ===, !==
&
^
|
&&
||
?:
=, +=, -=, *=, /=, %=, <<=, >>=, >>>=, &=, ^=, |=不需要记忆,如果遇到优先级不清楚,可以使用
()来改变优先级
流程控制语句
JS 中的程序是从上到下一行一行执行的,通过流程控制语句可以控制程序执行流程,使程序可以根据一定的条件来选择执行
- 语句的分类:
- 条件判断语句
- 条件分支语句
- 循环语句
代码块
我们的程序是由一条一条语句构成的,语句是按照自上向下的顺序一条一条执行的
在 JS 中可以使用 {} 来为语句进行分组,同一个 {} 中的语句我们称为是一组语句,它们要么都执行,要么都不执行,一个 {} 中的语句我们也称为叫一个代码块,在代码块的后边就不用再编写了。
JS 中的代码块,只具有分组的作用,没有其他的用途,代码块内容的内容,在外部是完全可见的。
条件判断语句
使用条件判断语句可以在执行某个语句之前进行判断,如果条件成立才会执行语句,条件不成立则语句不执行。
if 语句
if(条件表达式){
语句...
}if 语句在执行时,会先对条件表达式进行求值判断:
- 如果条件表达式的值为
true,则执行if后的语句; - 如果该值为
false,则不会执行if后的语句。
注意: if 语句只能控制紧随其后的那个语句。如果希望
if语句可以控制多条语句,可以将这些语句统一放到代码块中。
if(条件表达式){
语句...
}else{
语句...
}if...else...语句: 当该语句执行时,会先对 if 后的条件表达式进行求值判断:
- 如果该值为
true,则执行if后的语句; - 如果该值为
false,则执行else后的语句。
if(条件表达式){
语句...
}else if(条件表达式){
语句...
}else if(条件表达式){
语句...
}else{
语句...
}if...else if...else: 当该语句执行时,会从上到下依次对条件表达式进行求值判断:
- 如果值为
true,则执行当前语句; - 如果值为
false,则继续向下判断。 - 如果所有的条件都不满足,则执行最后一个
else后的语句。 - 该语句中,只会有一个代码块被执行,一旦代码块执行了,则直接结束语句。
条件分支语句
条件分支语句也叫 switch 语句
switch(条件表达式){
case 表达式:
语句...
break;
case 表达式:
语句...
break;
default:
语句...
break;
}switch...case...语句
在执行时会依次将 case 后的表达式的值和 switch 后的条件表达式的值进行全等比较,如果比较结果为 true,则从当前 case 处开始执行代码。当前 case 后的所有的代码都会执行,我们可以在 case 的后边跟着一个 break 关键字,这样可以确保只会执行当前 case 后的语句,而不会执行其他的 case。如果比较结果为 false,则继续向下比较。如果所有的比较结果都为 false,则只执行 default 后的语句。
注意: switch 语句和 if 语句的功能实际上有重复的,使用 switch 可以实现 if 的功能,同样使用 if 也可以实现 switch 的功能,所以我们使用时,可以根据自己的习惯选择。
循环语句
通过循环语句可以反复的执行一段代码多次
while 循环
while(条件表达式){
语句...
}while 语句在执行时,先对条件表达式进行求值判断:
- 如果值为
true,则执行循环体; - 循环体执行完毕以后,继续对表达式进行判断;
- 如果为
true,则继续执行循环体,以此类推; - 如果值为
false,则终止循环。
do...while 循环
do{
语句...
}while(条件表达式)执行流程:
do...while 语句在执行时,会先执行循环体,循环体执行完毕以后,在对 while 后的条件表达式进行判断:
- 如果结果为
true,则继续执行循环体,执行完毕继续判断,以此类推; - 如果结果为
false,则终止循环。
对比: 实际上这两个语句功能类似,不同的是
while是先判断后执行,而do...while会先执行后判断。do...while可以保证循环体至少执行一次,而while不能。
for 循环
for 语句也是一个循环语句,也称为 for 循环。
在 for 循环中,为我们提供了专门的位置来放三个表达式:
for(①初始化表达式; ②条件表达式; ④更新表达式){
③循环体语句...
}- 执行初始化表达式,初始化变量(初始化表达式只会执行一次)
- 执行条件表达式,判断是否执行循环。
- 如果为true,则执行循环体语句
- 如果为false,终止循环 - 执行更新表达式,更新表达式执行完毕继续重复2
⚠️ 注意
for 循环中的三个部分都可以省略,也可以写在外部。
如果在for循环中不写任何的表达式,只写两个';',此时是死循环会一直执行下去,慎用。
循环控制关键字
- break 关键字
可以用来退出 switch 或循环语句。不能在 if 语句中使用 break 和 continue。
break 关键字,会立即终止离他最近的那个循环语句。
可以为循环语句创建一个 label,来标识当前的循环。
- label:循环语句
使用 break 语句时,可以在 break 后跟着一个 label,这样 break 将会结束指定的循环,而不是最近的。
- continue 关键字
可以用来跳过当次循环,同样 continue 也是默认只会对离他最近的循环起作用。
对象
对象 Object
对象属于一种复合的数据类型,在对象中可以保存多个不同数据类型的属性。
对象的分类
内建对象
- 由 ES 标准中定义的对象,在任何的 ES 的实现中都可以使用
- 比如:Math String Number Boolean Function Object....
宿主对象
- 由 JS 的运行环境提供的对象,目前来讲主要指由浏览器提供的对象
- 比如 BOM DOM
自定义对象
- 由开发人员自己创建的对象
- 创建对象
- 使用
new关键字调用的函数,是构造函数constructor。构造函数是专门用来创建对象的函数。使用typeof检查一个对象时,会返回object,在对象中保存的值称为属性。 - 使用对象字面量创建对象时,直接指定对象中的属性。
- 使用
对象字面量创建对象语法
{属性名:属性值, 属性名:属性值...}- 对象字面量的属性名可加引号也可不加;如果要使用一些特殊的名字,则必须加引号。
- 属性名和值是一组一组的名值对结构,名和值之间使用
:连接,多个名值对之间使用,隔开。 - 如果一个属性之后没有其他属性了,
,就不要写了。
- 向对象添加属性
- 语法:
对象.属性名 = 属性值; - 如果要使用特殊的属性名,不能采用
.的方式来操作。需要使用另一种方式:语法:对象["属性名"] = 属性值
- 语法:
- 使用
[]这种形式去读取属性时也需要采用这种方式。- 在
[]中可以直接传递一个变量。- JS 对象的属性值,可以是任意的数据类型,甚至也可以是一个对象。
读取对象中的属性
- 语法:
对象.属性名 - 如果读取对象中没有的属性,不会报错而是会返回
undefined
- 语法:
修改对象的属性值
- 语法:
对象.属性名 = 新值
- 语法:
删除对象的属性
- 语法:
delete 对象.属性名
- 语法:
in运算符
通过该运算符可以检查一个对象中是否含有指定的属性。如果有则返回 true,没有则返回 false。
"属性名" in 对象;对象的方法
对象的属性值可以是任何的数据类型,也可以是个函数。
函数也可以作为对象的属性,如果一个函数作为一个对象的属性保存,那么我们称这个函数是这个对象的方法。调用这个函数就说调用对象的方法(method)。
但是调用方法和调用函数只是名称上的区别没有其他的区别。
函数 function
函数也是一个对象,函数中可以封装一些代码,在需要时可以执行这些代码。
函数中可以保存一些代码在需要的时候调用,使用 typeof 检查一个函数对象时,会返回 function。
- 函数的创建
我们在实际开发中很少使用构造函数来创建一个函数对象,可以将要封装的代码以字符串的形式传递给构造函数:
// 封装到函数中的代码不会立即执行,函数中的代码会在函数调用的时候执行。
var fun = new Function("console.log('Hello 这是我的第一个函数');");使用 函数声明 来创建一个函数
function 函数名([形参1, 形参2...形参N]){
语句...
}使用 函数表达式 来创建一个函数
var 函数名 = function([形参1, 形参2...形参N]){
语句...
}- 函数的调用
调用函数 语法:函数对象()
当调用函数时,函数中封装的代码会按照顺序执行。
- 形参与实参
可以在函数的 () 中来指定一个或多个形参(形式参数)。多个形参之间使用 , 隔开,声明形参就相当于在函数内部声明了对应的变量,但是并不赋值。
在调用函数时,可以在 () 中指定实参(实际参数),实参将会赋值给函数中对应的形参。
函数参数说明
调用函数时解析器不会检查实参的类型,所以要注意,是否有可能会接收到非法的参数,如果有可能则需要对参数进行类型的检查。函数的实参可以是任意的数据类型。
调用函数时,解析器也不会检查实参的数量,多余实参不会被赋值。如果实参的数量少于形参的数量,则没有对应实参的形参将是
undefined。
实参可以是任意的数据类型,也可以是一个对象。当我们的参数过多时,可以将参数封装到一个对象中,然后通过对象传递。实参可以是一个对象,也可以是一个函数。
- 返回值
可以使用 return 来设置函数的返回值
return 值;返回值可以是任意的数据类型
return后的值将会作为函数的执行结果返回,可以定义一个变量,来接收该结果。
- 如果
return语句后不跟任何值就相当于返回一个undefined,如果函数中不写return,则也会返回undefined。 return后可以跟任意类型的值。
⚠️ 注意
在函数中 return 后的语句都不会执行。
mianji(); // 调用函数 - 相当于使用的函数的返回值;
mianji; // 函数对象 - 相当于直接使用函数对象;立即执行函数IIFE
函数定义完,立即被调用,这种函数叫做立即执行函数。立即执行函数往往只会执行一次。
(function () {
alert("我是一个匿名函数~~~");
})();提示
在调用函数时,浏览器每次都会传递进两个隐含的参数:
- 函数的上下文对象
this
- 函数的上下文对象
- 封装实参的对象
arguments
- 封装实参的对象
arguments
- 是一个类数组对象,它也可以通过索引来操作数据,也可以获取长度
- 在调用函数时,我们所传递的实参都会在
arguments中保存 arguments.length可以用来获取实参的长度- 我们即使不定义形参,也可以通过
arguments来使用实参,只不过比较麻烦arguments[0]表示第一个实参arguments[1]表示第二个实参 ……
- 它里边还有一个属性叫做
callee,- 这个属性对应一个函数对象,就是当前正在指向的函数的对象
构造函数
创建一个构造函数,专门用来创建 Person 对象的。构造函数就是一个普通的函数,创建方式和普通函数没有区别,不同的是构造函数习惯上首字母大写。
构造函数和普通函数的区别就是调用方式的不同。普通函数是直接调用,而构造函数需要使用
new关键字来调用。
构造函数的执行流程
- 立刻创建一个新的对象
- 将新建的对象设置为函数中
this,在构造函数中可以使用this来引用新建的对象
- 将新建的对象设置为函数中
- 逐行执行函数中的代码
- 将新建的对象作为返回值返回
使用同一个构造函数创建的对象,我们称为一类对象,也将一个构造函数称为一个类。
我们将通过一个构造函数创建的对象,称为是该类的实例。
call、apply、bind
- 这三个方法都是函数对象的方法,需要通过函数对象来调用
- 当对函数调用
call()和apply()都会调用函数执行 - 在调用
call()和apply()时可以将一个对象指定为第一个参数,此时这个对象将会成为函数执行时的this。call()方法可以将实参在对象之后依次传递apply()方法需要将实参封装到一个数组中统一传递
bind()方法可以将一个对象指定为第一个参数,此时这个对象将会成为函数执行时的this,但是不会立即调用函数。bind()方法可以将实参在对象之后依次传递
数组 Array
- 数组也是一个对象,它和我们普通对象功能类似,也是用来存储一些值的。不同的是普通对象是使用字符串作为属性名的,而数组时使用数字来作为索引操作元素。
- 索引:从 0 开始的整数就是索引。
- 数组的存储性能比普通对象要好,在开发中我们经常使用数组来存储一些数据。
创建数组对象
var arr = new Array();使用字面量来创建数组
语法:[] // var arr = [];
说明
- 使用字面量创建数组时,可以在创建时就指定数组中的元素。
- 使用构造函数创建数组时,也可以同时添加元素,将要添加的元素作为构造函数的参数传递,元素之间使用 “,” 隔开
- 数组中的元素可以是任意的数据类型。
向数组中添加元素
语法:数组[索引] = 值
读取数组中的元素
语法:数组[索引]
- 如果读取不存在的索引,他不会报错而是返回
undefined
获取数组的长度
语法:数组.length
- 可以使用 length 属性来获取数组的长度(元素的个数)
- 对于连续的数组,使用 length 可以获取到数组的长度(元素的个数)
- 对于非连续的数组,使用 length 会获取到数组的最大的索引+1,尽量不要创建非连续的数组。
修改 length
- 如果修改的 length 大于原长度,则多出部分会空出来
- 如果修改的 length 小于原长度,则多出的元素会被删除
向数组的最后一个位置添加元素
语法:数组[数组.length] = 值;
数组中也可以放数组,这种数组我们称为 二维数组。
数组的方法
push()
- 该方法可以向数组的末尾添加一个或多个元素,并返回数组的新长度。可以将要添加的元素作为方法的参数传递,这样这些元素将会自动添加到数组的末尾。该方法会将数组新的长度作为返回值返回。
pop()
- 该方法可以删除数组的最后一个元素,并将被删除的元素作为返回值返回。
unshift()
- 向数组开头添加一个或多个元素,并返回新的数组长度。向前边插入元素以后,其他的元素索引会依次调整。
shift()
- 可以删除数组的第一个元素,并将被删除的元素作为返回值返回。
slice()
- 可以用来从数组提取指定元素
- 该方法不会改变原数组,而是将截取到的元素封装到一个新数组中返回
- 参数:
- 截取开始的位置的索引,包含开始索引
- 截取结束的位置的索引,不包含结束索引
- 第二个参数可以省略不写,此时会截取从开始索引往后的所有元素
- 索引可以传递一个负值,如果传递一个负值,则从后往前计算
-1倒数第一个-2倒数第二个
splice()
- 可以用于删除数组中的指定元素
- 使用
splice()会影响到原数组,会将指定元素从原数组中删除,并将被删除的元素作为返回值返回 - 参数:
- 第一个,表示开始位置的索引
- 第二个,表示删除的数量
- 第三个及以后……
- 可以传递一些新的元素,这些元素将会自动插入到开始位置索引前边
concat()
- 可以连接两个或多个数组,并将新的数组返回,不会对原数组产生影响
join()
- 该方法可以将数组转换为一个字符串,不会对原数组产生影响,而是将转换后的字符串作为结果返回
- 在
join()中可以指定一个字符串作为参数,这个字符串将会成为数组中元素的连接符- 如果不指定连接符,则默认使用 “,” 作为连接符
reverse()
- 该方法用来反转数组(前边的去后边,后边的去前边),会直接修改原数组
sort()
- 可以用来对数组中的元素进行排序
- 也会影响原数组,默认会按照 Unicode 编码进行排序
- 即使对于纯数字的数组,使用
sort()排序时,也会按照 Unicode 编码来排序,所以对数字进排序时,可能会得到错误的结果。
- 即使对于纯数字的数组,使用
- 我们可以自己来指定排序的规则
- 我们可以在
sort()添加一个回调函数,来指定排序规则,回调函数中需要定义两个形参,浏览器将会分别使用数组中的元素作为实参去调用回调函数。使用哪个元素调用不确定,但是肯定的是在数组中 a 一定在 b 前边 - 浏览器会根据回调函数的返回值来决定元素的顺序:
- 如果返回一个大于 0 的值,则元素会交换位置;
- 如果返回一个小于 0 的值,则元素位置不变;
- 如果返回一个 0,则认为两个元素相等,也不交换位置。
- 我们可以在
结论:
如果需要升序排列,则返回 a - b;
如果需要降序排列,则返回 b - a。
arr.sort(function (a, b) {
return b - a;
});遍历数组
for (var i = 0; i < arr.length; i++) {
console.log(arr[i]);
}一般我们都是使用 for 循环来遍历数组,JS 中还为我们提供了一个forEach()方法,用来遍历数组。
这个方法只支持 IE8 以上的浏览器,IE8 及以下的浏览器均不支持该方法,所以如果需要兼容 IE8,则不要使用
forEach。还是使用for循环来遍历。forEach()方法需要一个函数作为参数- 像这种函数,由我们创建但不是由我们调用的,我们称为回调函数
- 数组中有几个元素,函数就会执行几次,每次执行时,浏览器会将遍历到的元素以实参的形式传递进来,我们可以来定义形参,来读取这些内容
在回调函数中传递三个参数:
- 第一个参数,就是当前正在遍历的元素
- 第二个参数,就是当前正在遍历的元素的索引
- 第三个参数,就是正在遍历的数组
arr.forEach(function (元素, 索引, 数组) {
console.log(元素, 索引, 数组);
});Date 对象
- 在 JS 中使用 Date 对象来表示一个时间
- 创建一个 Date 对象,如果直接使用构造函数创建一个 Date 对象,则会封装为当前代码执行的时间。js
var d = new Date(); - 创建一个指定的时间对象,需要在构造函数中传递一个表示时间的字符串作为参数,日期的格式:月份/日/年 时:分:秒。js
var d2 = new Date("2/18/2011 11:10:30");
Date 方法
getDate() — 获取当前日期对象是几号
getDay() — 获取当前日期对象是周几
- 会返回一个 0~6 的值
0表示周日1表示周一 ……
- 会返回一个 0~6 的值
getMonth() — 获取当前时间对象的月份
- 会返回一个 0~11 的值
0表示 1 月1表示 2 月 ……11表示 12 月
- 会返回一个 0~11 的值
getFullYear() — 获取当前日期对象的年份
getTime() — 获取当前日期对象的时间戳
- 时间戳,指的是从格林威治标准时间的 1970 年 1 月 1 日,0 时 0 分 0 秒 到当前日期所经过的毫秒数。
计算机底层在保存时间时使用的都是时间戳
可以利用时间戳来测试代码的执行的性能
var start = Date.now();
for (var i = 0; i < 100; i++) {
console.log(i);
}
var end = Date.now();
console.log("执行了:" + (end - start) + "毫秒");Math 对象
- Math.PI — 表示的圆周率
- Math.abs() — 可以用来计算一个数的绝对值
- Math.ceil() — 可以对一个数进行向上取整,小数位只要有值就自动进 1
- Math.floor() — 可以对一个数进行向下取整,小数部分会被舍掉
- Math.round() — 可以对一个数进行四舍五入取整
- Math.random() — 可以用来生成一个 0~1 之间的随机数
→ 生成一个 x~y 之间的随机数:Math.round(Math.random()*(y-x)+x) - max() — 可以获取多个数中的最大值
var max = Math.max(10,45,30,100); - min() — 可以获取多个数中的最小值
var min = Math.min(10,45,30,100); - Math.pow(x,y) — 返回 x 的 y 次幂
- Math.sqrt() — 用于对一个数进行开方运算
包装类
在 JS 中为我们提供了三个包装类,通过这三个包装类可以将基本数据类型的数据转换为对象。
- String() — 将字符串转换为 String 对象
- Number() — 将数字转换为 Number 对象
- Boolean() — 将布尔值转换为 Boolean 对象
⚠️ 注意
我们在实际应用中不会使用基本数据类型的对象,如果使用基本数据类型的对象,在做一些比较时可能会带来一些不可预期的结果。
方法和属性只能添加给对象,不能添加给基本数据类型。当我们对一些基本数据类型的值去调用属性和方法时,浏览器会临时使用包装类将其转换为对象,然后在调用对象的属性和方法。调用完以后,再将其转为基本数据类型。
字符串方法
在计算机底层,字符串是以字符数组的形式保存的。
- length 属性 — 可以用来获取字符串的长度
- charAt(i) — 可以返回字符串中指定位置的字符,根据索引获取指定的字符。
- charCodeAt() — 获取指定位置字符的字符编码(Unicode 编码)
- fromCharCode() — 可以根据字符编码去获取字符
- concat() — 可以用来连接两个或多个字符串,作用和
+一样。 - indexOf() — 该方法可以检索一个字符串中是否含有指定内容,如果字符串中含有该内容,则会返回其第一次出现的索引;如果没有找到指定的内容,则返回
-1。可以指定一个第二个参数,指定开始查找的位置。 - lastIndexOf() — 该方法的用法和
indexOf()一样,不同的是indexOf是从前往后找,而lastIndexOf是从后往前找。也可以指定开始查找的位置。 - slice() — 可以从字符串中截取指定的内容,不会影响原字符串,而是将截取到的内容返回
- 参数:
- 第一个,开始位置的索引(包括开始位置)
- 第二个,结束位置的索引(不包括结束位置),如果省略第二个参数,则会截取到后边所有的。也可以传递一个负数作为参数,负数的话将会从后边计算
- 参数:
- substring() — 可以用来截取一个字符串,与
slice()类似- 参数:
- 第一个:开始截取位置的索引(包括开始位置)
- 第二个:结束位置的索引(不包括结束位置)
- 不同的是这个方法不能接受负值作为参数,如果传递了一个负值,则默认使用
0。而且他还自动调整参数的位置,如果第二个参数小于第一个,则自动交换。
- 参数:
- substr() — 用来截取字符串
- 参数:
- 截取开始位置的索引
- 截取的长度
- 参数:
- split() — 将一个字符串拆分为一个数组
- 参数: 需要一个字符串作为参数,将会根据该字符串去拆分数组。如果传递一个空串作为参数,则会将每个字符都拆分为数组中的一个元素
- toUpperCase() — 将一个字符串转换为大写并返回
- toLowerCase() — 将一个字符串转换为小写并返回
- split() - 可以将一个字符串拆分为一个数组
- 方法中可以传递一个正则表达式作为参数,这样方法将会根据正则表达式去拆分字符串。这个方法即使不指定全局匹配,也会全都拆分。
- search()
- 可以搜索字符串中是否含有指定内容
- 如果搜索到指定内容,则会返回第一次出现的索引,如果没有搜索到返回
-1 - 它可以接受一个正则表达式作为参数,然后会根据正则表达式去检索字符串
search()只会查找第一个,即使设置全局匹配也没用
- match() - 可以根据正则表达式,从一个字符串中将符合条件的内容提取出来
- 默认情况下我们的
match只会找到第一个符合要求的内容,找到以后就停止检索 - 我们可以设置正则表达式为全局匹配模式,这样就会匹配到所有的内容
- 可以为一个正则表达式设置多个匹配模式,且顺序无所谓
match()会将匹配到的内容封装到一个数组中返回,即使只查询到一个结果
- 默认情况下我们的
- replace()
- 可以将字符串中指定内容替换为新的内容
- 参数:
- 被替换的内容,可以接受一个正则表达式作为参数
- 新的内容
- 默认只会替换第一个
原型 与 作用域
原型 prototype
我们所创建的每一个函数,解析器都会向函数中添加一个属性 prototype,这个属性对应着一个对象,这个对象就是我们所谓的原型对象。
如果函数作为普通函数调用,prototype 没有任何作用。当函数以构造函数的形式调用时,它所创建的对象中都会有一个隐含的属性,指向该构造函数的原型对象,我们可以通过 __proto__ 来访问该属性。
原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,我们可以将对象中共有的内容,统一设置到原型对象中。当我们访问对象的一个属性或方法时,它会先在对象自身中寻找,如果有则直接使用;如果没有则会去原型对象中寻找,如果找到则直接使用。
以后我们创建构造函数时,可以将这些对象共有的属性和方法,统一添加到构造函数的原型对象中,这样不用分别为每一个对象添加,也不会影响到全局作用域,就可以使每个对象都具有这些属性和方法了。
instanceof
使用 instanceof 可以检查一个对象是否是一个类的实例
对象 instanceof 构造函数;- 如果是,则返回
true;否则返回false。 - 所有的对象都是
Object的后代,所以任何对象和Object作instanceof检查时都会返回true。
in 检查对象属性
- 如果对象中没有但是原型中有,也会返回
true。 - 可以使用对象的
hasOwnProperty()来检查对象自身中是否含有该属性,使用该方法只有当对象自身中含有属性时,才会返回true。
原型对象也是对象,所以它也有原型。当我们使用一个对象的属性或方法时,会先在自身中寻找,自身中如果有,则直接使用;如果没有则去原型对象中寻找,如果原型对象中有,则使用;如果没有则去原型的原型中寻找,直到找到
Object对象的原型。
Object 对象的原型没有原型,如果在
Object原型中依然没有找到,则返回undefined。
作用域
作用域指一个变量的作用的范围。
在 JS 中一共有两种作用域:全局作用域和函数作用域。
- 全局作用域
- 直接编写在
<script>标签中的 JS 代码,都在全局作用域 - 全局作用域在页面打开时创建,在页面关闭时销毁
- 在全局作用域中有一个全局对象
window,它代表的是一个浏览器的窗口,它由浏览器创建我们可以直接使用
特性
- 创建的变量都会作为
window对象的属性保存。 - 创建的函数都会作为
window对象的方法保存。 - 全局作用域中的变量都是全局变量,在页面的任意的部分都可以访问的到。
- 函数作用域
- 调用函数时创建函数作用域,函数执行完毕以后,函数作用域销毁
- 每调用一次函数就会创建一个新的函数作用域,他们之间是互相独立的
作用域链
- 在函数作用域中可以访问到全局作用域的变量,在全局作用域中无法访问到函数作用域的变量。
- 当在函数作用域操作一个变量时,它会先在自身作用域中寻找,如果有就直接使用;如果没有则向上一级作用域中寻找,直到找到全局作用域。如果全局作用域中依然没有找到,则会报错
ReferenceError。 - 在函数中要访问全局变量可以使用
window对象。
声明提前
变量的声明提前
- 使用
var关键字声明的变量,会在所有的代码执行之前被声明(但就不会赋值)。 - 但是如果声明变量时不使用
var关键字,则变量不会被声明提前。
函数的声明提前
- 使用函数声明形式创建的函数
function 函数(){},它会在所有的代码执行之前就被创建,所以我们可以在函数声明前来调用函数。 - 使用函数表达式创建的函数,不会被声明提前,所以不能在声明前调用。
在函数作用域也有声明提前的特性,使用
var关键字声明的变量,会在函数中所有的代码执行之前被声明。函数声明也会在函数中所有的代码执行之前执行。在函数中,不使用var声明的变量都会成为全局变量。定义形参就相当于在函数作用域中声明了变量。
this
解析器在调用函数每次都会向函数内部传递进一个隐含的参数,这个隐含的参数就是 this。this 指向的是一个对象,这个对象我们称为函数执行的“上下文对象”。
根据函数的调用方式的不同,this 会指向不同的对象:
- 以函数的形式调用时,
this永远都是window - 以方法的形式调用时,
this就是调用方法的那个对象 - 以构造函数的形式调用时,
this就是新创建的那个对象 - 使用 call 和 apply 调用时,
this是指定的那个对象
附录
附录1: console.time
console.time("计时器的名字")
可以用来开启一个计时器
它需要一个字符串作为参数,这个字符串将会作为计时器的标识
console.timeEnd("计时器的名字")
用来停止一个计时器,需要一个计时器的名字作为参数
附录2: 变量与内存(堆栈)
JS 中的变量都是保存到栈内存中的。
基本数据类型的值直接在栈内存中存储,值与值之间是独立存在,修改一个变量不会影响其他的变量。
对象是保存到堆内存中的,每创建一个新的对象,就会在堆内存中开辟出一个新的空间,而变量保存的是对象的内存地址(对象的引用)。如果两个变量保存的是同一个对象引用,当一个通过一个变量修改属性时,另一个也会受到影响。
⚠️ 注意
当比较两个基本数据类型的值时,就是比较值。而比较两个引用数据类型时,它是比较的对象的内存地址,如果两个对象是一模一样的,但是地址不同,它也会返回 false。
附录3: for/in 和 for/of
| 特性 | for...in | for...of |
|---|---|---|
| 遍历内容 | 对象的键名 (key) | 可迭代对象的值 (value) |
| 适用对象 | 普通对象 (Object) | 可迭代对象 (Array, String, Map, Set等) |
| 原型链 | 会遍历原型链上的可枚举属性 | 不会遍历原型链 |
| 顺序 | 不保证顺序 | 保证迭代顺序 |
for...in
for (var 变量 in 对象) {
}for...in语句:对象中有几个属性,循环体就会执行几次。每次执行时,会将对象中的一个属性的名字赋值给变量。
for...of
for (var 变量 of 可迭代对象) {
}for...of语句:可迭代对象中有几个元素,循环体就会执行几次。每次执行时,会将可迭代对象中的一个元素赋值给变量。
附录4: 工厂模式创建对象
通过该方法可以大批量的创建对象。使用工厂方法创建的对象,使用的构造函数都是 Object,所以创建的对象都是 Object 这个类型,就导致我们无法区分出多种不同类型的对象。
function createPerson(name, age, gender) {
// 创建一个新的对象
var obj = new Object();
// 向对象中添加属性
obj.name = name;
obj.age = age;
obj.gender = gender;
obj.sayName = function () {
alert(this.name);
};
// 将新的对象返回
return obj;
}附录5: 垃圾回收(GC)
当一个对象没有任何的变量或属性对它进行引用,此时我们将永远无法操作该对象,此时这种对象就是一个垃圾,这种对象过多会占用大量的内存空间,导致程序运行变慢,所以这种垃圾必须进行清理。
在 JS 中拥有自动的垃圾回收机制,会自动将这些垃圾对象从内存中销毁,我们不需要也不能进行垃圾回收的操作。我们需要做的只是要将不再使用的对象设置 null 即可。
回收算法
- 引用计数算法(老IE):每个对象都有一个引用计数器,当有变量引用该对象时,计计数器就会增加,当没有变量引用该对象时,计数器就会减少。当计数器为 0 时,该对象就会被销毁。
- 标记清除算法:首先从根对象(GC Roots)出发,标记出所有正在使用(可达)的对象,然后遍历内存,清除掉所有未被标记的垃圾对象。