Skip to content

javascript基础

初识JS

  • 控制浏览器弹出一个警告框,alert("哥,你真帅啊!!");
  • 让计算机在页面中输出一个内容,document.write() 可以向 body 中输出一个内容 document.write("<h1>我出不出来~~~</h1>");
  • 向控制台输出一个内容,console.log() 的作用是向控制台输出一个内容 console.log("你猜我在哪出来呢?");
  • prompt()弹出一个提示框,框中会带有一个文本框,用户可以在文本框中输入一段内容。该函数需要一个字符串作为参数,该字符串将会作为提示框的提示文字。用户输入的内容将会作为函数的返回值返回,可以定义一个变量来接收该内容。

js 代码需要编写到 script 标签中,js 语句都是一条一条执行的。

代码引入方式

  1. 推荐使用的方式:将js代码编写到外部js文件中,然后通过script标签引入。写到外部文件中可以在不同的页面中同时引用,也可以利用到浏览器的缓存机制。

script 标签一旦用于引入外部文件了,就不能在编写代码了,即使编写了浏览器也会忽略
如果需要则可以在创建一个新的 script 标签用于编写内部代码

html
<script type="text/javascript" src="js/script.js"></script>
  1. 将js代码编写到script标签

  2. 可以将js代码编写到标签的onclick属性中,当我们点击按钮时,js代码才会执行;可以将js代码写在超链接的href属性中,这样当点击超链接时,会执行js代码

html
<button onclick="alert('讨厌,你点我干嘛~~');">点我一下</button>
<a href="javascript:alert('让你点你就点!!');">你也点我一下</a>

虽然可以写在标签的属性中,但是他们属于结构与行为耦合,不方便维护 —— 不推荐使用

注释

js
/*  多行注释  */

//单行注释

基本语法

  1. JS 中严格区分大小写
  2. JS 中每一条语句以分号(;)结尾
    • 如果不写分号,浏览器会自动添加,但是会消耗一些系统资源,而且有些时候,浏览器会加错分号,所以在开发中分号必须写
  3. JS 中会忽略多个空格和换行,所以我们可以利用空格和换行对代码进行格式化

变量与数据类型

字面量和变量

  • 字面量,都是一些不可改变的值,比如:1 2 3 4。字面量都是可以直接使用,但是我们一般都不会直接使用字面量。
  • 变量,变量可以用来保存字面量,而且变量的值是可以任意改变的。变量更加方便我们使用,所以在开发中都是通过变量去保存一个字面量,而很少直接使用字面量。可以通过变量对字面量进行描述。
js
//声明变量,在 js 中使用 var 关键字来声明一个变量
var a;

//为变量赋值
a = 123;

//声明和赋值同时进行
var b = 789;
var age = 80;

标识符

  • 在 JS 中所有的可以由我们自主命名的都可以称为是标识符
  • 例如:变量名、函数名、属性名都属于标识符
  • 命名一个标识符时需要遵守如下的规则:
    1. 标识符中可以含有字母、数字、_ 、$
    2. 标识符不能以数字开头
    3. 标识符不能是 ES 中的关键字或保留字
    4. 标识符一般都采用驼峰命名法
      • 首字母小写,每个单词的开头字母大写,其余字母小写
        helloWorld xxxYyyZzz

JS 底层保存标识符时实际上是采用的 Unicode 编码,所以理论上讲,所有的 utf-8 中含有的内容都可以作为标识符。但是一般不使用中文作为标识符的命名。

数据类型

数据类型的指的就是字面量的类型,在 JS 中一共有六种数据类型:

String 字符串 Number 数值 Boolean 布尔值 Null 空值 Undefined 未定义 Object 对象

其中 String Number Boolean Null Undefined 属于基本数据类型,而 Object 属于引用数据类型


  1. String 字符串
  • 在 JS 中字符串需要使用引号引起来
  • 使用双引号或单引号都可以,但是不要混着用
  • 引号不能嵌套,双引号里不能放双引号,单引号里不能放单引号

在字符串中我们可以使用 \ 作为转义字符,当表示一些特殊符号时可以使用 \ 进行转义:

\" 表示 "
\' 表示 '
\n 表示换行
\t 制表符
\\ 表示 \

输出字面量:alert("str");
输出变量 str:alert(str);


  1. Number 数值
  • 在 JS 中所有的数值都是 Number 类型,包括整数和浮点数(小数)

JS 中可以表示的数字的最大值 Number.MAX_VALUE1.7976931348623157e+308, Number.MIN_VALUE → 大于 0 的最小值 5e-324

如果使用 Number 表示的数字超过了最大值,则会返回一个 Infinity 表示正无穷;-Infinity 表示负无穷。注意 Infinity 是一个字面量。

在 JS 中整数的运算基本可以保证精确;如果使用 JS 进行浮点运算,可能得到一个不精确的结果,所以千万不要使用 JS 进行对精度要求比较高的运算


  1. Boolean 布尔值

布尔值只有两个,主要用来做逻辑判断:

true - 表示真
false - 表示假


  1. Null 空值

