数组的概念

问:之前学习的变量,只能存储一个值。如果我们想存储班级中所有学生的姓名,那么该如何储存呢?

答:可以使用数组(Array)。数组可以把一组相关的数据一起存放,并且提供方便的访问(获取)方式

问:什么是数组呢?

答:数组是指一组数据的集合,其中的每个数据被称作元素,再数组中可以存放任意类型的元素。数组是一种将一组数据存储在单个变量名下的优雅方式

// 普通变量一次只能存储一个值
var num = 10;
// 数组一次可以存储多个值
var arr = [1,2,3,4,5];

数组(Array)就是一组数据的集合,存储在单个变量下的优雅方式

数组的创建方式

JS 中创建数组有两种方式:

利用 new 创建数组

var 变量名 = new Array();

创建了一个空数组

利用数组字面量创建数组

使用数组字面量方式创建空的数组

var 变量名 = [];

也是创建了一个空数组

数组里面的数据一定要用逗号分隔:

var arr1 = [1, 2, 'pink老师', true];

数组里面的元素比如说:1, 2 我们称为数组元素

数组元素的类型

数组中可以存放任意类型的数据,例如字符串,数字,布尔值等

var arrStus = ['小白',12,true,28.9];

控制台会输出全部4个数组:

var arr1 = [1, 2, 'pink老师', true];
console.log(arr1);

但是,在开发里面,需要获取数组里面的元素,请看下一级

数组的索引

索引也称下标,用来访问数组元素的序号(数组下表从0开始)

var arr = ['小白','小黑','大黄','瑞奇'];

索引号:0 1 2 3 依次排列

数组可以通过索引来访问、设置、修改对应的数组元素,我们可以通过“数组名[索引]”的形式来获取数组中的元素

这里的访问就是获取得到的意思

获取数组元素:格式 数组名[索引号];

var arr = ['小白','小黑','大黄','瑞奇'];
console.log(arr[2]);

结果:大黄

如果是 console.log(arr1[3]); 的话结果是:瑞奇

但是如果这样写的话:

var arr = ['小白','小黑','大黄','瑞奇'];
console.log(arr[4]);

结果:undefind

一定要记住:索引号从 0 开始

案例:数组练习

遍历数组

遍历:就是把数组中的每个元素从头到尾都访问一次(类似我们每天早上学生的点名)

var arr = ['red', 'green', 'blue'];
for (var i = 0; i < 3; i++) {
    console.log(arr[i]);
}

因为数组索引号从 0 开始,所以 i 必须从 0 开始 i < 3 输出的时候 arr[i] i 计数器当索引号来用

遍历数组案例:

数组的长度

使用 "数组名.length" 可以访问数组元素的数量 (数组长度)

var arr = ['关羽', '张飞', '马超', '赵云', '黄忠', '刘备', '姜维'];
console.log(arr.length);

结果:7

也有更简单的写法:

var arr = ['关羽', '张飞', '马超', '赵云', '黄忠', '刘备', '姜维', '其他'];
for (var i = 0; i < arr.length; i++) {
console.log(arr[i]);
}

结果:8

因为 arr.length 是动态检测数组元素的个数

遍历数组案例:

案例:求数组中的最大值 (重点)

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);

案例:数组转换为分隔字符串

要求:将数组 ['red','green','blue','pink'] 转换为字符串,并且用 | 或其他符号分隔

输出:'red|green|blue|pink|'

案例分析:

var arr = ['red', 'green', 'blue', 'pink'];
var str = '';
var sep = '|';
for (var i = 0; i < arr.length; i++) {
    str += arr [i] + sep;
}
console.log(str);

实例:

通过修改 length 长度新增数组元素

实例:

var arr = ['red', 'green', 'blue'];
console.log(arr.length);
arr.length = 5;

但是第 3 4 个元素是 undefined

通过修改数组索引新增数组元素

实例:

var arr1 = ['red', 'green', 'blue'];
arr1[3] = 'pink';
console.log(arr1);

这是一种比较常用的方法,叫做 "追加数组元素"

可以替换原先数组里面的元素:

var arr2 = ['red', 'green', 'blue'];
arr2[0] = 'yellow';
console.log(arr2);

