一.变量及数据类型
1.变量
变量是用来临时存储数值的容器,在使用过程中可以随时改变储存的值。
2.变量的命名规则
- 只能是 数字、字母、下划线、$,且数字不能开头;
- 使用var关键字声明变量,如果不使用 var 则会污染全局变量。
3.变量的数据类型:
- PHP中的数据类型,共有八种:
对象,null,数组,整形,资源,浮点,字符串,布尔; - JavaScript 的数据类型,共有六种:
对象,数值,字符串,布尔,null ,undefined
4.检测值类型
- typeof运算符可以返回一个值的数据类型
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript</title>
</head>
<body>
<script>
var s = null; //object
var u = undefined; //undefinded
alert(typeof(s));
alert(typeof(u));
</script>
</body>
</html>
效果:
5.null 和 undefined
- 为什么 null的类型也是object?
这是由于历史原因造成的。1995年JavaScript语言的第一版,当时,只设计了五种数据类型(对象、整数、浮点数、字符串和布尔值),完全没考虑null,只把它当作object的一种特殊值,为了兼容以前的代码,后来就没法修改了。
这并不是说null就属于对象,本质上就是一个特殊值,表示“没有”
null与undefined都可以表示“没有”,含义非常相似;
相等运算符(==)甚至直接报告两者相等:
- 宽松比较时,两者相等,都表示 空 、没有、无 等含义
- undefined的出现情况:
(1) 变量没有值
(2) 函数调用需要传值,但是没有传值时,函数返回值为undefined
(3) 函数没有return时,函数的返回值为undefined
(4) 获取对象中不存在的属性时 - null的出现情况:
(1) 获取一个不存在的对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript</title>
</head>
<body>
<script>
//变量没有值
var i;
console.log(typeof(i));
//函数调用需要传值,但是没有传值时,函数返回值为undefined
function f(x){
return x;
}
var s = f();
console.log(s);
//函数没有return时,函数的返回值为undefined
function f1(){}
var g = f1();
console.log(g);
//获取对象中不存在的属性时
var o = {};
console.log(o.p);
//获取一个不存在的对象
var x = document.getElementById('x');
console.log(x);
</script>
</body>
</html>
效果:
- null 的数据类型是object ,undefined数据类型就是undefined;
- null 是表示 ‘无’ 的对象,转为数值时是 0;
undefined 是表示‘无’ 的原始值,转为数值时NaN;
二.运算符
1.算数运算符
-
+
加法 -
-
减法 -
*
乘法 -
/
除法 -
%
系数 -
++
递加 -
--
递减
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript</title>
</head>
<body>
<script>
var i = 10;
var j = i++; //先做其他运算,再做自增运算
var h = ++i; //先做自增运算,再做其他运算
console.log(j);
console.log(h);
</script>
</body>
</html>
效果:
2.比较运算符
-
==
等于 -
===
等值等型 -
!=
不相等 -
!==
不等值或不等型 -
>
大于 -
<
小于 -
>=
大于或等于 -
<=
小于或等于 -
?
三元运算符
3.逻辑运算符
-
&&
逻辑与 -
||
逻辑或 -
!
逻辑非
其中,&& 和 || 运算的结果是:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript</title>
</head>
<body>
<script>
var i = 3;
//取反操作,先对变量进行转换,然后取反
console.log(!i); //false
var a = 0;
var b1 = 3;
var b2 = 4;
var c = true;
var d = false;
//在JS中&&和||运算的结果是决定整个表达式结果的子表达式的值
var x = b1||b2; //3
var y = a||b2; //4
var z = c||b1; //true
var o = b1&&c; //true
var p = a&&d; //0
var q = b2&&d; //false
console.log(x);
console.log(y);
console.log(z);
console.log(o);
console.log(p);
console.log(q);
</script>
</body>
</html>
效果:
控制台打印出相应结果
(
&&
优先级比||
高)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript</title>
</head>
<body>
<script>
var a = 0;
var b = 1;
var c = true
console.log(b || a && c); //1
</script>
</body>
</html>
效果:
三.流程结构
1.顺序结构:从上而下执行
2.分支结构(选择结构)
if结构不仅可以判断固定值,还可以判断范围区间。
switch结构只能判断固定值情况。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript</title>
</head>
<body>
<script>
var a = 4;
var b = 3;
if(a>=5){
console.log("大于等于5");
}else if(a<4)
{
console.log("小于4");
}else{
console.log("4");
}
switch(b){
case 3:
console.log(3);break;
case 4:
console.log(4);break;
case 5:
console.log(5);break;
default:
console.log("no");
}
</script>
</body>
</html>
效果:
3.循环结构
-
continue
:跳出当前循环,继续下次循环; -
break
: 跳出整个循环
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript</title>
</head>
<body>
<script>
var i = 5;
while(i>0)
{
console.log(i);
i--;
}
</script>
</body>
</html>
效果:
四.数组
数组是一组有序数据的集合,在内存中表现为一段连续的内存地址。
1.JS数组的声明
- 字面量声明
- 构造函数声明
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript</title>
</head>
<body>
<script>
//字面量声明
var arr1 = [1,2,3,4];
console.log(arr1);
//构造函数声明
var arr2 = new Array();
arr2[0] = '1';
arr2[1] = 1;
arr2[2] = '3';
console.log(arr2);
</script>
</body>
</html>
效果:
2.多维数组
数组A中的元素值是一个数组,则数组A为多维数组;
3.获取数组元素
基本语法: 数组名[元素标号]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript</title>
</head>
<body>
<script>
//多维数组
var arr = [2,[3,4,[5,6,[7,8,[9,10]]]]];
console.log(arr[1][2][2][2][0]) //9
</script>
</body>
</html>
效果:
4.数组的遍历操作
-
for
循环,根据循环的语法,在循环之前,用arr.length
属性获取数组的长度; -
for…in
循环遍历数组:for...in
循环不仅可以遍历对象,也可以遍历数组,毕竟数组只是一种特殊对象。 -
forEach()
方法遍历数组
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript</title>
</head>
<body>
<script>
var arr = ['1',2,'三','Ⅳ',[4,5]];
//for循环
for(var i=0;i<arr.length;i++)
{
console.log(arr[i]);
}
console.log('\n');
//for...in循环
for(var i in arr){
console.log(arr[i]);
}
console.log('\n');
//forEach方法
arr.forEach(function(v,k){
console.log(v);
})
</script>
</body>
</html>
效果:
5.数组的属性和方法
(1) 数组对象的常用属性
-
length
属性: 返回数组的成员数量。
(2) 数组对象的常用方法
push
方法用于在数组的末端添加一个或多个元素,并返回添加新元素后的数组长度。pop
方法用于删除数组的最后一个元素,并返回该元素。join
方法以参数作为分隔符,将所有数组成员组成一个字符串返回。如果不提供参数,默认用逗号分隔。concat
方法用于多个数组的合并。它将新数组的成员,添加到原数组的尾部,然后返回一个新数组,原数组不变。shift
方法用于删除数组的第一个元素,并返回该元素。reverse
方法用于颠倒数组中元素的顺序,返回改变后的数组。slice
方法用于提取原数组的一部分,返回一个新数组,原数组不变。
它的第一个参数为起始位置(从0开始),第二个参数为终止位置(但该位置的元素本身不包括在内)。
如果省略第二个参数,则一直返回到原数组的最后一个成员。splice
方法用于删除原数组的一部分成员,并可以在被删除的位置添加入新的数组成员,返回值是被删除的元素。
splice
的第一个参数是删除的起始位置,第二个参数是被删除的元素个数。如果后面还有更多的参数,则表示这些就是要被插入数组的新元素。forEach
方法用于遍历数组的所有成员,执行某种操作,forEach
方法的参数是一个函数,数组的所有成员会依次执行该函数,
它接受三个参数,分别是当前位置的值、当前位置的编号和整个数组。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript</title>
</head>
<body>
<script>
var arr = ['1',2,'三','Ⅳ',[4,5]];
console.log(arr.length); //获取数组个数
console.log("\n");
arr.push(6); //往数组中最后面添加元素
console.log(arr);
console.log("\n");
var a = arr.pop(); //弹出数组中最后面的元素
console.log(a);
console.log(arr);
console.log("\n");
console.log(arr.join()); //以逗号作为分隔符,将所有数组成员组成一个字符串返回
</script>
</body>
</html>
效果:
五.函数
1.函数定义
- 函数就是一段可以反复调用的代码块。
- 函数接受输入的参数,不同的参数会返回不同的值。
2.函数到底有什么用?
其实就是将很多重复的代码写到一个地方,在需要执行代码的地方,直接调用函数即可。
- 节省了我们代码的数量,使代码可以重用;
- 使我们的程序代码,具有结构化,模块化;
- 如果程序要求发生变化,我们只需要修改一次函数即可;
3.函数声明及调用
- 函数的一般声明方式:
function f(){}
- 表达式声明匿名函数:
var f =function(){}
- 函数的调用:
f()
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript</title>
</head>
<body>
<script>
//表达式声明匿名函数,变量指向内存中函数的位置
var f = function()
{
//函数体
console.log(2);
}
//函数的一般声明方式
function f()
{
//函数体
console.log(1);
}
// 变量的调用比函数要有优先级
f(); //2
</script>
</body>
</html>
效果:
3.递归
- 递归就是函数自己调用自己
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript</title>
</head>
<body>
<script>
//计算1+2+3+...+100
function sum(n)
{
if(n==1){
return 1;
}else{
return n+sum(n-1);
}
}
console.log(sum(100));
</script>
</body>
</html>
结果:控制台打印出50504.函数的参数及返回值
在函数中,参数一共有两种形式:函数的形参、函数的实参
- 形参:在函数定义时所指定的参数,就称之为“形参”。
- 实参:在函数调用时所指定的参数,就称之为“实参”。
- 返回值: 函数体内部的return语句,表示将数据返回给调用者;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript</title>
</head>
<body>
<script>
function f(a,b) //定义函数时传参为形参
{
return a+b; //return给函数调用者返回数据
//后面的语句均不执行
}
var x = f(1,2); //调用函数时传参为实参
console.log(x);
</script>
</body>
</html>
结果:
- 函数中的
arguments
(参数列表)对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript</title>
</head>
<body>
<script>
function f() //定义函数时传参为形参
{
//arguments 是函数中的对象,这个对象中包含所有实参,实参列表以数组形式存在
console.log(arguments)
console.log(arguments[2])
console.log(arguments.length)
}
f('1',2,'三',4,5,6); //调用函数时传参为实参
</script>
</body>
</html>
效果:
5.自调用匿名函数
- 基本语法:
(function(){}) ()
- 为什么要有自调用匿名函数?
在之前的程序开发中,我们都是通过有名函数来实现程序封装的,但是有名函数有一个缺点,可能在运行时会出现命名冲突。
如果在编写程序时,我们采用自调用匿名函数,则不会产生命名冲突问题,
所以很多框架jquery.js、ext.js等等都是采用自调用匿名函数进行封装的。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript</title>
</head>
<body>
<script>
//自调用匿名函数
(function(){
console.log(3);
})();
//自调用匿名函数实现递归
//计算1+2+3+...+100
var a = (function(n){
if(n==1){
return 1;
}else{
//arguments.callee代表函数自身
return n+arguments.callee(n-1)
}
})(100);
console.log(a);
</script>
</body>
</html>
效果:
六.作用域
1.变量的作用域
变量的作用域作用域指的是变量存在的范围,Javascript中变量有两种作用域:
(1) 一种是全局作用域,变量在整个程序中一直存在,所有地方都可以读取,变量也被称为全局变量可以采用两种方式创建全局变量。
- 第一种方法是在所有函数外部使用var语句,在函数外部声明的变量都属于全局作用域。
- 第二种方法是在声明变量的时候忽略var语句(也称为隐式全局变量)。
(2) 另一种是局部作用域,变量只在函数内部存在。称为局部变量;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript</title>
</head>
<body>
<script>
var b = 3;
var i = 1;
function f(){
var b = 5;
var a = 1;
c = 4; //不加var,c变成全局变量
//函数内部允许访问函数外部的全局变量
console.log(i); //1
//函数内部的变量暂时覆盖了全局变量
console.log(b); //5
}
f();
console.log(b) //3
console.log(c) //4
</script>
</body>
</html>
效果:
2.变量提升
变量提升:你在一个作用域任何一个地方使用var
声明变量,都会被提升到作用域开始
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript</title>
</head>
<body>
<script>
function f1(){
console.log(h);
var h = 3;
}
//f被调用的瞬间,函数内部所有变量声明被提升
f1(); //不会报错,会打印undefined
//相当于这个函数
function f2(){
var h;
console.log(h);
h = 3;
}
f2(); //不会报错,会打印undefined
function f3(){
console.log(h);
//let声明的函数不会变量提升
let h = 3;
}
f3(); //会报错
</script>
</body>
</html>
效果:
3.函数的作用域
函数本身也是一个值,也有自己的作用域。它的作用域与变量一样,就是其声明时所在的作用域,与其运行时所在的作用域无关。
总之,函数执行时所在的作用域,是的作用域,而不是调用时所在的作用域。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript</title>
</head>
<body>
<script>
var a = 1;
function x(){
console.log(a);
}
function x2(){
var a = 3;
x();
}
x2();
</script>
</body>
</html>
效果:
六.词法分析
1.代码运行的两个阶段
代码在运行时,共分为两个阶段:编译阶段、执行阶段
- 编译阶段:声明变量、声明函数(display)、语法检查(报错)等……
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript</title>
</head>
<body>
<script>
console.log(1);
console.log(s);
console.log(2 //语法错误,全部代码不执行
</script>
</body>
</html>
效果:
- 执行阶段:变量的赋值、函数的调用、代码的执行等都是发生在执行阶段
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript</title>
</head>
<body>
<script>
console.log(1);
console.log(s); //遇到错误,停止运行
console.log(2);
</script>
</body>
</html>
效果:
打印出1,然后报错