Null(空值) 类型的值只有一个,就是 null
null 这个值专门用来表示一个为空的对象


  1. 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

  1. 方式一:调用被转换数据类型的 toString() 方法

    • 该方法不会影响到原变量,它会将转换的结果返回
    • 但是注意:null 和 undefined 这两个值没有 toString() 方法,如果调用他们的方法,会报错
  2. 方式二:调用 String() 函数,并将被转换的数据作为参数传递给函数

    • 使用 String() 函数做强制类型转换时,对于 NumberBoolean 实际上就是调用的 toString() 方法,但是对于 nullundefined,就不会调用 toString() 方法,它会将 null 直接转换为 "null",将 undefined 直接转换为 "undefined"
js
a = a.toString(); // 调用方法
a = String(a); // 调用函数

调用 xxx 的 yyy() 方法,就是 xxx.yyy()

将其他的数据类型转换为 Number

  1. 方式一:使用 Number() 函数

    • 字符串 → 数字
      1. 如果是纯数字的字符串,则直接将其转换为数字
      2. 如果字符串中有非数字的内容,则转换为 NaN
      3. 如果字符串是一个空串或者是一个全是空格的字符串,则转换为 0
    • 布尔 → 数字 true 转成 1false 转成 0
    • null → 数字0
    • undefined → 数字NaN
  2. 方式二:使用parseInt()parseFloat()函数

    • 这种方式专门用来对付字符串
    • parseInt() 把一个字符串转换为一个整数
    • parseFloat() 把一个字符串转换为一个浮点数
    • parseInt() 可以将一个字符串中的有效的整数内容丢出去,然后转换为 Number
    • parseFloat() 作用和 parseInt() 类似,不同的是它可以获得有效的小数
    • 如果对非 String 使用 parseInt()parseFloat(),它会先将其转换为 String 然后在操作

在 js 中,如果需要表示 16 进制的数字,则需要以 0x 开头。
如果需要表示 8 进制的数字,则需要以 0 开头。
如果要表示 2 进制的数字,则需要以 0b 开头,但是不是所有的浏览器都支持。
"070" 这种字符串,有些浏览器会当成 8 进制解析,有些会当成 10 进制解析。
可以parseInt() 中传递一个第二个参数,来指定数字的进制。

将其他的数据类型转换为 Boolean

  • 使用 Boolean() 函数
    • 数字 → 布尔
      • 除了 0NaN,其余的都是 true
    • 字符串 → 布尔
      • 除了空串,其余的都是 true
    • nullundefined 都会转换为 false
    • 对象也会转换为 true

运算符

运算符也叫操作符
通过运算符可以对一个或多个值进行运算,并获取运算结果。
比如:typeof 就是运算符,可以获得一个值的类型,它会将该值的类型以字符串的形式返回:number string boolean undefined object

算数运算符

当对非 Number 类型的值进行运算时,会将这些值转换为 Number 然后在运算
任何值和 NaN 做运算都得 NaN

加减乘除取模 +-*/%

  1. 加法 +

    • 可以对两个值进行加法运算,并将结果返回
    • 如果对两个字符串进行加法运算,则会做拼接。会将两个字符串拼接为一个字符串,并返回。任何的值和字符串做加法运算,都会先转换为字符串,然后再和字符串做拼接的操作
  2. 减法 -

    • 可以对两个值进行减法运算,并将结果返回
  3. 乘法 *

    • 可以对两个值进行乘法运算
  4. 除法 /

    • 可以对两个值进行除法运算
  5. 取模 %

    • 取模运算(取余数)

⚠️ 注意

任何值和字符串相加都会转换为字符串,并做拼接操作

为任意的数据类型 + 一个 '' 即可将其转换为 String。这是一种隐式的类型转换,实际上它也是调用 String() 函数。

任何值做 - * / 运算时都会自动转换为 Number。可以通过为一个值 -0 *1 /1 来将其转换为 Number,原理和 Number() 函数一样。

一元运算符(+,-,++,--)

  1. 正负号 +,-
    • + 正号,正号不会对数字产生任何影响
    • - 负号,负号可以对数字进行负号的取反

对于非 Number 类型的值,它会先将转换为 Number,然后在运算。可以对一个其他的数据类型使用 +,来将其转换为 number,它的原理和 Number() 函数一样。

  1. 自增 自减 ++ --
    • 通过自增可以使变量在自身的基础上增加 1
    • 对于一个变量自增以后,原变量的值会立即自增 1
    • 自增分成两种:后++ (a++) 和前++ (++a)

自减 同理

无论是 a++ 还是 ++a,都会立即使原变量的值自增 1
不同的是 a++++a 的值不同:

  • a++ 的值等于原变量的值(自增前的值)
  • ++a 的值等于新值 (自增后的值)

逻辑运算符

! 非

  • 可以用来对一个值进行非运算,所谓非运算就是对一个布尔值进行取反操作,truefalsefalsetrue。如果对一个值进行两次取反,它不会变化。如果对非布尔值进行非运算,则会将其转换为布尔值,然后再取反,所以我们可以利用该特点,来将一个其他的数据类型转换为布尔值。
  • 可以为一个任意数据类型取两次反,来将其转换为布尔值,原理和 Boolean() 函数一样