结果:['yellow', 'green', 'blue'];

注意:不要直接给数组名赋值,否则里面的数组元素都没有了

案例:数组新增元素

新建一个数组,里面存放10个整数 (1~10`)

案例分析

  1. 使用循环来追加数组
  2. 声明一个空数组 arr
  3. 循环中计数器 i 可以作为数组元素存入
  4. 由于数组的索引号是从0开始的,因此计数器从0开始更合适,存入的数组元素要 +1

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

实例:

案例:筛选数组

要求:将数组 [2, 0, 6, 1, 77, 0, 52, 0, 25, 7] 中大于等于10的元素选出来,放入新数组。

案例分析

  1. 声明一个新的数组用于存放新数据 newArr
  2. 遍历原来的旧数组,找出大于等于10的元素
  3. 一次追加给新数组 newArr

方法一:

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) {
        newArr[j] = arr[i];
        j++;
    }
}
console.log(newArr);

方法二:

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] >= 10) {
        newArr[newArr.length] = arr[i];
    }
}
console.log(newArr);

案例:

案例:

懂了吗?懂了的话你会说一声: "妙~~啊~~~"

其他案例:

函数的使用

函数在使用时分为两步:声明函数和调用函数。

声明函数

语法结构:

function 函数名() {
    //函数体
}

以下就是最简单的函数

function sayHi() {
    console.log('Hi~~');
}

function 是声明函数的关键字,注意全部小写

函数是做某件事情,函数名一般是动词 sayHi

函数不调用自己不执行

调用函数

调用函数名来执行函数体代码

function 函数名() {
    //函数体
}
函数名();

注意: 一定不要忘了加小括号

案例:利用函数计算1-100之间的累加和

1.声明函数

function getSum() {
    var sum = 0;
    for (var i = 1; i <= 100; i++) {
        sum += i;
    }
    console.log(sum);
}

2.调用函数

getSum();

案例:

形参和实参

在声明函数时,可以在函数名称后面的小括号中添加一些参数,这些参数被称为形参,而在调用函数时,同样也需要传递相应的参数,这些参数被称为实参

参数 说明
形参     形式上的参数 函数定义的时候 传递的参数 当前并不知道是什么
实参     实际上的参数 函数调用的时候传递的参数 实参时传递给形参的

我们可以利用函数的参数实现函数重复不同的代码

function 函数名(形参1, 形参2...) {
     
}

在声明函数的小括号里面是形参(形式上的参数)

参数名(实参,实参2...);

在函数调用的小括号里面是实参(实际的参数)

形参和实参的执行过程

function cook(aru) {
    console.log(aru);
}
cook('酸辣土豆丝');

结果:'酸辣土豆丝'

形参是接受实参的 aru = '酸辣土豆丝' 形参类似于一个变量

function cook(aru) {
    console.log(aru);
}
cook('酸辣土豆丝');
cook('大肘子');

结果:输出一个 '酸辣土豆丝' 再输出一个 '大肘子'

这就等于执行了两遍

提示:函数的参数可以有也可以没有,个数不限

两个案例:

利用函数求任意两个数的和

function getSum(num1, num2) {
    console.log(num1 + num2);
}
getSum(1, 3);

利用函数求任意两个数之间的和

function getSums(start, end) {
    var sum = 0;
    for (var i = start; i <= end; i++) {
        sum += i;
    }
    console.log(sum);
}
getSums(1, 100);

案例:

函数形参和实参个数不匹配问题

参数个数 说明
实参个数等于形参个数 输出正确结果
实参个数多于形参个数 只取到形参个数
实参个数小于形参个数 多的形参定义为 undefined 结果为 NaN

return 语句

有时候,我们会希望函数将值返回给调用者,此视通过使用 return 语句就可以实现

function 函数名() {
    return 需要返回的结果;
}
函数名();

我们函数只是实现某种功能,最终的结果需要返回给函数调用者函数名() 通过 return 实现的

只要函数遇到 return 就把后面的结果返回给函数的调用者 函数名() = return 后面的结果

代码验证:

function getResult() {
    return 666;
}
getResult();
console.log(getResult());

求两个数的和

function getSum(num1, num2) {
    return num1 + num2;
}
console.log(getSum(1, 2));

这样做才算一个完美意义上的写法

两个案例:

利用函数求任意两个数的最大值

function getMax(num1, num2) {
    if (num1 > num2) {
        return num1;
    } else {
        return num2;
    }
}
console.log(getMax(1, 3));

结果:3

也可以这样写:

function getMax(num1, num2) {
    return num1 > num2 ? num1 : num2;
}
console.log(getMax(1, 3));

利用函数求任意一个数组中的最大值

求数组 [5, 2, 99, 101, 67, 77] 中最大数值

function getArrMax(arr) {
    var max = arr[0];
    for(var i = 1; i <= arr.length; i++) {
        if (arr[i] > max) {
            max = arr[i];
        }
    }
    return max;
}
var re = getArrMax([5, 2, 99, 101, 67, 77]);
console.log(re);

在实际开发里面,我们经常用一个变量来接收函数的返回结果使用更简单

return 终止函数

return 语句之后的代码不被执行

function getSum(num, num2) {
    return num1 + num2;
    alert('我是不会被执行的哦!');
}
console.log(getSum(1, 2));

结果:3

return 后面的代码不会被执行

return 只能返回一个值

function fn(num1, num2) {
    return num1, num2;
}
console.log(fn(1, 2));

结果:2

它返回的结果是最后一个值

我们求任意两个数的加减乘数结果

function getResult(num1, num2) {
    return [num1 + num2, num1 - num2, num1 * num2, num1 / num2];
}
var re = getResult(1, 2);
console.log(re);

break, continue, return 的区别

arguments 的使用

当我们不确定多少个参数传递的适合,可以用 arguments 来获取。在 JavaScript 中 arguments 实际上它是当前函数的一个内置对象。所有函数都内置了一个 arguments 对象,arguments 对象中储存了传递的所有实参。

arguments 的使用

function fn() {
    console.log(arguments);
}
fn(1, 2, 3);

结果:[1, 2, 3];

arguments 可以存储数据,用户输入几个就存储几个

我们可以按照数组的方式遍历 arguments

function fn() {
    for (var i = 0; i < arguments.length; i++) {
        console.log(arguments[i]);
    }
}

当你也不知道用户需要储存多少个数值的时候,arguments 就有用了,它可以存储一个或多个数值

案例:利用函数求任意两个数的最大值

代码:

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));
console.log(getMax(1, 2, 3, 4, 5));
console.log(getMax(11, 2, 34, 444, 5, 100));

结果:3 5 444

这样就可以利用 arguments 求任意数值的最大值。实例:

案例:利用函数翻转数组

代码:

function reverse(arr) {
    var newArr = [];
    for (var i = arr.length - 1; i >= 0; i--) {
        newArr[newArr.length] = arr[i];
    }
    return newArr;
}
var arr1 = reverse([1, 3, 4, 6, 9]);
console.log(arr1);
var arr2 = reverse(['red', 'pink', 'blue']);
console.log(arr2);

实例:

案例:利用函数冒泡排序 sort 排序

代码:

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, 4, 2, 9]);
console.log(arr1);

实例:

案例:利用函数判断闰年

代码:

function isRunYear(year) {
    var flag = false;
    if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) {
        flag = true
    }
    return flag;
}
console.log(isRunYear(2000));

如果结果是 true 就是闰年

实例:

函数是可以相互调用的

fn1 和 fn2 互相调用

function fn1() {
    console.log(11);
    fn2();
}
fn1();

function fn2() {
    console.log(22);
}

案例:输出年份的2月份天数

利用函数相互调用组成代码

// 用户输入年份,输出当前年份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;
}

实例:

函数中的两种声明方式

利用函数关键字自定义函数(命名函数)

function fn() {

}
fn();

函数表达式(匿名函数)

var fun = function(aru) {
    console.log('我是函数表达式');
    console.log(aru);

}
fun('pink老师');

fun是变量名 不是函数名

函数表达式声明方式跟声明变量差不多,只不过变量里面存的是值 而 函数表达式里面存的是函数

函数表达式也可以进行传递参数

JavaScript 作用域

JavaScript 作用域就是代码名字在某个范围内起作用和效果,目的是为了提高程序的可靠性更重要的是减少命名冲突

全局作用域

全局作用域就是整个 script 标签,或者是一个单独的 JS 文件

比如说一个页面的一段 JS 代码,就是全局作用域

var num = 10;

局部作用域

局部作用域(也叫函数作用域)在函数内部就是局部作用域,这个代码的名字只在函数内部起效果和作用

局部作用域就是在函数里面的作用域:

function fn() {
    // 局部作用域
    var num = 20;
    console.log(num);
}
fn();

全局作用域和局部作用域不会互相影响

全局变量和局部变量

根据作用域的不同我们变量分为全局变量和局部变量

全局变量

num 就是一个全局变量

var num = 10;
console.log(num);

全局变量可以在函数中使用

var num = 10;
function fn() {
    console.log(num);
}
fn();

以上结果为:10

局部作用域

局部变量就是在局部作用域下的变量,后者在函数内部的变量就是局部变量

function fun() {
    var num1 = 10;
}
console.log(num1);

结果:Uncaught ReferenceError: num1 is not defind

事实证明,局部作用域是不能在全局作用域中使用的

注意:如果在函数内部没有声明直接赋值的变量也属于全局变量

function fun() {
    num2 = 20
}
console.log(num2);

结果:20

函数的形参也可以看作是局部变量

从执行效率来看全局变量和局部变量:

JavaScript 没有块级作用域

JavaScript 的作用域:全局作用域、局部作用域、现阶段我们 JS 没有块级作用域

我们 JS 也是在 es6 的时候新增的块级作用域

块级作用域 {} | if {} | for {}

使用花括号的写的变量都可以使用

if (3 < 5) {
    var num = 10;
}
console.log(num);

作用域链

作用域链:内部函数访问外部函数的变量,采取的是链式查找的方式来决定取那个值,这种结构我们称为作用域链,就近原则

var num = 10;
function fn() { // 外部函数
    var num = 20;
    function fun() { // 内部函数
        console.log(num);
    }
    fun();
}
fn();

结果是:20

这个内部函数会首先检查自己内部的函数,如果没有,返回上一级函数查找变量,如果还是没有,返回最上面查找变量,如果还没有,那就报错了

案例:作用域链案例

案例1:结果是几

function f1() {
    var num = 123;
    function f2() {
        console.log(num);
    }
    f2();
}
var num = 456;
f1();

结果是:123

案例2:结果是几

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);
            console.log(b);
        }
    }
}
fn1();

预解析

JavaScript 代码是由浏览器中的 JavaScript 解析器来执行的。JavaScript 解析器在运行 JavaScript 代码的时候分为两步:预解析和代码执行。

我们 JS 引擎分为两步:预解析、代码执行

预解析就是 JS 引擎把 JS 里面所有的 var 还有 function 提升到当前作用域的最前面,然后进行代码执行,按照代码书写的顺序从上往下执行

预解析发呢为变量预解析(变量提升)和函数预解析(函数提升)

(1) 变量提升就是把所有的变量声明提升到当前的作用域最前面,不提升赋值操作

console.log(num);
var num = 10;

结果:undefind

此代码就相当于执行了这个代码:

var num;
console.log(num);
num = 10;

num 被提升到前面去了

那么为什么这行代码会报错呢?

fun();
var fun = function() {
    console.log(22);
}

结果:报错

因为这行代码就相当于执行了以下代码:

var fun;
fun();
fun = function() {
    console.log(22);
}

因为没有那个函数所以报错了

正确写法:

fun = function() {
    console.log(22);
}
fun();

(2) 函数提升就是把所有函数声明提升到当前作用域的最前面,不调用函数

fn();
function fn() {
    console.log(11);
}

结果是:11

这就是函数提升

如果是直接赋值的形式:"var fun = function()" 就不支持提升

案例:结果是几?

案例1:

var num = 10;
fun();
function fun() {
    console.log(num);
    var num = 20;
}

结果是:20

相当于执行以下操作:

var num;
function fun() {
    var num;
    console.log(num);
    num = 20;
}
num = 10;
fun();

结果是:undefined

案例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:

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);
}

结果:9 9 9 9 9 报错