一、数组
1.1 数组的概念
数组是一组数据的集合,其中的每个数据被称作元素,在数组中可以存放任意类型的元素。
数组是一种将一组数据存储在单个变量名下的优雅方式。
1.2 创建数组
1.利用new创建数组
注意:
-
这种方式暂且了解,等学完对象再看
-
注意
Array()
,A要大写
2.利用数组字面量创建数组
// 使用数组字面量的方式创建空的数组
var 数组名 = [];
// 使用数组字面量方式创建带初始值的数组
var arr1 = [1, 2, 'pink老师', true];
注意:
- 数组里面可以放任意的数据类型
- 数组里面的数据用,分隔
- 数组里面的数据称为数组元素
- 这种方式是使用最多的方式
1.3 获取数组中的元素
数组的索引
索引(下标):用来访问数组元素的序号(数组下标从0开始)。
格式:数组名[索引号]
var arr2 = ['迪丽热巴', '古丽扎娜', '佟丽丫丫'];
console.log(arr2[0]);
console.log(arr2[1]);
console.log(arr2[2]);
console.log(arr2[3]); // 因为没有这个数组元素 所以输出的结果是 undefined
1.4 遍历数组
遍历:把数组中每个元素从头到尾访问一次。
<script>
// 遍历数组:就是把数组的元素从头到尾访问一次
var arr = ['red', 'green', 'blue'];
for (var i = 0; i < 3; i++) {
console.log(arr[i]);
}
// 1. 因为我们的数组索引号从0开始 ,所以 i 必须从 0开始 i < 3
// 2. 输出的时候 arr[i] i 计数器当索引号来用
</script>
1.5 获取数组长度
使用“数组名.length”可以访问数组元素的数量(数组长度)
<script>
// 数组长度 数组名.length
var arr = ['关羽', '张飞', '马超', '赵云', '黄忠', '刘备', '姜维', 'pink'];
for (var i = 0; i < 7; i++) {
console.log(arr[i]);
}
console.log(arr.length);
for (var i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
// 1. 数组的长度是元素个数 不要跟索引号混淆
// 2. arr.length 动态监测数组元素的个数
</script>
- for 里面的 i 是计数器,当索引号使用,arr[i]是数组元素,第i个数组元素。
⭐数组求和及平均值
分析:
- 声明一个求和变量 sum。
- 遍历这个数组,把里面每个数组元素加到 sum 里面。
- 用求和变量 sum 除以数组的长度就可以得到数组的平均值。
<script>
var array = [2,6,1,7,4];
var sum = 0;
var avg = 0;
for (var i = 0; i < array.length; i++){
sum += array[i]; //我们加的是数组元素,不是计数器
}
avg = sum / array.length;
console.log(sum,avg);
</script>
⭐求数组最大值
<script>
var arr = [2,6,1,77,52,25,7];
var max =arr[0];
for (var i = 1; i < arr.length; i++){
if (arr[i] > max){
max = arr[i];
}
}
console.log(max);
</script>
⭐数组转换为分割字符串
要求:将数组['red','green','blue','pink']转换为字符串,并用|或其他分隔符分割
<script>
//需要一个新变量用于存放转换完的字符串str
//遍历原来的数组,分别把里面的数据取出来,加到字符串里面
//同时在后面多加一个分隔符
var arr = ['red','blue','green','pink'];
var str = '';
var sep = '|';
for (var i = 0; i < arr.length; i++){
str += arr[i] + sep ;
}
console.log(str);
alert(str);
</script>
1.6 数组中新增元素
可以通过修改length长度以及索引和增加数组元素。
1. 通过修改length长度新增数组元素
- 可以通过修改length长度来实现数组扩容的目的
- length属性是可读写的
2.通过修改数组索引新增数组元素
- 可以通过修改数组索引的方式追加数组元素
- 不能直接给数组名赋值,否则会覆盖掉以前的数据
<script>
// 1. 新增数组元素 修改length长度
var arr = ['red', 'green', 'blue'];
console.log(arr.length);
arr.length = 5; // 把我们数组的长度修改为了 5 里面应该有5个元素
console.log(arr);
console.log(arr[3]); // undefined
console.log(arr[4]); // undefined
// 2. 新增数组元素 修改索引号 追加数组元素
var arr1 = ['red', 'green', 'blue'];
arr1[3] = 'pink';
console.log(arr1);
arr1[4] = 'hotpink';
console.log(arr1);
arr1[0] = 'yellow'; // 这里是替换原来的数组元素
console.log(arr1);
arr1 = '有点意思';
console.log(arr1); // 不要直接给 数组名赋值 否则里面的数组元素都没有了
</script>
⭐数组新增元素
新建一个数组,里面存放10个整数(1-10)
分析:
- 使用循环来追加数组
- 声明一个空数组arr
- 循环中的计数器i 可以作为数组元素存入
- 由于数组的索引号是从0开始的,因此计数器从0开始更合适,存入的数据元素要加+1
var arr = [];
for (var i = 0; i < 100; i++) {
// arr = i; 不要直接给数组名赋值 否则以前的元素都没了
arr[i] = i + 1;
}
console.log(arr);
⭐筛选数组
要求:将数组[2,0,6,1,77,0,52,0,25,7]中大于等于10的元素选出来,放入新数组
<script>
//方法1
var arr = [2,0,6,1,77,0,52,0,25,7];
var newarr = [];
var j = 0;
for (var i = 0; i < arr.length; i++){
if (arr[i] > 10){
//新数组的索引号应该从0开始存
newarr[j] = arr[i];
j++;
}
}
console.log(newarr);
//方法2
var arr = [2,0,6,1,77,0,52,0,25,7];
var newarr = [];
// 刚开始的newarr.length为0,length会自动检测元素的变化
for (var i = 0; i < arr.length; i++){
if (arr[i] > 10){
//新数组的索引号应该从0开始存,依次递增
newarr[newarr.length] = arr[i];
j++;
}
}
console.log(newarr);
</script>
1.7 数组案例
⭐删除指定数组元素
要求:将数组[2, 0, 6, 1, 77, 0, 52, 0, 25, 7]中的 0 去掉后,形成一个不包含 0 的新数组。
<script>
// 将数组[2, 0, 6, 1, 77, 0, 52, 0, 25, 7]中的 0 去掉后,形成一个不包含 0 的新数组。
// 1、需要一个新数组用于存放筛选之后的数据。
// 2、遍历原来的数组, 把不是 0 的数据添加到新数组里面(此时要注意采用数组名 + 索引的格式接收数据)。
// 3、新数组里面的个数, 用 length 不断累加。
var arr = [2, 0, 6, 1, 77, 0, 52, 0, 25, 7];
var newarr = [];
for (var i = 0; i< arr.length; i++){
if (arr[i] != 0){
newarr[newarr.length] = arr[i];
}
}
console.log(newarr);
</script>
⭐翻转数组
将数组 ['red', 'green', 'blue', 'pink', 'purple'] 的内容反过来存放
<script>
// 将数组 ['red', 'green', 'blue', 'pink', 'purple'] 的内容反过来存放
// 1、声明一个新数组 newArr
// 2、把旧数组索引号第4个取过来(arr.length - 1),给新数组索引号第0个元素 (newArr.length)
// 3、我们采取 递减的方式 i--
var arr = ['red', 'green', 'blue', 'pink', 'purple'];
var newarr = [];
for (var i = arr.length-1; i >= 0; i--){
newarr[newarr.length] = arr[i];
}
console.log(newarr);
</script>
⭐数组排序(冒泡排序)
冒泡排序:是一种算法,把一系列的数据按照一定的顺序进行排列的显示(从小到大或从大到小)
<script>
var arr = [5,4,3,2,1];
for (var i = 0; i <= arr.length-1; i++){ //外层循环管趟数
for (var j = 0; j < arr.length-i-1;j++){ //里层循环管每趟的交换次数
//内部交换两个变量的值 前一个和后面一个数组元素相比较
if (arr[j] > arr[j+1]){
var temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
console.log(arr);
</script>
二、函数
2.1 函数的概念
函数计算被封装了一段可以被重复执行调用的代码块。
目的:让大量代码重复使用。
2.2 函数的使用
函数在使用时分为两步:声明函数和调用函数。
1.声明函数
function 函数名() {
函数体
}
- function 声明函数的关键字 全部小写
- 函数是做某件事情,函数名一般是动词
- 函数不调用自己不执行(只声明函数不调用,函数不执行)
2.调用函数
函数名();
- 一定不要忘记小括号
2.3 函数的封装
函数的封装是把一个或者多个功能通过函数的方式封装起来,对外只提供一个简单的函数接口。
⭐利用函数计算1-100之间的累加和
<script>
//利用函数求1-100之间累加和
function getsum(){
var sum = 0;
for (var i = 1; i <= 100; i++){
sum +=i;
}
console.log(sum);
}
getsum();
</script>
2.4 函数的参数
我们可以利用函数的参数实现函数重复不同的代码。
1.形参(声明函数时)
2.实参(调用函数时)
3.形参和实参的执行过程
function cook(aru) { // 形参是接受实参的 aru = '酸辣土豆丝' 形参类似于一个变量
console.log(aru);
}
cook('酸辣土豆丝');
cook('大肘子');
函数的参数可以有也可以没有,个数不限
⭐利用函数求任意两个数的和
⭐利用函数求任意两个数之间的和
<script>
//1.利用函数求任意两个数的和
function getsum(num1,num2){
getsum = num1 + num2 ;
console.log(getsum);
}
getsum(1,2);
//2.利用函数求任意两个数之间的和(如求1-100之间数的和)
function getsum1(num3,num4){
var sum1=0;
for (var i = num3; i <= num4; i++){
sum1 += i;
}
console.log(parseInt(sum1));
}
getsum1(1,10);
</script>
注意点:
- 多个参数之间用逗号隔开
- 形参可以看做不用声明的变量
- 注意有些变量一定要初始化
4.函数形参和实参个数不匹配的问题
<script>
//函数形参实参个数匹配
function getsum(num1,num2){
console.log(num1 + num2);
}
//1.如果实参的个数和形参的个数一致,则正常输出结果
getsum(1,2);
//2.如果实参的个数多于形参的个数 会取到形参的个数
getsum(1,2,3);
//3.如果实参的个数小于形参的个数
//形参可以看做是不用声明的变量 num2是一个变量但是没有接受值
getsum(1);
</script>
5.小结
- 函数可以带参数也可以不带参数
- 声明函数的时候,函数名括号里面的是形参,形参的默认值为 undefined
- 调用函数的时候,函数名括号里面的是实参
- 多个参数中间用逗号分隔
- 形参的个数可以和实参个数不匹配,但是结果不可预计,我们尽量要匹配
2.5 函数的返回值
1.return语句
有的时候,我们会希望函数将值返回给调用者,此时通过使用return语句就可以实现
- 我们函数只是实现某种功能,最终的结果需要返回给函数的调用者函数名() 通过return 实现的
- 只要函数遇到return 就把后面的结果 返回给函数的调用者 函数名() = return后面的结果
注意事项:
- return后面的代码不会被执行
- return只能返回一个值,且返回的结果是最后一个值
- 若要返回多个值,可以采用数组
function getResult(num1, num2) {
return [num1 + num2, num1 - num2, num1 * num2, num1 / num2];
}
var re = getResult(1, 2); // 返回的是一个数组
console.log(re);
⭐求任意两个数的和
function getSum(num1, num2) {
return num1 + num2;
}
console.log(getSum(1, 2));
⭐利用函数求任意两个数的最大值(函数值)
<script>
function getmax (num1,num2) {
var max = 0;
if (num1 > num2){
max = num1;
} else {
max = num2;
}
return max;
}
console.log(getmax(2,6));
</script>
⭐求数组中的最大值(函数版)
两种输出方法都可以
<script>
function getarrmax(arr) {
var max = arr[0];
for ( var i = 1 ; i < arr.length; i++){
if (arr[i] > max){
max = arr[i];
}
}
return max;
}
// console.log(getarrmax([5,2,99,101,67,77]));
var re =getarrmax[5,2,99,101,67,77];
console.log(re);
2.函数没有 return 则返回 undefined
console.log(re);
// 4. 我们的函数如果有return 则返回的是 return 后面的值,如果函数么有 return 则返回undefined
function fun1() {
return 666;
}
console.log(fun1()); // 返回 666
function fun2() {
}
console.log(fun2()); // 函数返回的结果是 undefined
3.break,continue,return的区别
break
: 结束当前循环体(如 for、while)continue
:跳出本次循环,继续执行下次循环(如for、while)return
:不仅可以退出循环,还能够返回 return 语句中的值,同时还可以结束当前的函数体内的代码
2.6 arguments的使用
当我们不确定有多少个参数传递的时候,可以用 arguments 来获取。在 JavaScript 中,arguments 实际上它是当前函数的一个内置对象。所有函数都内置了一个 arguments 对象,arguments 对象中存储了传递的所有实参。
function fn() {
console.log(arguments);
}
fn(1,2,3);
arguments展示形式是一个伪数组,因此可以进行遍历。伪数组具有以下特点
- 具有 length 属性
- 按索引方式储存数据
- 不具有数组的 push , pop 等方法
只有函数才有 arguments 对象,而且是每个函数都内置好了这个arguments。
⭐利用函数求任意个数的最大值
<script>
function getmax() {
var max = arguments[0];
for (var i = 1; i < arguments.length; i++){
if (arguments[i] > max){
max = arguments[i];
}
}
return max;
}
console.log(getmax(1,2,3,4,5,6));
</script>
⭐利用函数封装方式翻转任意一个数组
<script>
//利用函数任意数组reverse翻转
function reverse(arr) {
var newArr = [];
for (var i = arr.length; i >= 0 ; i-- ){
newArr[newArr.length] = arr[i];
}
return newArr;
}
var arr1 =reverse([1,3,4,6,9]);
console.log(arr1);
</script>
⭐利用函数封装方式对数组排序--冒泡排序
<script>
function sort(arr) {
for (var i = 0; i < arr.length-1; i++){
for (var j =0; j < arr.length-i-1; j++){
if (arr[j] > arr[j+1]){
var temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
return arr;
}
var arr1 = sort([1,2,3,4,5]);
console.log(arr1);
</script>
⭐利用函数封装实现判断闰年
<script>
function isRunYear (year) {
var flag = false;
if (year % 4 == 0 && year % 100 !== 0 || year %400 == 0){
flag =true;
}
return flag;
}
console.log(isRunYear(2024));
</script>
⭐函数可以调用另外一个函数
因为每个函数都是独立的代码块,用于完成特殊任务,因此会经常用到函数相互调用的情况。
<script>
//函数是可以相互调用的
function fn1() {
console.log(11);
fn2();
}
fn1();
function fn2() {
console.log(22);
}
</script>
⭐用户输入年份,输出当前年份2月份的天数
如果是闰年,则2月份是29天,如果是平年,则二月份是28天
<script>
// 用户输入年份,输出当前年份2月份的天数
function backDay() {
var year = prompt('请您输入年份:');
if (isRunYear(year)) { // 调用函数需要加小括号
alert('当前年份是闰年2月份有29天');
} else {
alert('当前年份是平年2月份有28天');
}
}
backDay();
// 判断是否为闰年的函数
function isRunYear(year) {
// 如果是闰年我们返回 true 否则 返回 false
var flag = false;
if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) {
flag = true;
}
return flag;
}
</script>
2.7 函数表达式声明函数的方式
<script>
// 函数的2中声明方式
// 1. 利用函数关键字自定义函数(命名函数)
function fn() {
}
fn();
// 2. 函数表达式(匿名函数)
// var 变量名 = function() {};
var fun = function(aru) {
console.log('我是函数表达式');
console.log(aru);
}
fun('pink老师');
// (1) fun是变量名 不是函数名
// (2) 函数表达式声明方式跟声明变量差不多,只不过变量里面存的是值 而 函数表达式里面存的是函数
// (3) 函数表达式也可以进行传递参数
</script>
- fun是变量名 不是函数名
- 函数表达式声明方式跟声明变量差不多,只不过变量里面存的是值 而函数表达式里面存的是函数
- 函数表达式也可以进行传递参数
三、JavaScript作用域
3.1 作用域概述
通常来说,一段程序代码中所用到的名字并不总是有效和可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域。作用域的使用提高了程序逻辑的局部性,增强了程序的可靠性,减少了名字冲突。
JavaScript的作用域(es6之前)有两种:
- 全局作用域
- 局部作用域
1.全局作用域
整个script标签 或者是一个单独的js文件
2.局部作用域(函数作用域)
在函数内部就是函数作用域,这个代码的名字只在函数内部起效果和作用。
3.2 变量的作用域
根据作用域的不同,我们变量分为全局变量和局部变量
1.全局变量
在全局作用域下声明的变量叫做全局变量(在函数外部定义的变量)
-
全局变量在代码的任何位置都可以使用
-
在全局作用域下 var 声明的变量 是全局变量
-
特殊情况下,在函数内不使用 var 声明的变量也是全局变量(不建议使用)
注意:如果在函数内部,没有声明直接赋值的变量也属于全局变量
2.局部变量
在局部作用域下的变量 或者说在函数内部的变量就是局部变量
-
局部变量只能在该函数内部使用
-
在函数内部 var 声明的变量是局部变量
-
函数的形参实际上就是局部变量
注意:函数的形参也可以看作是局部变量
注意:
全局变量只有浏览器关闭的时候才会销毁
3.全局变量和局部变量的区别
-
全局变量:在任何一个地方都可以使用,只有在浏览器关闭时才会被销毁,因此比较占内存
-
局部变量:只在函数内部使用,当其所在的代码块被执行时,会被初始化;当代码块运行结束后,就会被销毁,因此更节省内存空间
3.3 JavaScript中没有块级作用域
3.4 作用域链
-
只要是代码,就至少有一个作用域
-
写在函数内部的叫局部作用域
-
如果函数中还有函数,那么在这个作用域中就又可以诞生一个作用域
-
根据在内部函数可以访问外部函数变量的这种机制,用链式查找决定哪些数据能被内部函数访问,就称作作用域链(就近原则)
<script>
// 作用域链 : 内部函数访问外部函数的变量,采取的是链式查找的方式来决定取那个值 这种结构我们称为作用域链 就近原则
var num = 10;
function fn() { // 外部函数
var num = 20;
function fun() { // 内部函数
console.log(num);
}
fun();
}
fn();
</script>
⭐作用域链案例
function f1() {
var num = 123;
function f2() {
var num = 0;
console.log(num); // 站在目标出发,一层一层的往外查找
}
f2();
}
var num = 456;
f1();
//答案为123
var a = 1;
function fn1() {
var a = 2;
var b = '22';
fn2();
function fn2() {
var a = 3;
fn3();
function fn3() {
var a = 4;
console.log(a); //a的值 ?
console.log(b); //b的值 ?
}
}
}
fn1();
//a=4,b='22'
四、JavaScript预解析
4.1 预解析(面试常问)
JavaScript 代码是由浏览器中的 JavaScript 解析器来执行的。JavaScript 解析器在运行 JavaScript 代码的时候分为两步:预解析和代码执行。
1.预解析:
js引擎会把js里面所有的var 还有 function 提升到当前作用域的最前面
预解析分为:变量预解析(变量提升) 和 函数预解析(函数提升)
变量提升:就是把所有的变量声明提前到当前的作用域的最前面,不提升赋值操作
函数提升:就是把所有的函数声明提升到当前作用域的最前面, 不调用函数
2.代码执行:
按照代码书写的顺序从上到下执行
下面这一段代码很重要,注意理解里面的坑所在位置:
<script>
// 1问
console.log(num);
// 2问
console.log(num); // undefined 坑 1
var num = 10;
// 相当于执行了以下代码
// var num;
// console.log(num);
// num = 10;
// 3问
function fn() {
console.log(11);
}
fn();
// 4问
fun(); // 报错 坑2
var fun = function() {
console.log(22);
}
// 函数表达式 调用必须写在函数表达式的下面
// 相当于执行了以下代码
// var fun;
// fun();
// fun = function() {
// console.log(22);
// }
预解析案例
案例1
//案例1
var num = 10;
fun ();
function fun(){
console.log(num);
var num = 20;
}
//相当于执行了以下操作
var num;
function fun(){
var num;
console.log(num);
num = 20;
}
num = 10;
fun();
//结果是undefined
案例2
//案例2
var num = 10;
function fn() {
console.log(num);
var num = 20;
console.log(num);
}
fn();
//相当于执行了下面代码
var num;
function fn() {
var num;
console.log(num);
num = 20;
console.log(num);
}
num = 10;
fn();
// undefined 和 20
案例3
//案例3
var a = 18;
f1();
function f1() {
var b = 9;
console.log(a);
console.log(b);
var a = '123';
}
//相当于执行了下列代码
var a;
function f1() {
var b;
var a;
b = 9;
console.log(a);
console.log(b);
a = '123';
}
a = 18;
f1();
// unfined 和 9
案例4(面试常见混淆题)
//案例4
f1();
console.log(c);
console.log(b);
console.log(a);
function f1() {
var a = b = c = 9;
console.log(a);
console.log(b);
console.log(c);
}
//相当于执行了下列代码
function f1() {
var a;
//相当于 var a = 9; b = 9; c = 9; b和c直接赋值,没有var声明,当全局变量看
//集体声明 var a = 9, b = 9, c = 9; 注意上面和下面的逗号和分号
a = b = c = 9;
console.log(a);
console.log(b);
console.log(c);
}
f1();
console.log(c);
console.log(b);
console.log(a);
//结果是 9 9 9 9 9 报错
五、对象
5.1 对象
在 JavaScript 中,对象是一组无序的相关属性和方法的集合,所有的事物都是对象,例如字符串、数值、数组、函数等。
对象是由属性和方法组成的:
- 属性:事物的特征,在对象中用属性来表示(常用名词)
- 方法:事物的行为,在对象中用方法来表示(常用动词)
5.2 为什么需要对象
- 保存一个值时,可以用变量
- 保存多个值(一组值)时,可以用数组
- 保存一个人的完整信息 -----用对象
JS中的对象表达结构更清晰,更强大。
5.3 创建对象的三种方式
在 JavaScript 中,现阶段我们可以采用三种方式创建对象(object):
- 利用字面量创建对象
- 利用 new Object创建对象
- 利用构造函数创建对象
1.利用字面量创建对象
对象字面量:就是花括号{ }里面包含了这个具体事物(对象)的属性和方法。
{ }
里面采取键值对的形式表示
- 键:相当于属性名
- 值:相当于属性值,可以是任意类型的值(数字类型、字符串类型、布尔类型,函数类型等)
对象的调用
-
对象里面的属性调用 : 对象.属性名 ,这个小点 . 就理解为“ 的 ”
-
对象里面属性的另一种调用方式 : 对象[‘属性名’],注意方括号里面的属性必须加引号,我们后面会用
-
对象里面的方法调用:对象.方法名() ,注意这个方法名字后面一定加括号
<script>
// 1.利用对象字面量创建对象 {}
// var obj = {}; // 创建了一个空的对象
var obj = {
uname: '张三疯',
age: 18,
sex: '男',
sayHi: function() {
console.log('hi~');
}
}
// (1) 里面的属性或者方法我们采取键值对的形式 键 属性名 : 值 属性值
// (2) 多个属性或者方法中间用逗号隔开的
// (3) 方法冒号后面跟的是一个匿名函数
// 2. 使用对象
// (1). 调用对象的属性 我们采取 对象名.属性名 . 我们理解为 的
console.log(obj.uname);
// (2). 调用属性还有一种方法 对象名['属性名']
console.log(obj['age']);
// (3) 调用对象的方法 sayHi 对象名.方法名() 千万别忘记添加小括号
obj.sayHi();
</script>