&& 与

&& 可以对符号两侧的值进行与运算并返回结果

运算规则:

  • 两个值中只要有一个值为 false 就返回 false,只有两个值都为 true 时,才会返回 true
  • JS 中的 “与” 属于短路的与,如果第一个值为 false,则不会看第二个值

|| 或

|| 可以对符号两侧的值进行或运算并返回结果

运算规则:

  • 两个值中只要有一个 true,就返回 true,如果两个值都为 false,才返回 false
  • JS 中的 “或” 属于短路的或,如果第一个值为 true,则不会检查第二个值

⚠️ 注意

&& || 非布尔值的情况
对于非布尔值进行与或运算时,会先将其转换为布尔值,然后再运算,并且返回原值

  1. 与运算:

    • 如果第一个值为 true,则必然返回第二个值
    • 如果第一个值为 false,则直接返回第一个值
  2. 或运算:

    • 如果第一个值为 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 四位编码

js
console.log("\u2620");

在网页中使用 Unicode 编码,&#编码;这里的编码需要的是 10 进制

等于不等于

  1. 相等运算符:比较两个值是否相等,如果相等会返回true,否则返回false

    • 使用 == 来做相等运算,如果值的类型不同,则会自动进行类型转换,将其转换为相同的类型,然后在比较
    • 使用 === 来做全等运算,和相等类似,但不会自动进行类型转换
  2. 不相等运算符:判断两个值是否不相等,如果不相等返回true,否则返回false

    • 使用 != 来做不相等运算,会对变量进行自动的类型转换,如果转换后相等它也会返回false
    • 使用 !== 来做不全等运算,和不等类似,不同的是它不会做自动的类型转换

⚠️ 注意:特殊情况

js
console.log(null == 0); // false

undefined 衍生自 null,所以这两个值做相等判断时,会返回 true
NaN 不和任何值相等,包括他本身
可以通过 isNaN() 函数来判断一个值是否是 NaN,如果该值是 NaN 则返回 true,否则返回 false

条件运算符

条件运算符也叫三元运算符

语法:
条件表达式 ? 语句1 : 语句2;

执行的流程:
条件运算符在执行时,首先对条件表达式进行求值。

  • 如果该值为 true,则执行语句 1,并返回执行结果
  • 如果该值为 false,则执行语句 2,并返回执行结果

如果条件的表达式的求值结果是一个非布尔值,会将其转换为布尔值然后在运算

运算符优先级

在 JS 中有一个运算符优先级的表,在表中越靠上优先级越高,优先级越高越优先计算,如果优先级一样,则从左往右计算。

[]、new
0
++, --
!, ~, +(单目), -(单目), typeof, void, delete
%, *, /
+(双目), -(双目)
<<, >>, >>>
<, <=, >, >=
==, !=, ===, !==
&
^
|
&&
||
?:
=, +=, -=, *=, /=, %=, <<=, >>=, >>>=, &=, ^=, |=

不需要记忆,如果遇到优先级不清楚,可以使用 () 来改变优先级

流程控制语句

JS 中的程序是从上到下一行一行执行的,通过流程控制语句可以控制程序执行流程,使程序可以根据一定的条件来选择执行

  • 语句的分类:
    1. 条件判断语句
    2. 条件分支语句
    3. 循环语句

代码块

我们的程序是由一条一条语句构成的,语句是按照自上向下的顺序一条一条执行的

在 JS 中可以使用 {} 来为语句进行分组,同一个 {} 中的语句我们称为是一组语句,它们要么都执行,要么都不执行,一个 {} 中的语句我们也称为叫一个代码块,在代码块的后边就不用再编写了。

JS 中的代码块,只具有分组的作用,没有其他的用途,代码块内容的内容,在外部是完全可见的。

条件判断语句

使用条件判断语句可以在执行某个语句之前进行判断,如果条件成立才会执行语句,条件不成立则语句不执行。

if 语句

js
if(条件表达式){
    语句...
}

if 语句在执行时,会先对条件表达式进行求值判断:

  • 如果条件表达式的值为 true,则执行 if 后的语句;
  • 如果该值为 false,则不会执行 if 后的语句。

注意: if 语句只能控制紧随其后的那个语句。如果希望 if 语句可以控制多条语句,可以将这些语句统一放到代码块中。


js
if(条件表达式){
    语句...
}else{
    语句...
}

if...else...语句: 当该语句执行时,会先对 if 后的条件表达式进行求值判断:

  • 如果该值为 true,则执行 if 后的语句;
  • 如果该值为 false,则执行 else 后的语句。

js
if(条件表达式){
    语句...
}else if(条件表达式){
    语句...
}else if(条件表达式){
    语句...
}else{
    语句...
}

if...else if...else: 当该语句执行时,会从上到下依次对条件表达式进行求值判断:

  • 如果值为 true,则执行当前语句;
  • 如果值为 false,则继续向下判断。
  • 如果所有的条件都不满足,则执行最后一个 else 后的语句。
  • 该语句中,只会有一个代码块被执行,一旦代码块执行了,则直接结束语句。

条件分支语句

