一、初识JS
1、写js的三个地方
-
行内写法:写在开始标签中
-
内部写法:把js代码写在script标签中,一般把script标签放到html最后面,一个html文档可以写多个script标签
-
外部写法:把js代码单独写在
.js
文件中注意:
引入外部script标签必须是双标签
在这个script标签中不能在放其它js,就是放了,里面的JS代码也不会执行
JS文件中不能写HTML,
<script>
标签也是HTML标签不能直接把JS文件放到浏览器中运行,需要把JS文件放到HTML文件中,浏览器可以直接解析HTML文件,HTML文件中 有JS文件,浏览器才能识别出JS文件,并解析与执行。
2、js中的注释
-
// 单行注释
-
/* * 多行注释 */
3、内存模型
JS内存空间分为栈(stack)、堆(heap)、池(一般也会归类为栈中)。 其中栈存放变量,堆存放复杂对象,池存放常量。
栈空间先进后出,后进先出
<script>
var a = 20;
var b = a;
b = 30;
alert(a);//20
</script>
在栈内存中的数据发生复制行为时,系统会自动为新的变量分配一个新值。var b = a执行之后,a与b虽然值都等 于20,但是他们其实已经是相互独立互不影响的值了。
<script>
var m = {
a: 10,
b: 20
};
var n = m;
n.a = 150;
alert(m.a);//150
</script>
我们通过var n = m执行一次复制引用类型的操作。引用类型的复制同样也会为新的变量自动分配一个新的值保存在栈内存中,但不同的是,这个新的值,仅仅只是引用类型的一个地址指针。当地址指针相同时,尽管他们相互独立,但是在堆内存中访问到的具体对象实际上是同一个。
内存的生命周期
- 内存分配:当我们申明变量、函数、对象的时候,系统会自动为他 们分配内存
- 内存使用:即读写内存,也就是使用变量、函数等
- 内存回收:使用完毕,由垃圾回收机制自动回收不再使用的内存
4、变量
变量 = 变量名+变量值
由于内存地址操作比较麻烦,一般情况下,会给这块内存地址起一个别名,这个别名叫变量名。 换句话说,变量名就是一块内存空间的别名。
JS中如何定义变量?
var,let,const
//使用const来声明变量(变量的值不能再修改了,也就是所谓的常量)
可以使用var一次定义一个变量,也可以使用var一次性定义三个变量
声明变量,如果仅仅是声明了,没有赋值,它的值是 undefined
重复声明,后面的值会把前面的值覆盖掉
5、数据类型
为什么需要数据类型?
基本上所有编程语言中都提供了数据类型的概念。因为计算机资源(内存)有限的,为了更加合理使用内存空间,需要把数据进行分类,不同的数据,分配不同大小的内存空间。
js中的数据类型
-
基本数据类型
- number string boolean null undefined
-
引用数据类型
- object arr funtion math
如何查看一个数据的数据类型?
typeof
注意:仅仅是一个运算符,不是函数
在JS中,对浮点数进行计算,不靠谱
Not a Number (NaN) 不是一个数字
string注意细节
-
字符串需要使用英文中的单引号或双引号引起来 “” ‘’
引号要配对,不能一边是单引号,另一边是双引号 -
如果在一个字符串中,需要嵌套另一个字符串,只能在单引号中嵌套双引号,或者在双引号中去嵌套单引号
-
ES6中提供了模板字符串,方便字符串的拼接
`2345${a}6789`
-
为了显示某些有特殊含义的字符,例如“”、/、等,可以在字符串中使用转义字符 \
-
字符串中可以使用+进行拼接,它不表示数学中的相加运算,也就是说+两侧,只要有一侧是字符串,就表示字符串的拼接
-
string数据类型对应的数据也是无数个
boolean
boolean数据类型对应的值只有两个,一个是true,一个是false,还是区分大小写的
undefined null 0 -0 NaN ""默认会(隐式)转化成false,其它的所有的数据都转化为true
undefined
1,undefined这种数据类型对应的值只有一个,还是undefined
2,在一个变量声明了,但没有赋值,这个变量的值是undefined,值的类型也是undefiend
3,还有一些其它地方也会出现undefined,如一个函数没有返回值,默认返回undefiend
4,在JS中表示空的除了undefined,还有null,使用typeof打印出null的数据类型是object
数组
1,数组是一个引用数据类型,存储在堆区,在栈区保存着数组这个数据的地址
2,函数也是引用数据类型,存储在堆区,在栈区保存着函数这个数据的地址,然后给栈区地址起一个别名,是函数名
3,JS中的单体内置对象就是不需要new的对象,如Math, Math.random()得到一个随机数
4,在JS中还有很多很多其它的对象,如Date…
二、运算符
运算符是一种符号,不同的符号代表不同的功能
1、根据功能分类
算术运算符
- 算术运算符+ - * / % ++ –
关系运算符
-
关系运算符: == = = = != !== > < >= <=
- 关系运算符最终得到的结果,要么是true,要么是false
- 关系运算符两侧的操作数类型要一致,才能运算,如果不一致,操作数的类型会进行隐式转化(隐式转化我会专门写一个文档)
- **===**进行比较时,会先看一下数据类型,数据不一样,直接返回false,如果类型一样,再比较值是否一样
- **==**进行比较时,如果类型不一样,先进行隐式类型转化,把一侧的数据类型转化成另一侧的数据类型,目的就是为了保证运算符两侧的数据类型一致,隐式转化完后,再比较值,如果值一样,也是返回true
- 对于引用数据类型比较时,比较是地址,不同的内存空间,地址是唯一的
- 对于引用数据类型比较时,== = == 比较的都是地址 (类型一样)
- == 或 ===在比较引用数据类型时作用一样
逻辑运算符
-
逻辑运算符:&& 逻辑与 || 逻辑或 ! 逻辑非(取反)
- 逻辑运算符两侧的操作数主要是布尔类型,如果不是,会隐式转化成布尔类型
- 对于&&来说,只有两侧的操作数都为真时,整体结果才是true
- 对于||来说,只要有一侧的操作数为真,整体结果就为真
- 对于!来说,就是取反操作,之前为真,取反为假,之前假,取反后为真
逻辑运算符需要注意的点
1,对于逻辑运算符来说,最终的结果可能并不是布尔类型
2,对于&&来说,如果说操作数都是布尔类型,结果肯定是布尔类型,当两个操作数都是true,结果才为true
3,对于&&来说,如果操作不是布尔类型,&&在运算时,需要保证运算符两侧的数据类型要一样,如果不一样,会发生隐式地类型转化
6,对于&&来说,如果操作数不是布尔类型,并且两侧的数据类型一样,会发生隐式类型转化,转化完后再进行&&运算,运算的结果并不只限于true或false
7,基于第6点,先计算左操作数的值,如果左操作数的值是一个假值,则整个表达式结果是一个假值,同时停止对右操作数的计算,整体的值就是左操作数值。 如果左值是一个真值,整体这个值还不能确定,要看右操作数的值,整体这个结果就是右操作数的值,这个值也是转化之前的值。
-
赋值运算符:= += -= *= /= %= (赋值运算符有副作用,整体也是有一个值,是左值)
-
位运算符
-
其它运算符
-
逗号运算符
-
下标运算符
-
三元运算符
-
typeof运算符
-
delete运算符
-
in运算符
-
字符串拼接运算符
2、根据操作数的个数分类
运算符两边的数据或符号叫操作数。如果一个运算符的操作数有两个,那么这个运算符叫二元运算符,也叫二目运算符,也叫双目运算符。
* 如果说一个运算符的操作数就一个,叫这个运算符是一元运算符,或单目运算符。
* 如果说一个运算符的操作数有三个,叫这个运算符是三元运算符,也叫三目运算符。
* 一元:! let f = false; let k = **!**f;
* 二元:+ - * / % = < > >= <= == = = = != !== …
* 三元: ? : 操作数1 ? 操作数2 : 操作数3