JavaScript基础
JavaScript
1. 简介
JavaScript(简称“JS”) 是一种具有函数优先的轻量级,解释型或即时编译型的高级编程语言。虽然它是作为开发Web页面的脚本语言而出名的,但是它也被用到了很多非浏览器环境中,JavaScript 基于原型编程、多范式的动态脚本语言,并且支持面向对象、命令式和声明式(如函数式编程)风格。
JavaScript在1995年由Netscape公司的[Brendan Eich](https://baike.baidu.com/item/Brendan Eich),在网景导航者浏览器上首次设计实现而成。因为Netscape与Sun合作,Netscape管理层希望它外观看起来像Java,因此取名为JavaScript。但实际上它的语法风格与Self及Scheme较为接近。
布兰登·艾奇(Brendan Eich,1961年~),JavaScript的发明人,2005年至2014年期间,在Mozilla公司担任首席技术长(Chief Technology Officer)。出任Mozilla的CEO十天就被迫辞职。
在本地运行的浏览器脚本语言。Node.js 服务端开发,应用很广。
浏览器怎么执行JS?
浏览器中有渲染引擎和Js引擎,JavaScript需要js引擎来编译执行,chrome中的V8就是JS引擎。
JS的组成
JavaScript是世界上最流行的脚本语言。
==一个合格的后端人员必须精通JavaScript==
最新版本已经到es6版本,但是大部分浏览器只停留在支持es5代码上!
开发环境 -- 线上环境,版本不一致。
JavaScript负责页面中的的行为,它是一门运行在浏览器端的脚本语言。
2. 快速入门
2.1 JS的编写的位置
1.可以编写到标签的指定属性中
<button onclick="alert('hello');">我是按钮</button>
<a href="javascript:alert('aaa');">超链接</a>
2.可以编写到script标签中
<script type="text/javascript">
//编写js代码
</script>
3.可以将代码编写到外部的js文件中,然后通过标签将其引入
script标签一旦用于引入外部文件了,就不能在编写代码了,即使编写了浏览器也会忽略,如果需要则可以在创建一个新的script标签用于编写内部代码
<script type="text/javascript" src="文件路径"></script>
2.2 输出语句
alert("要输出的内容");
该语句会在浏览器窗口中弹出一个警告框
document.write("要输出的内容");
该内容将会被写到body标签中,并在页面中显示
console.log("要输出的内容");
该内容会被写到开发者工具的控制台中
2.3 基本的语法
js函数声明不需要;分号,但是赋值语句要加;分号
function functionName(arg0,arg1,arg2){
//函数声明
}
var functionName=function(arg0,arg1,arg2){
//函数表达式
};(注意分号)
注释
单行注释
//注释内容
多行注释
/*
注释内容
*/
JS严格区分大小写
JS中每条语句以分号(;)结尾如果不写分号,浏览器会自动添加,但是会消耗一些系统资源,而且有些时候,浏览器会加错分号,所以在开发中分号必须写。JS中会自动忽略多个空格和换行,所以我们可以利用空格和换行对代码进行格式化。
2.4 字面量和变量
字面量
字面量实际上就是一些固定的值,比如 1 2 3 4 true false null NaN "hello"
**字面量都是不可以改变的。**由于字面量不是很方便使用,所以在JS中很少直接使用字面量。
变量
变量可以用来保存字面量,并且可以保存任意的字面量,一般都是通过变量来使用字面量,而不直接使用字面量,而且也可以通过变量来对字面量进行一个描述。
声明变量
// 使用var关键字来声明一个变量
var a;
// 为变量赋值
a = 1
//声明和赋值同时进行
var a = 456;
2.5 标识符
在JS中所有的可以自主命名的内容,都可以认为是一个标识符,是标识符就应该遵守标识符的规范。比如:变量名、函数名、属性名。
规范:
1.标识符中可以含有字母、数字、_、$
2.标识符不能以数字开头
3.标识符不能是JS中的关键字和保留字
4.标识符一般采用驼峰命名法
3. 数据类型
3.1 六种数据类型
JS中一共分成六种数据类型 5个基本数据类型+object
String 字符串
Number 数值
Boolean 布尔值
Null 空值
Undefined 未定义
Object 对象其中基本数据类型有5个
1.String 字符串
JS中的字符串需要使用引号引起来双引号或单引号都行,在字符串中使用\作为转义字符;
\'==> '
\"==> "
\n==> 换行
\t==> 制表符
\\==> \
使用typeof运算符检查字符串时,会返回"string"
2.Number 数值
JS中所有的整数和浮点数都是Number类型
最大能表示的值:Number.MAX_VALUE= 1.7976931348623157e+308
特殊的数字:能赋值给变量
//Infinity 正无穷 a = Infinity ,能赋值
//-Infinity 负无穷
//NaN 非法数字(Not A Number)其他进制的数字的表示:
//0b 开头表示二进制,但是不是所有的浏览器都支持
//0 开头表示八进制
//0x 开头表示十六进制
使用typeof检查一个Number类型的数据时,会返回"number"(包括NaN 和 Infinity)
3.Boolean 布尔值
布尔值主要用来进行逻辑判断,布尔值只有两个
true 逻辑的真
false 逻辑的假
使用typeof检查一个布尔值时,会返回"boolean"
4.Null 空值
空值专门用来表示为空的对象,Null类型的值只有一个
null
使用typeof检查一个Null类型的值时会返回"object"
5.Undefined 未定义
如果声明一个变量但是没有为变量赋值此时变量的值就是undefined
该类型的值只有一个 undefined
使用typeof检查一个Undefined类型的值时,会返回"undefined"
6. 引用数据类型
Object 对象
3.2类型转换
类型转换就是指将其他的数据类型,转换为String Number 或 Boolean
3.2.1 转换为String
方式一(强制类型转换):
调用被转换数据的toString()方法
var a = 123;
a = a.toString();
注意:这个方法不适用于null和undefined
由于这两个类型的数据中没有方法,所以调用toString()时会报错
方式二(强制类型转换):
调用String()函数
例子:
var a = 123;
a = String(a);
原理:对于Number Boolean String都会调用他们的toString()方法来将其转换为字符串,对于null值,直接转换为字符串"null"。对于undefined直接转换为字符串"undefined"
方式三(隐式的类型转换):
为任意的数据类型 +""
例子:
var a = true;
a = a + "";
原理:和String()函数一样
3.2.2 转换为Number
方式一(强制类型转换):
调用Number()函数
例子:
var s = "123";
s = Number(s);
转换的情况:
- 字符串 > 数字
如果字符串是一个合法的数字,则直接转换为对应的数字
如果字符串是一个非法的数字,则转换为NaN
如果是一个空串或纯空格的字符串,则转换为0 - 布尔值 > 数字
true转换为1
false转换为0 - 空值 > 数字
null转换为0 - 未定义 > 数字
undefined 转换为NaN
方式二(强制类型转换):
调用parseInt()或parseFloat()
这两个函数专门用来将一个字符串转换为数字的
如果对非String使用parseInt()或parseFloat(),它会先将其转换为String然后在操作 parseInt()
可以将一个字符串中的有效的整数位提取出来,并转换为Number
例子:
var a = "123.456px";
a = parseInt(a); //123
如果需要可以在parseInt()中指定一个第二个参数,来指定进制parseFloat()可以将一个字符串中的有效的小数位提取出来,并转换为Number
例子:
var a = "123.456px";
a = parseFloat(a); //123.456
方式三(隐式的类型转换):
使用一元的+来进行隐式的类型转换
例子:
var a = "123";
a = +a;
原理:和Number()函数一样
3.2.3 转换为布尔值
方式一(强制类型转换):
使用Boolean()函数
例子:
var s = "false";
s = Boolean(s); //true
转换的情况
字符串 > 布尔
除了空串其余全是true
数值 > 布尔
除了0和NaN其余的全是true
null、undefined > 布尔
都是false
对象 > 布尔
都是true
方式二(隐式类型转换):
为任意的数据类型做两次非运算,即可将其转换为布尔值
例子:
var a = "hello";
a = !!a; //true
4. 基础语法
4.1 运算符
运算符也称为操作符,通过运算符可以对一个或多个值进行运算或操作。
typeof运算符
用来检查一个变量的数据类型
语法:typeof 变量
它会返回一个用于描述类型的字符串作为结果
算数运算符
+ 对两个值进行加法运算并返回结果
- 对两个值进行减法运算并返回结果
* 对两个值进行乘法运算并返回结果
/ 对两个值进行除法运算并返回结果
% 对两个值进行取余运算并返回结果
除了加法以外,对非Number类型的值进行运算时,都会先转换为Number然后在做运算。
而做加法运算时,如果是两个字符串进行相加,则会做拼串操作,将两个字符连接为一个字符串。
任何值和字符串做加法,都会先转换为字符串,然后再拼串
一元运算符
一元运算符只需要一个操作数
一元的+
就是正号,不会对值产生任何影响,但是可以将一个非数字转换为数字
例子:
var a = true;
a = +a;
一元的-
就是负号,可以对一个数字进行符号位取反
例子:
var a = 10;
a = a;
自增
自增可以使变量在原值的基础上自增1
自增使用 ++
自增可以使用 前++(a)后(a++)
无论是a 还是 a都会立即使原变量自增1
不同的是a和a的值是不同的,
a的值是变量的新值(自增后的值)
a的值是变量的原值(自增前的值)
自减
自减可以使变量在原值的基础上自减1
自减使用
自减可以使用 前(a)后(a)
无论是a 还是 a都会立即使原变量自减1
不同的是a和a的值是不同的,
a的值是变量的新值(自减后的值)
a的值是变量的原值(自减前的值)
逻辑运算符
!
非运算可以对一个布尔值进行取反,true变false false边true
当对非布尔值使用!时,会先将其转换为布尔值然后再取反
我们可以利用!来将其他的数据类型转换为布尔值
&&
&&可以对符号两侧的值进行与运算
只有两端的值都为true时,才会返回true。只要有一个false就会返回false。
与是一个短路的与,如果第一个值是false,则不再检查第二个值
对于非布尔值,它会将其转换为布尔值然后做运算,并返回原值
规则:
1.如果第一个值为false,则返回第一个值
2.如果第一个值为true,则返回第二个值
||
||可以对符号两侧的值进行或运算
只有两端都是false时,才会返回false。只要有一个true,就会返回true。
或是一个短路的或,如果第一个值是true,则不再检查第二个值
对于非布尔值,它会将其转换为布尔值然后做运算,并返回原值
规则:
1.如果第一个值为true,则返回第一个值
2.如果第一个值为false,则返回第二个值
赋值运算符
=
可以将符号右侧的值赋值给左侧变量
+=
a += 5 相当于 a = a+5
var str = "hello";str += "world";
-=
a -= 5相当于 a = a-5
*=
a *= 5 相当于 a = a*5
/=
a /= 5 相当于 a = a/5
%=
a %= 5 相当于 a = a%5
关系运算符
关系运算符用来比较两个值之间的大小关系的
>
>=
<
<=
关系运算符的规则和数学中一致,用来比较两个值之间的关系,
如果关系成立则返回true,关系不成立则返回false。
如果比较的两个值是非数值,会将其转换为Number然后再比较。
如果比较的两个值都是字符串,此时会比较字符串的Unicode编码,而不会转换为Number。
相等运算符
相等,判断左右两个值是否相等,如果相等返回true,如果不等返回false
相等会自动对两个值进行类型转换,如果对不同的类型进行比较,会将其转换为相同的类型然后再比较,转换后相等它也会返回true,null == undifined
!=
不等,判断左右两个值是否不等,如果不等则返回true,如果相等则返回false
不等也会做自动的类型转换。
===
全等,判断左右两个值是否全等,它和相等类似,只不过它不会进行自动的类型转换,
如果两个值的类型不同,则直接返回false
!==
不全等,和不等类似,但是它不会进行自动的类型转换,如果两个值的类型不同,它会直接返回true
特殊的值:
null和undefined
由于undefined衍生自null,所以null == undefined 会返回true。
但是 null === undefined 会返回false。
NaN
NaN不与任何值相等,报告它自身 NaN == NaN //false
判断一个值是否是NaN
使用isNaN()函数
三元运算符:
?:
语法:条件表达式?语句1:语句2;
执行流程:
先对条件表达式求值判断,
如果判断结果为true,则执行语句1,并返回执行结果
如果判断结果为false,则执行语句2,并返回执行结果
优先级:
和数学中一样,JS中的运算符也是具有优先级的,
比如 先乘除 后加减 先与 后或
具体的优先级可以参考优先级的表格,在表格中越靠上的优先级越高,
优先级越高的越优先计算,优先级相同的,从左往右计算。
优先级不需要记忆,如果越到拿不准的,使用()来改变优先级。
5. 流程控制语句
程序都是自上向下的顺序执行的,通过流程控制语句可以改变程序执行的顺序,或者反复的执行某一段的程序。
5.1 条件分支语句
条件判断语句也称为if语句
语法一:
if(条件表达式){
语句...
}
执行流程:
if语句执行时,会先对条件表达式进行求值判断,
如果值为true,则执行if后的语句
如果值为false,则不执行
语法二:
if(条件表达式){
语句...
}else{
语句...
}
执行流程:
if...else语句执行时,会对条件表达式进行求值判断,
如果值为true,则执行if后的语句
如果值为false,则执行else后的语句
语法三:
if(条件表达式){
语句...
}else if(条件表达式){
语句...
}else if(条件表达式){
语句...
}else if(条件表达式){
语句...
}else{
语句...
}
执行流程
if...else if...else语句执行时,会自上至下依次对条件表达式进行求值判断,
如果判断结果为true,则执行当前if后的语句,执行完成后语句结束。
如果判断结果为false,则继续向下判断,直到找到为true的为止。
如果所有的条件表达式都是false,则执行else后的语句
1.条件分支语句
switch语句
语法:
switch(条件表达式){
case 表达式:
语句...
break;
case 表达式:
语句...
break;
case 表达式:
语句...
break;
default:
语句...
break;
}
执行流程:
switch...case...语句在执行时,会依次将case后的表达式的值和switch后的表达式的值进行全等比较,
如果比较结果为false,则继续向下比较。如果比较结果为true,则从当前case处开始向下执行代码。
如果所有的case判断结果都为false,则从default处开始执行代码。
5.2 循环语句
通过循环语句可以反复执行某些语句多次
while循环
语法:
while(条件表达式){
语句...
}
执行流程:
while语句在执行时,会先对条件表达式进行求值判断,
如果判断结果为false,则终止循环
如果判断结果为true,则执行循环体
循环体执行完毕,继续对条件表达式进行求值判断,依此类推
do...while循环
语法:
do{
语句...
}while(条件表达式)
执行流程
do...while在执行时,会先执行do后的循环体,然后在对条件表达式进行判断,
如果判断判断结果为false,则终止循环。
如果判断结果为true,则继续执行循环体,依此类推
和while的区别:
while:先判断后执行
do...while: 先执行后判断
do...while可以确保循环体至少执行一次。
for循环
语法:
for(①初始化表达式 ; ②条件表达式 ; ④更新表达式){
③语句...
}
执行流程:
首先执行①初始化表达式,初始化一个变量,
然后对②条件表达式进行求值判断,如果为false则终止循环
如果判断结果为true,则执行③循环体
循环体执行完毕,执行④更新表达式,对变量进行更新。
更新表达式执行完毕重复②
死循环
while(true){
}
for(;;){
}
5.3 遍历
使用in检查对象中是否含有指定属性
语法:"属性名" in 对象
如果在对象中含有该属性,则返回true
如果没有则返回false
循环遍历对象自身的和继承的可枚举属性(不含Symbol属性).
var obj = {'0':'a','1':'b','2':'c'};
for(var i in obj) {
console.log(i,":",obj[i]);
}
使用对象字面量,在创建对象时直接向对象中添加属性
语法:
var obj = {
属性名:属性值,
属性名:属性值,
属性名:属性值,
属性名:属性值
}
基本数据类型和引用数据类型
基本数据类型
String Number Boolean Null Undefined
引用数据类型
Object
基本数据类型的数据,变量是直接保存的它的值。
变量与变量之间是互相独立的,修改一个变量不会影响其他的变量。
引用数据类型的数据,变量是保存的对象的引用(内存地址)。
如果多个变量指向的是同一个对象,此时修改一个变量的属性,会影响其他的变量。
比较两个变量时,对于基本数据类型,比较的就是值,
对于引用数据类型比较的是地址,地址相同才相同
6. 对象(Object)
对象是JS中的引用数据类型
对象是一种复合数据类型,在对象中可以保存多个不同数据类型的属性
使用typeof检查一个对象时,会返回object
对象的分类:
1.内建对象
- 由ES标准中定义的对象,在任何的ES的实现中都可以使用
- 比如:Math String Number Boolean Function Object....
2.宿主对象
- 由JS的运行环境提供的对象,目前来讲主要指由浏览器提供的对象
- 比如 BOM DOM
3.自定义对象
- 由开发人员自己创建的对象
创建对象
方式一:
var obj = new Object();
方式二:
var obj = {};
向对象中添加属性
语法:
对象.属性名 = 属性值;
对象["属性名"] = 属性值; //这种方式能够使用特殊的属性名
对象的属性名没有任何要求,不需要遵守标识符的规范,但是在开发中,尽量按照标识符的要求去写。
属性值也可以任意的数据类型。
读取对象中的属性
语法:
对象.属性名
对象["属性名"] //"属性名"可以使字符串常量,也可以是字符串变量
如果读取一个对象中没有的属性,它不会报错,而是返回一个undefined
删除对象中的属性
语法:
delete 对象.属性名
delete 对象["属性名"]
7. 函数(Function)
函数也是一个对象,也具有普通对象的功能(能有属性),函数中可以封装一些代码,在需要的时候可以去调用函数来执行这些代码,使用typeof检查一个函数时会返回function;
函数声明
function 函数名([形参1,形参2...形参N]){
语句...
}
函数表达式
var 函数名 = function([形参1,形参2...形参N]){
语句...
};
调用函数
语法:函数对象([实参1,实参2...实参N]);
fun() sum() alert() Number() parseInt()
当我们调用函数时,函数中封装的代码会按照编写的顺序执行
立即执行函数
函数定义完,立即被调用,这种函数叫做立即执行函数
立即执行函数往往只会执行一次
(function(a,b){
console.log("a = "+a);
console.log("b = "+b);
})(123,456);
遍历对象
for(var v in obj){
document.write("property:name ="+v+"value="+obj[v]+"<br/>" );
}
/*
调用函数时JS解析器不会检查实参的类型和个数,可以传递任意数据类型的值。
**如果实参的数量大于形参,多余实参将不会赋值,**
**如果实参的数量小于形参,则没有对应实参的形参将会赋值undefined**
*/
7.1 函数的属性和方法
call()
apply()
这两个方法都是函数对象的方法需要通过函数对象来调用
通过两个方法可以直接调用函数,并且可以通过第一个实参来指定函数中this
不同的是call是直接传递函数的实参而apply需要将实参封装到一个数组中传递
arguments
arguments和this类似,都是函数中的隐含的参数
arguments是一个类数组元素,它用来封装函数执行过程中的实参
所以即使不定义形参,也可以通过arguments来使用实参
arguments中有一个属性callee表示当前执行的函数对象
this(调用函数的那个对象)
this是函数的上下文对象,根据函数的调用方式不同会执向不同的对象
1.以函数的形式调用时,this是window
2.以方法的形式调用时,this是调用方法的对象
3.以构造函数的形式调用时,this是新建的那个对象
4.使用call和apply调用时,this是指定的那个对象
5.在全局作用域中this代表window
8. 作用域
作用域简单来说就是一个变量的作用范围。
在JS中作用域分成两种:
1.全局作用域
直接在script标签中编写的代码都运行在全局作用域中
全局作用域在打开页面时创建,在页面关闭时销毁。
全局作用域中有一个全局对象window,window对象由浏览器提供,
可以在页面中直接使用,它代表的是整个的浏览器的窗口。
在全局作用域中创建的变量都会作为window对象的属性保存
在全局作用域中创建的函数都会作为window对象的方法保存
在全局作用域中创建的变量和函数可以在页面的任意位置访问。
在函数作用域中也可以访问到全局作用域的变量。
尽量不要在全局中创建变量
2.函数作用域
函数作用域是函数执行时创建的作用域,每次调用函数都会创建一个新的函数作用域。
函数作用域在函数执行时创建,在函数执行结束时销毁。
在函数作用域中创建的变量,不能在全局中访问。
当在函数作用域中使用一个变量时,它会先在自身作用域中寻找,
如果找到了则直接使用,如果没有找到则到上一级作用域中寻找,
如果找到了则使用,找不到则继续向上找,一直会
变量的声明提前
在全局作用域中,使用var关键字声明的变量会在所有的代码执行之前被声明,但是不会赋值。
所以我们可以在变量声明前使用变量。但是不使用var关键字声明的变量不会被声明提前。
在函数作用域中,也具有该特性,使用var关键字声明的变量会在函数所有的代码执行前被声明,
如果没有使用var关键字声明变量,则变量会变成全局变量
函数的声明提前
在全局作用域中,使用函数声明创建的函数(function fun(){}),会在所有的代码执行之前被创建,
也就是我们可以在函数声明前去调用函数,但是使用函数表达式(var fun = function(){})创建的函数没有该特性
在函数作用域中,使用函数声明创建的函数,会在所有的函数中的代码执行之前就被创建好了。
9. this(上下文对象)
我们每次调用函数时,解析器都会将一个上下文对象作为隐含的参数传递进函数。
使用this来引用上下文对象,根据函数的调用形式不同,this的值也不同。
this的不同的情况:
1.以函数的形式调用时,this是window
2.以方法的形式调用时,this就是调用方法的对象
3.以构造函数的形式调用时,this就是新创建的对象
10. 构造函数
构造函数是专门用来创建对象的函数
一个构造函数我们也可以称为一个类
通过一个构造函数创建的对象,我们称该对象时这个构造函数的实例
通过同一个构造函数创建的对象,我们称为一类对象
构造函数就是一个普通的函数,只是他的调用方式不同,
如果直接调用,它就是一个普通函数
如果使用new来调用,则它就是一个构造函数
例子:
function Person(name , age , gender){
this.name = name;
this.age = age;
this.gender = gender;
this.sayName = function(){
alert(this.name);
};
}
构造函数的执行流程:
1.创建一个新的对象
2.将新的对象作为函数的上下文对象(this)
3.执行函数中的代码
4.将新建的对象返回
instanceof 用来检查一个对象是否是一个类的实例
语法:对象 instanceof 构造函数
如果该对象时构造函数的实例,则返回true,否则返回false
Object是所有对象的祖先,所以任何对象和Object做instanceof都会返回true
枚举对象中的属性
for...in
语法:
for(var 属性名 in 对象){
}
for...in语句的循环体会执行多次,对象中有几个属性就会执行几次,
每次讲一个属性名赋值给我们定义的变量,我们可以通过它来获取对象中的属性
11. 原型(prototype)
创建一个函数以后,解析器都会默认在函数中添加一个数prototype
prototype属性指向的是一个对象,这个对象我们称为原型对象。
当函数作为构造函数使用,它所创建的对象中都会有一个隐含的属性执行该原型对象。
这个隐含的属性可以通过对象.__proto__来访问。
原型对象就相当于一个公共的区域,凡是通过同一个构造函数创建的对象他们通常都可以访问到相同的原型对象。
我们可以将对象中共有的属性和方法统一添加到原型对象中,
这样我们只需要添加一次,就可以使所有的对象都可以使用。
当我们去访问对象的一个属性或调用对象的一个方法时,它会先自身中寻找,
如果在自身中找到了,则直接使用。
如果没有找到,则去原型对象中寻找,如果找到了则使用,
如果没有找到,则去原型的原型中寻找,依此类推。直到找到Object的原型为止,Object的原型的原型为null,
如果依然没有找到则返回undefined
hasOwnProperty()
这个方法可以用来检查对象自身中是否含有某个属性
语法:对象.hasOwnProperty("属性名")
12. toString方法
当我们直接在页面中打印一个对象时,事件上是输出的对象的toString()方法的返回值
如果我们希望在输出对象时不输出[object Object],可以为对象添加一个toString()方法
//修改Person原型的toString
Person.prototype.toString = function(){
return "Person[name="+this.name+",age="+this.age+",gender="+this.gender+"]";
};