条件分支语句也叫 switch 语句

js
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 循环

js
while(条件表达式){
    语句...
}

while 语句在执行时,先对条件表达式进行求值判断:

  • 如果值为 true,则执行循环体;
  • 循环体执行完毕以后,继续对表达式进行判断;
  • 如果为 true,则继续执行循环体,以此类推;
  • 如果值为 false,则终止循环。

do...while 循环

js
do{
    语句...
}while(条件表达式)

执行流程:

do...while 语句在执行时,会先执行循环体,循环体执行完毕以后,在对 while 后的条件表达式进行判断:

  • 如果结果为 true,则继续执行循环体,执行完毕继续判断,以此类推;
  • 如果结果为 false,则终止循环。

对比: 实际上这两个语句功能类似,不同的是 while 是先判断后执行,而 do...while 会先执行后判断。do...while 可以保证循环体至少执行一次,而 while 不能。

for 循环

for 语句也是一个循环语句,也称为 for 循环。

for 循环中,为我们提供了专门的位置来放三个表达式:

js
for(①初始化表达式; ②条件表达式; ④更新表达式){
    ③循环体语句...
}
  1. 执行初始化表达式,初始化变量(初始化表达式只会执行一次
  2. 执行条件表达式,判断是否执行循环。
     - 如果为 true,则执行循环体语句
     - 如果为 false,终止循环
  3. 执行更新表达式,更新表达式执行完毕继续重复2

⚠️ 注意

for 循环中的三个部分都可以省略,也可以写在外部。
如果在for循环中不写任何的表达式,只写两个';',此时是死循环会一直执行下去,慎用。

循环控制关键字

  1. break 关键字

可以用来退出 switch 或循环语句。不能在 if 语句中使用 break 和 continue

break 关键字,会立即终止离他最近的那个循环语句。

可以为循环语句创建一个 label,来标识当前的循环。


  1. label:循环语句

使用 break 语句时,可以在 break 后跟着一个 label,这样 break 将会结束指定的循环,而不是最近的。


  1. continue 关键字

可以用来跳过当次循环,同样 continue 也是默认只会对离他最近的循环起作用。

对象

对象 Object

对象属于一种复合的数据类型,在对象中可以保存多个不同数据类型的属性。

对象的分类

  1. 内建对象

    • 由 ES 标准中定义的对象,在任何的 ES 的实现中都可以使用
    • 比如:Math String Number Boolean Function Object....
  2. 宿主对象

    • 由 JS 的运行环境提供的对象,目前来讲主要指由浏览器提供的对象
    • 比如 BOM DOM
  3. 自定义对象

    • 由开发人员自己创建的对象
  1. 创建对象
    • 使用 new 关键字调用的函数,是构造函数 constructor。构造函数是专门用来创建对象的函数。使用 typeof 检查一个对象时,会返回 object,在对象中保存的值称为属性。
    • 使用对象字面量创建对象时,直接指定对象中的属性。

对象字面量创建对象语法

js
{属性名:属性值, 属性名:属性值...}
  • 对象字面量的属性名可加引号也可不加;如果要使用一些特殊的名字,则必须加引号。
  • 属性名和值是一组一组的名值对结构,名和值之间使用 : 连接,多个名值对之间使用 , 隔开。
  • 如果一个属性之后没有其他属性了,, 就不要写了。
  1. 向对象添加属性
    • 语法: 对象.属性名 = 属性值;
    • 如果要使用特殊的属性名,不能采用 .的方式 来操作。需要使用另一种方式:语法: 对象["属性名"] = 属性值
  1. 使用 [] 这种形式去读取属性时也需要采用这种方式。
  2. []中可以直接传递一个变量。
  3. JS 对象的属性值,可以是任意的数据类型,甚至也可以是一个对象。
  1. 读取对象中的属性

    • 语法: 对象.属性名
    • 如果读取对象中没有的属性,不会报错而是会返回 undefined
  2. 修改对象的属性值

    • 语法: 对象.属性名 = 新值
  3. 删除对象的属性

    • 语法: delete 对象.属性名

in运算符

通过该运算符可以检查一个对象中是否含有指定的属性。如果有则返回 true,没有则返回 false

js
"属性名" in 对象;

对象的方法

对象的属性值可以是任何的数据类型,也可以是个函数。

函数也可以作为对象的属性,如果一个函数作为一个对象的属性保存,那么我们称这个函数是这个对象的方法。调用这个函数就说调用对象的方法(method)。

但是调用方法和调用函数只是名称上的区别没有其他的区别。

函数 function

函数也是一个对象,函数中可以封装一些代码,在需要时可以执行这些代码。
函数中可以保存一些代码在需要的时候调用,使用 typeof 检查一个函数对象时,会返回 function

  1. 函数的创建

我们在实际开发中很少使用构造函数来创建一个函数对象,可以将要封装的代码以字符串的形式传递给构造函数:

js
// 封装到函数中的代码不会立即执行,函数中的代码会在函数调用的时候执行。
var fun = new Function("console.log('Hello 这是我的第一个函数');");

使用 函数声明 来创建一个函数

js
function 函数名([形参1, 形参2...形参N]){
    语句...
}

使用 函数表达式 来创建一个函数

js
var 函数名 = function([形参1, 形参2...形参N]){
    语句...
}
  1. 函数的调用

调用函数 语法:函数对象()
当调用函数时,函数中封装的代码会按照顺序执行。

  1. 形参与实参

可以在函数的 () 中来指定一个或多个形参(形式参数)。多个形参之间使用 , 隔开,声明形参就相当于在函数内部声明了对应的变量,但是并不赋值。

在调用函数时,可以在 () 中指定实参(实际参数),实参将会赋值给函数中对应的形参。

函数参数说明

调用函数时解析器不会检查实参的类型,所以要注意,是否有可能会接收到非法的参数,如果有可能则需要对参数进行类型的检查。函数的实参可以是任意的数据类型。

调用函数时,解析器也不会检查实参的数量,多余实参不会被赋值。如果实参的数量少于形参的数量,则没有对应实参的形参将是 undefined

实参可以是任意的数据类型,也可以是一个对象。当我们的参数过多时,可以将参数封装到一个对象中,然后通过对象传递。实参可以是一个对象,也可以是一个函数。

  1. 返回值

可以使用 return 来设置函数的返回值

js
return 值;

返回值可以是任意的数据类型 return 后的值将会作为函数的执行结果返回,可以定义一个变量,来接收该结果。

  • 如果 return 语句后不跟任何值就相当于返回一个 undefined,如果函数中不写 return,则也会返回 undefined
  • return 后可以跟任意类型的值。

⚠️ 注意

在函数中 return 后的语句都不会执行。

js
mianji(); // 调用函数 - 相当于使用的函数的返回值;
mianji; // 函数对象 - 相当于直接使用函数对象;

立即执行函数IIFE

函数定义完,立即被调用,这种函数叫做立即执行函数。立即执行函数往往只会执行一次。

js
(function () {
  alert("我是一个匿名函数~~~");
})();

提示

在调用函数时,浏览器每次都会传递进两个隐含的参数:

    1. 函数的上下文对象 this
    1. 封装实参的对象 arguments

arguments

  • 是一个类数组对象,它也可以通过索引来操作数据,也可以获取长度
  • 在调用函数时,我们所传递的实参都会在 arguments 中保存
  • arguments.length 可以用来获取实参的长度
  • 我们即使不定义形参,也可以通过 arguments 来使用实参,只不过比较麻烦
    • arguments[0] 表示第一个实参
    • arguments[1] 表示第二个实参 ……
  • 它里边还有一个属性叫做 callee
    • 这个属性对应一个函数对象,就是当前正在指向的函数的对象

构造函数

创建一个构造函数,专门用来创建 Person 对象的。构造函数就是一个普通的函数,创建方式和普通函数没有区别,不同的是构造函数习惯上首字母大写。

构造函数和普通函数的区别就是调用方式的不同。普通函数是直接调用,而构造函数需要使用 new 关键字来调用。

构造函数的执行流程

    1. 立刻创建一个新的对象
    1. 将新建的对象设置为函数中 this,在构造函数中可以使用 this 来引用新建的对象
    1. 逐行执行函数中的代码
    1. 将新建的对象作为返回值返回

使用同一个构造函数创建的对象,我们称为一类对象,也将一个构造函数称为一个类。
我们将通过一个构造函数创建的对象,称为是该类的实例。

call、apply、bind

  • 这三个方法都是函数对象的方法,需要通过函数对象来调用
  • 当对函数调用 call()apply() 都会调用函数执行
  • 在调用 call()apply() 时可以将一个对象指定为第一个参数,此时这个对象将会成为函数执行时的 this
    • call() 方法可以将实参在对象之后依次传递
    • apply() 方法需要将实参封装到一个数组中统一传递
  • bind() 方法可以将一个对象指定为第一个参数,此时这个对象将会成为函数执行时的 this,但是不会立即调用函数。
    • bind() 方法可以将实参在对象之后依次传递

数组 Array

  • 数组也是一个对象,它和我们普通对象功能类似,也是用来存储一些值的。不同的是普通对象是使用字符串作为属性名的,而数组时使用数字来作为索引操作元素。
  • 索引:从 0 开始的整数就是索引。
  • 数组的存储性能比普通对象要好,在开发中我们经常使用数组来存储一些数据。

创建数组对象

js
var arr = new Array();

使用字面量来创建数组
语法:[] // var arr = [];

说明

  1. 使用字面量创建数组时,可以在创建时就指定数组中的元素。
  2. 使用构造函数创建数组时,也可以同时添加元素,将要添加的元素作为构造函数的参数传递,元素之间使用 “,” 隔开
  3. 数组中的元素可以是任意的数据类型。

向数组中添加元素
语法:数组[索引] = 值

读取数组中的元素
语法:数组[索引]

  • 如果读取不存在的索引,他不会报错而是返回 undefined

获取数组的长度
语法:数组.length

  • 可以使用 length 属性来获取数组的长度(元素的个数)
  • 对于连续的数组,使用 length 可以获取到数组的长度(元素的个数)
  • 对于非连续的数组,使用 length 会获取到数组的最大的索引+1,尽量不要创建非连续的数组。

修改 length

  • 如果修改的 length 大于原长度,则多出部分会空出来
  • 如果修改的 length 小于原长度,则多出的元素会被删除

向数组的最后一个位置添加元素
语法:数组[数组.length] = 值;

数组中也可以放数组,这种数组我们称为 二维数组

数组的方法

  1. push()
  • 该方法可以向数组的末尾添加一个或多个元素,并返回数组的新长度。可以将要添加的元素作为方法的参数传递,这样这些元素将会自动添加到数组的末尾。该方法会将数组新的长度作为返回值返回。
  1. pop()
  • 该方法可以删除数组的最后一个元素,并将被删除的元素作为返回值返回。
  1. unshift()
  • 向数组开头添加一个或多个元素,并返回新的数组长度。向前边插入元素以后,其他的元素索引会依次调整。
  1. shift()
  • 可以删除数组的第一个元素,并将被删除的元素作为返回值返回。
  1. slice()
  • 可以用来从数组提取指定元素
  • 该方法不会改变原数组,而是将截取到的元素封装到一个新数组中返回
  • 参数:
    1. 截取开始的位置的索引,包含开始索引
    2. 截取结束的位置的索引,不包含结束索引
      • 第二个参数可以省略不写,此时会截取从开始索引往后的所有元素
  • 索引可以传递一个负值,如果传递一个负值,则从后往前计算
    • -1 倒数第一个
    • -2 倒数第二个
  1. splice()
  • 可以用于删除数组中的指定元素
  • 使用 splice() 会影响到原数组,会将指定元素从原数组中删除,并将被删除的元素作为返回值返回
  • 参数:
    • 第一个,表示开始位置的索引
    • 第二个,表示删除的数量
    • 第三个及以后……
      • 可以传递一些新的元素,这些元素将会自动插入到开始位置索引前边
  1. concat()
  • 可以连接两个或多个数组,并将新的数组返回,不会对原数组产生影响
  1. join()
  • 该方法可以将数组转换为一个字符串,不会对原数组产生影响,而是将转换后的字符串作为结果返回
  • join() 中可以指定一个字符串作为参数,这个字符串将会成为数组中元素的连接符
    • 如果不指定连接符,则默认使用 “,” 作为连接符
  1. reverse()
  • 该方法用来反转数组(前边的去后边,后边的去前边),会直接修改原数组
  1. sort()
  • 可以用来对数组中的元素进行排序
  • 也会影响原数组,默认会按照 Unicode 编码进行排序
    • 即使对于纯数字的数组,使用 sort() 排序时,也会按照 Unicode 编码来排序,所以对数字进排序时,可能会得到错误的结果。
  • 我们可以自己来指定排序的规则
    • 我们可以在 sort() 添加一个回调函数,来指定排序规则,回调函数中需要定义两个形参,浏览器将会分别使用数组中的元素作为实参去调用回调函数。使用哪个元素调用不确定,但是肯定的是在数组中 a 一定在 b 前边
    • 浏览器会根据回调函数的返回值来决定元素的顺序:
      • 如果返回一个大于 0 的值,则元素会交换位置;
      • 如果返回一个小于 0 的值,则元素位置不变;
      • 如果返回一个 0,则认为两个元素相等,也不交换位置。

结论:
如果需要升序排列,则返回 a - b
如果需要降序排列,则返回 b - a

js
arr.sort(function (a, b) {
  return b - a;
});

遍历数组

js
for (var i = 0; i < arr.length; i++) {
  console.log(arr[i]);
}

一般我们都是使用 for 循环来遍历数组,JS 中还为我们提供了一个forEach()方法,用来遍历数组。

  • 这个方法只支持 IE8 以上的浏览器,IE8 及以下的浏览器均不支持该方法,所以如果需要兼容 IE8,则不要使用 forEach。还是使用 for 循环来遍历。

  • forEach() 方法需要一个函数作为参数

    • 像这种函数,由我们创建但不是由我们调用的,我们称为回调函数
    • 数组中有几个元素,函数就会执行几次,每次执行时,浏览器会将遍历到的元素以实参的形式传递进来,我们可以来定义形参,来读取这些内容
  • 在回调函数中传递三个参数:

    • 第一个参数,就是当前正在遍历的元素
    • 第二个参数,就是当前正在遍历的元素的索引
    • 第三个参数,就是正在遍历的数组
js
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 表示周一 ……
  • getMonth() — 获取当前时间对象的月份

    • 会返回一个 0~11 的值
      • 0 表示 1 月
      • 1 表示 2 月 ……
      • 11 表示 12 月
  • getFullYear() — 获取当前日期对象的年份

  • getTime() — 获取当前日期对象的时间戳

    • 时间戳,指的是从格林威治标准时间的 1970 年 1 月 1 日,0 时 0 分 0 秒 到当前日期所经过的毫秒数。
  • 计算机底层在保存时间时使用的都是时间戳

可以利用时间戳来测试代码的执行的性能

js
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 对象

⚠️ 注意

我们在实际应用中不会使用基本数据类型的对象,如果使用基本数据类型的对象,在做一些比较时可能会带来一些不可预期的结果。

方法和属性只能添加给对象,不能添加给基本数据类型。当我们对一些基本数据类型的值去调用属性和方法时,浏览器会临时使用包装类将其转换为对象,然后在调用对象的属性和方法。调用完以后,再将其转为基本数据类型。

字符串方法

在计算机底层,字符串是以字符数组的形式保存的。

  1. length 属性 — 可以用来获取字符串的长度
  2. charAt(i) — 可以返回字符串中指定位置的字符,根据索引获取指定的字符。
  3. charCodeAt() — 获取指定位置字符的字符编码(Unicode 编码)
  4. fromCharCode() — 可以根据字符编码去获取字符
  5. concat() — 可以用来连接两个或多个字符串,作用和 + 一样。
  6. indexOf() — 该方法可以检索一个字符串中是否含有指定内容,如果字符串中含有该内容,则会返回其第一次出现的索引;如果没有找到指定的内容,则返回 -1。可以指定一个第二个参数,指定开始查找的位置。
  7. lastIndexOf() — 该方法的用法和 indexOf() 一样,不同的是 indexOf 是从前往后找,而 lastIndexOf 是从后往前找。也可以指定开始查找的位置。
  8. slice() — 可以从字符串中截取指定的内容,不会影响原字符串,而是将截取到的内容返回
    • 参数:
      • 第一个,开始位置的索引(包括开始位置)
      • 第二个,结束位置的索引(不包括结束位置),如果省略第二个参数,则会截取到后边所有的。也可以传递一个负数作为参数,负数的话将会从后边计算
  9. substring() — 可以用来截取一个字符串,与 slice() 类似
    • 参数:
      • 第一个:开始截取位置的索引(包括开始位置)
      • 第二个:结束位置的索引(不包括结束位置)
    • 不同的是这个方法不能接受负值作为参数,如果传递了一个负值,则默认使用 0。而且他还自动调整参数的位置,如果第二个参数小于第一个,则自动交换。
  10. substr() — 用来截取字符串
    • 参数:
      1. 截取开始位置的索引
      2. 截取的长度
  11. split() — 将一个字符串拆分为一个数组
    • 参数: 需要一个字符串作为参数,将会根据该字符串去拆分数组。如果传递一个空串作为参数,则会将每个字符都拆分为数组中的一个元素
  12. toUpperCase() — 将一个字符串转换为大写并返回
  13. toLowerCase() — 将一个字符串转换为小写并返回
  14. split() - 可以将一个字符串拆分为一个数组
    • 方法中可以传递一个正则表达式作为参数,这样方法将会根据正则表达式去拆分字符串。这个方法即使不指定全局匹配,也会全都拆分。
  15. search()
    • 可以搜索字符串中是否含有指定内容
    • 如果搜索到指定内容,则会返回第一次出现的索引,如果没有搜索到返回 -1
    • 它可以接受一个正则表达式作为参数,然后会根据正则表达式去检索字符串
    • search() 只会查找第一个,即使设置全局匹配也没用
  16. match() - 可以根据正则表达式,从一个字符串中将符合条件的内容提取出来
    • 默认情况下我们的 match 只会找到第一个符合要求的内容,找到以后就停止检索
    • 我们可以设置正则表达式为全局匹配模式,这样就会匹配到所有的内容
    • 可以为一个正则表达式设置多个匹配模式,且顺序无所谓
    • match() 会将匹配到的内容封装到一个数组中返回,即使只查询到一个结果
  17. replace()
    • 可以将字符串中指定内容替换为新的内容
    • 参数:
      1. 被替换的内容,可以接受一个正则表达式作为参数
      2. 新的内容
    • 默认只会替换第一个

原型 与 作用域

原型 prototype

我们所创建的每一个函数,解析器都会向函数中添加一个属性 prototype,这个属性对应着一个对象,这个对象就是我们所谓的原型对象

如果函数作为普通函数调用,prototype 没有任何作用。当函数以构造函数的形式调用时,它所创建的对象中都会有一个隐含的属性,指向该构造函数的原型对象,我们可以通过 __proto__ 来访问该属性。

原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,我们可以将对象中共有的内容,统一设置到原型对象中。当我们访问对象的一个属性或方法时,它会先在对象自身中寻找,如果有则直接使用;如果没有则会去原型对象中寻找,如果找到则直接使用。

以后我们创建构造函数时,可以将这些对象共有的属性和方法,统一添加到构造函数的原型对象中,这样不用分别为每一个对象添加,也不会影响到全局作用域,就可以使每个对象都具有这些属性和方法了。

instanceof

使用 instanceof 可以检查一个对象是否是一个类的实例

js
对象 instanceof 构造函数;
  • 如果是,则返回 true;否则返回 false
  • 所有的对象都是 Object 的后代,所以任何对象和 Objectinstanceof 检查时都会返回 true

in 检查对象属性

  • 如果对象中没有但是原型中有,也会返回 true
  • 可以使用对象的 hasOwnProperty() 来检查对象自身中是否含有该属性,使用该方法只有当对象自身中含有属性时,才会返回 true

原型对象也是对象,所以它也有原型。当我们使用一个对象的属性或方法时,会先在自身中寻找,自身中如果有,则直接使用;如果没有则去原型对象中寻找,如果原型对象中有,则使用;如果没有则去原型的原型中寻找,直到找到 Object 对象的原型。

Object 对象的原型没有原型,如果在 Object 原型中依然没有找到,则返回 undefined

作用域

作用域指一个变量的作用的范围。
在 JS 中一共有两种作用域:全局作用域和函数作用域。

  1. 全局作用域
  • 直接编写在 <script> 标签中的 JS 代码,都在全局作用域
  • 全局作用域在页面打开时创建,在页面关闭时销毁
  • 在全局作用域中有一个全局对象 window,它代表的是一个浏览器的窗口,它由浏览器创建我们可以直接使用

特性

  • 创建的变量都会作为 window 对象的属性保存。
  • 创建的函数都会作为 window 对象的方法保存。
  • 全局作用域中的变量都是全局变量,在页面的任意的部分都可以访问的到。
  1. 函数作用域
  • 调用函数时创建函数作用域,函数执行完毕以后,函数作用域销毁
  • 每调用一次函数就会创建一个新的函数作用域,他们之间是互相独立的

作用域链

  • 在函数作用域中可以访问到全局作用域的变量,在全局作用域中无法访问到函数作用域的变量。
  • 当在函数作用域操作一个变量时,它会先在自身作用域中寻找,如果有就直接使用;如果没有则向上一级作用域中寻找,直到找到全局作用域。如果全局作用域中依然没有找到,则会报错 ReferenceError
  • 在函数中要访问全局变量可以使用 window 对象。

声明提前

变量的声明提前

  • 使用 var 关键字声明的变量,会在所有的代码执行之前被声明(但就不会赋值)。
  • 但是如果声明变量时不使用 var 关键字,则变量不会被声明提前。

函数的声明提前

  • 使用函数声明形式创建的函数 function 函数(){},它会在所有的代码执行之前就被创建,所以我们可以在函数声明前来调用函数。
  • 使用函数表达式创建的函数,不会被声明提前,所以不能在声明前调用。

在函数作用域也有声明提前的特性,使用 var 关键字声明的变量,会在函数中所有的代码执行之前被声明。函数声明也会在函数中所有的代码执行之前执行。在函数中,不使用 var 声明的变量都会成为全局变量。定义形参就相当于在函数作用域中声明了变量。

this

解析器在调用函数每次都会向函数内部传递进一个隐含的参数,这个隐含的参数就是 this
this 指向的是一个对象,这个对象我们称为函数执行的“上下文对象”。

根据函数的调用方式的不同,this 会指向不同的对象:

  1. 以函数的形式调用时,this 永远都是 window
  2. 以方法的形式调用时,this 就是调用方法的那个对象
  3. 以构造函数的形式调用时,this 就是新创建的那个对象
  4. 使用 call 和 apply 调用时,this 是指定的那个对象

附录

附录1: console.time

console.time("计时器的名字")

可以用来开启一个计时器
它需要一个字符串作为参数,这个字符串将会作为计时器的标识

console.timeEnd("计时器的名字")

用来停止一个计时器,需要一个计时器的名字作为参数

附录2: 变量与内存(堆栈)

JS 中的变量都是保存到栈内存中的。

基本数据类型的值直接在栈内存中存储,值与值之间是独立存在,修改一个变量不会影响其他的变量。

对象是保存到堆内存中的,每创建一个新的对象,就会在堆内存中开辟出一个新的空间,而变量保存的是对象的内存地址(对象的引用)。如果两个变量保存的是同一个对象引用,当一个通过一个变量修改属性时,另一个也会受到影响。

⚠️ 注意

当比较两个基本数据类型的值时,就是比较值。而比较两个引用数据类型时,它是比较的对象的内存地址,如果两个对象是一模一样的,但是地址不同,它也会返回 false

附录3: for/in 和 for/of

特性for...infor...of
遍历内容对象的键名 (key)可迭代对象的值 (value)
适用对象普通对象 (Object)可迭代对象 (Array, String, Map, Set等)
原型链会遍历原型链上的可枚举属性不会遍历原型链
顺序不保证顺序保证迭代顺序

for...in

js
for (var 变量 in 对象) {
}
  • for...in 语句:对象中有几个属性,循环体就会执行几次。每次执行时,会将对象中的一个属性的名字赋值给变量。

for...of

js
for (var 变量 of 可迭代对象) {
}
  • for...of 语句:可迭代对象中有几个元素,循环体就会执行几次。每次执行时,会将可迭代对象中的一个元素赋值给变量。

附录4: 工厂模式创建对象

通过该方法可以大批量的创建对象。使用工厂方法创建的对象,使用的构造函数都是 Object,所以创建的对象都是 Object 这个类型,就导致我们无法区分出多种不同类型的对象。

js
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)出发,标记出所有正在使用(可达)的对象,然后遍历内存,清除掉所有未被标记的垃圾对象。

Released under the MIT License.