JavaScript中的函数和数组是编程中非常重要的概念。函数可以被看作是一种代码块,它可以接受输入参数并返回一个结果。而数组则是一种有序的数据集合,可以包含任意类型的数据。本文将深入介绍二者的概念及用法。
一、JavaScript中的函数
函数是 JavaScript 中的基本组件之一。一个函数是 JavaScript 过程 ------ 一组执行任务或计算值的语句。要使用一个函数,你必须将其定义在你希望调用它的作用域内。 每个 JavaScript 函数实际上都是一个 Function 对象。运行
(function(){}).constructor === Function // true
便可以得到这个结论。
1. 函数的基本概念
① 如何定义函数
Ⅰ. 常规定义函数
javascript
// 常规函数
let egg = '鸡蛋';
// Params 入参
function 微波炉(egg) {
console.log("加热"); // 加热
// Return 返回值
return egg + '煮熟了';
}
// 调用函数,得到热鸡蛋
const result = 微波炉(egg);
console.log(result); // 鸡蛋煮熟了
Ⅱ. 定义箭头函数
javascript
// 箭头函数
const washMachine = (clothes) => {
console.log("洗衣服"); // 洗衣服
return '干净衣服';
}
const result2 = washMachine('脏衣服');
console.log(result2); // 干净衣服
// 箭头函数的简写
const washMachine2 = clothes => {
console.log("洗衣服"); // 洗衣服
return '干净衣服';
}
const result3 = washMachine2('脏衣服');
console.log(result3); // 干净衣服
② 函数的参数
参数是传递给函数的值,可以是基本类型(如数字、字符串、布尔值)或对象类型。参数列表在括号内,多个参数之间用逗号分隔。
javascript
function add(a, b) {
return a + b;
}
③ 函数的返回值
函数可以使用return
语句返回一个值,这个值会作为函数调用的结果。如果没有return
语句,函数将返回undefined
。
javascript
function multiply(a, b) {
return a * b;
}
④ 普通函数与箭头函数的区别
Ⅰ. 箭头函数受变量提升的影响
由于js的内存机制,function
的级别最高,而用箭头函数定义函数的时候,需要var
(let
const
定义的时候更不必说)关键词,而var所定义的变量不能得到变量提升,故箭头函数一定要定义于调用之前!(使用let
定义一键解决这个问题😁)
javascript
foo(); //123
function foo(){
console.log('123');
}
arrowFn(); //Uncaught TypeError: arrowFn is not a function
var arrowFn = () => {
console.log('456');
};
Ⅱ. 箭头函数不会创建自己的this
我们先来看看MDN上对箭头函数this
的解释。
箭头函数不会创建自己的
this
,所以它没有自己的this
,它只会从自己的作用域链的上一层继承this
。
箭头函数没有自己的this
,它会捕获自己在定义时 (注意,是定义时,不是调用时)所处的外层执行环境的this
,并继承这个this
值。所以,箭头函数中this
的指向在它被定义的时候就已经确定了,之后永远不会改变。
来看个例子:
javascript
var id = 'Global';
function fun1() {
// setTimeout中使用普通函数
setTimeout(function(){
console.log(this.id);
}, 2000);
}
function fun2() {
// setTimeout中使用箭头函数
setTimeout(() => {
console.log(this.id);
}, 2000)
}
fun1.call({id: 'Obj'}); // 'Global'
fun2.call({id: 'Obj'}); // 'Obj'
上面这个例子,函数fun1
中的setTimeout
中使用普通函数,2秒后函数执行时,这时函数其实是在全局作用域执行的,所以this
指向Window
对象,this.id
就指向全局变量id
,所以输出'Global'
。 但是函数fun2
中的setTimeout
中使用的是箭头函数,这个箭头函数的this
在定义时就确定了,它继承了它外层fun2
的执行环境中的this
,而fun2
调用时this
被call
方法改变到了对象{id: 'Obj'}
中,所以输出'Obj'
。
再来看另一个例子:
javascript
var id = 'GLOBAL';
var obj = {
id: 'OBJ',
a: function(){
console.log(this.id);
},
b: () => {
console.log(this.id);
}
};
obj.a(); // 'OBJ'
obj.b(); // 'GLOBAL'
上面这个例子,对象obj
的方法a
使用普通函数定义的,普通函数作为对象的方法调用时,this
指向它所属的对象 。所以,this.id
就是obj.id
,所以输出'OBJ'
。 但是方法b
是使用箭头函数定义的,箭头函数中的this
实际是继承的它定义时所处的全局执行环境中的this
,所以指向Window
对象,所以输出'GLOBAL'
。(这里要注意,定义对象的大括号{}
是无法形成一个单独的执行环境的,它依旧是处于全局执行环境中!! )
Ⅲ. 箭头函数不能作为构造函数使用
我们先了解一下构造函数的new都做了些什么?简单来说,分为四步: ① JS内部首先会先生成一个对象; ② 再把函数中的this指向该对象; ③ 然后执行构造函数中的语句; ④ 最终返回该对象实例。
但是!!因为箭头函数没有自己的this
,它的this
其实是继承了外层执行环境中的this
,且this
指向永远不会随在哪里调用、被谁调用而改变,所以箭头函数不能作为构造函数使用,或者说构造函数不能定义成箭头函数,否则用new
调用时会报错!
javascript
let Fun = (name, age) => {
this.name = name;
this.age = age;
};
// 报错
let p = new Fun('cao', 24);
2. 函数的用法
① 函数声明
使用关键字function
声明一个函数,后面跟函数名、参数列表和函数体。
javascript
function functionName(parameters) {
// 函数体
}
② 函数调用
通过函数名加括号的方式调用函数,并传入相应的参数。
javascript
var result = functionName(arguments);
3. 函数的作用域
在JavaScript中,作用域是指变量的可见性范围。函数内部定义的变量从函数外部是不可访问的(不可见的),而在函数内部可以访问,每个函数都会默认创建一个作用域。
javascript
function fn1(){
let a=1
}
console.log(a) // a不存在
function fn2(){
let a=1
}
fn2()
console.log(a) // a还是不存在
这也就引申出了全局变量和局部变量的概念,在JavaScript中,全局变量是在函数外部定义的变量,拥有全局作用域,即在JavaScript代码的任何地方都可以访问。而局部变量是定义在函数内部,只能在函数中使用的变量,作用范围是从函数开始到结尾,即在{}里 。
javascript
var globalVar = "我是全局变量";
function exampleFunction() {
var localVar = "我是局部变量";
console.log(globalVar); // 输出 "我是全局变量"
console.log(localVar); // 输出 "我是局部变量"
}
exampleFunction();
console.log(globalVar); // 输出 "我是全局变量"
console.log(localVar); // 报错 "undefined"
4. arguments
和 this
arguments 对象是所在函数的一个内置类数组对象,每个函数都有一个 arguments 属性,表示函数的实参集合,即执行函数时实际传入的参数的集合。arguments 不是数组而是一个对象,但它和数组很相似,所以通常称为类数组对象。arguments 对象不能显式的创建,它只有在函数开始时才可用。下面是一些常见情况下arguments
的用法:
arguments.length
返回传递给函数的实参个数。arguments[index]
返回传递给函数的第index个实参。
下面是一个简单的例子来说明这个概念:
javascript
function fn(){
console.log(arguments)
}
在JavaScript中,this
关键字是一个特殊的变量,它指向当前执行上下文中的对象。this
的值取决于函数是如何被调用的。下面是一些常见情况下this
的值:
- 在全局作用域中,
this
指向全局对象(在浏览器中是window
对象)。 - 在普通函数中,
this
指向调用该函数的对象。 - 在构造函数中,
this
指向新创建的实例对象。 - 在事件处理程序中,
this
指向触发事件的元素。 - 在使用call()、apply()或bind()方法调用函数时,
this
指向传入的第一个参数。
下面是一个简单的例子来说明这个概念:
javascript
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
console.log('Hello, my name is ' + this.name);
};
var person1 = new Person('Alice');
person1.sayHello(); // 输出 "Hello, my name is Alice"
二、JavaScript中的数组
在 JavaScript 中,数组不是基本类型,而是具有以下核心特征的 Array 对象:
- JavaScript 数组是可调整大小的,并且可以包含不同的数据类型。(当不需要这些特征时,可以使用类型化数组。)
- JavaScript 数组不是关联数组,因此,不能使用任意字符串作为索引访问数组元素,但必须使用非负整数(或它们各自的字符串形式)作为索引访问。
- JavaScript 数组的索引从 0 开始 :数组的第一个元素在索引 0 处,第二个在索引 1 处,以此类推,最后一个元素是数组的
length
属性减去 1 的值。- JavaScript 数组复制操作创建浅拷贝。(所有 JavaScript 对象的标准内置复制操作都会创建浅拷贝,而不是深拷贝)。
1. 数组的创建与转换
① 数组的创建
javascript
// 1.字面量创建
let arr1 = [1,2,3];
console.log(arr1); // [1, 2, 3]
// 2.构造函数创建
let arr2 = new Array(1,2,3);
console.log(arr2); // [1, 2, 3]
// 3.通过Array.of()方法创建
let arr3 = Array.of(1,2,3);
console.log(arr3); // [1, 2, 3]
② 字符串转为数组
javascript
let str = 'hello world';
// 1.split() 方法用于把一个字符串分割成字符串数组
let strArr = str.split(' ');
console.log(strArr); // ["hello", "world"]
// 2.Array.from() 方法默认把每一个字符都分割出来
let strArr2 = Array.from(str);
console.log(strArr2); // ["h", "e", "l", "l", "o", " ", "w", "o", "r", "l", "d"]
③ 对象转为数组
javascript
let obj = {
name: 'jack',
age: 18
}
// 1.Object.keys() 方法会返回一个由一个给定对象的自身可枚举属性组成的数组
let objArr = Object.keys(obj);
console.log(objArr); // ["name", "age"]
// 2.Object.values() 方法返回一个给定对象自身的所有可枚举属性值的数组
let objArr2 = Object.values(obj);
console.log(objArr2); // ["jack", 18]
// 3.Array.from() 方法也可以把对象转换成数组
let obj2 = {
0: 'a',
1: 'b',
2: 'c',
length: 3
}
let objArr3 = Array.from(obj2);
console.log(objArr3); // ["a", "b", "c"]
④ 数组的截取和拼接
javascript
let arr = [1,2,3,4,5,6,7,8,9];
// slice() 方法可从已有的数组中返回选定的元素,即截取数组
let arr2 = arr.slice(2,5);
console.log(arr2); // [3, 4, 5]
// concat() 方法用于连接两个或多个数组,即拼接数组
let shortArr1 = [1,2,3];
let shortArr2 = [4,5,6];
let longArr = shortArr1.concat(shortArr2);
console.log(longArr); // [1, 2, 3, 4, 5, 6]
// 三点运算符可以将数组展开,相当于把数组中的每一项都取出来,再放到一个新的数组中
let longArr2 = [...shortArr1,...shortArr2];
console.log(longArr2); // [1, 2, 3, 4, 5, 6]
2. 数组的增删改查
① 向数组中增添元素
Ⅰ. push()
可向数组的末尾添加一个或多个元素,并返回新的长度
javascript
let arr = [1,2,3];
let arrPush = arr.push(4,5,6);
console.log(arr); // [1, 2, 3, 4, 5, 6]
console.log(arrPush); // 6
// 但是不能添加数组,它会把数组当成一个元素添加在末尾
arr.push([7,8,9]);
console.log(arr); // [1, 2, 3, 4, 5, 6, Array(3)]
// 只能借助三点运算符来添加数组
arr.push(...[7,8,9]);
console.log(arr); // [1, 2, 3, 4, 5, 6, Array(3), 7, 8, 9]
Ⅱ. unshift()
可向数组的开头添加一个或多个元素,并返回新的长度
javascript
let arr2 = [1,2,3];
let arrUnshift = arr2.unshift(4,5,6);
console.log(arr2); // [4, 5, 6, 1, 2, 3]
console.log(arrUnshift); // 6
// 但是不能添加数组,它会把数组当成一个元素添加在开头
arr2.unshift([7,8,9]);
console.log(arr2); // [Array(3), 4, 5, 6, 1, 2, 3]
// 只能借助三点运算符来添加数组
arr2.unshift(...[7,8,9]);
console.log(arr2); // [7, 8, 9, Array(3), 4, 5, 6, 1, 2, 3]
Ⅲ. splice()
✨
可向数组的任意位置添加一个或多个元素,也兼具删除和修改的功能
javascript
// 语法:arrayObject.splice(开始下标,删除的个数,需要添加的元素)
let months = ['Jan', 'March', 'April', 'June'];
// 从索引1开始删除0个元素,然后插入'Feb'
months.splice(1, 0, 'Feb');
console.log(months); // ["Jan", "Feb", "March", "April", "June"]
// 从索引4开始删除0个元素,然后插入'May'
months.splice(4, 0, 'May');
console.log(months); // ["Jan", "Feb", "March", "April", "May", "June"]
// 将七月至十二月都加进来
months.splice(6, 0, 'July', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec');
console.log(months); // ["Jan", "Feb", "March", "April", "May", "June", "July", "Aug", "Sept", "Oct", "Nov", "Dec"]
② 从数组中删除元素
Ⅰ. pop()
能够删除并返回数组的最后一个元素
javascript
let arr3 = [1,2,3];
let arrPop = arr3.pop();
console.log(arr3); // [1, 2]
console.log(arrPop); // 3
Ⅱ. shift()
能够把数组的第一个元素从其中删除,并返回第一个元素的值
javascript
let arr4 = [1,2,3];
let arrShift = arr4.shift();
console.log(arr4); // [2, 3]
console.log(arrShift); // 1
Ⅲ. splice()
✨
可向数组的任意位置删除一个或多个元素,也兼具添加和修改的功能
javascript
let months2 = ['Jan', 'Feb', 'March', 'April', 'May', 'June', 'July', 'Aug'];
// 从索引0开始删除1个元素
months2.splice(0, 1);
console.log(months2); // ["Feb", "March", "April", "May", "June", "July", "Aug"]
③ 修改数组元素
javascript
// 直接通过索引修改数组中的元素
var arr = [1, 2, 3];
arr[0] = 4; // 将第一个元素修改为4
console.log(arr); // 输出[4, 2, 3]
// splice()方法修改数组中元素
let months3 = ['Jan', 'Feb'];
// 从索引0开始删除1个元素,然后插入'一月'
months3.splice(0, 1, '一月');
console.log(months3); // ["一月", "Feb"]
④ 查找数组元素
javascript
// 1.通过索引查找
let arr5 = [1,2,3];
console.log(arr5[0]); // 1
// 2.通过值查找索引
const index = arr5.indexOf(3);
console.log(index); // 2
// 3.通过find()方法查找
// find() 方法返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined
const arr6 = [
{id: 1, name: 'jack'},
{id: 2, name: 'rose'},
{id: 3, name: 'tom'}
]
const obj = arr6.find(function(item){
return item.id === 2;
})
console.log(obj); // {id: 2, name: "rose"}
⑤ 判断数组中是否包含某个值
javascript
let arr7 = [1,2,3];
// includes()方法
const isInArr = arr7.includes(3);
console.log(isInArr); // true
const isInArr2 = arr7.includes(6);
console.log(isInArr2); // false
// indexof()方法
const isInArr3 = arr7.indexOf(3);
const isInArr4 = arr7.indexOf(6);
if (isInArr3 >= 0){
console.log('包含'); // 包含
}
if (isInArr4 < 0){
console.log('不包含'); // 不包含
}
3. 遍历数组
① for
循环
javascript
let arr = [1,2,3,4,5];
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
② forEach
方法
javascript
// 对原数组没有影响,没有返回值
let arr1 = [1,2,3,4,5];
// forEach接受一个函数作为参数
arr1.forEach((item,index)=>{
console.log(`${item}-${index}`); // 1-0 2-1 3-2 4-3 5-4
item = item*2;
})
console.log(arr1); // [1, 2, 3, 4, 5]
③ map
映射
javascript
// 跟forEach一样,接受一个函数作为参数
// 不同的是,map会将执行结果组成一个新的数组返回
// map不会对原数组产生影响
let arr2 = [1,2,3,4,5];
// double函数的作用是将数组中的每个元素都乘以2
const double = (arr)=>{
return arr.map((item)=>{
return item*2;
})
}
console.log(double(arr2)); // [2, 4, 6, 8, 10]
④ some
方法
javascript
// 用于检测数组中是否有满足条件的元素
// 如果有,返回true,否则返回false
let arr3 = [1,2,3,4,5];
// some方法接受一个函数作为参数
const res=arr3.some((item)=>{
return item>3;
})
console.log(res); // true
const res2=arr3.some((item)=>{
return item>5;
})
console.log(res2); // false
⑤ every
方法
javascript
// 用于检测数组中是否所有元素都满足条件
// 如果是,返回true,否则返回false
let arr4 = [1,2,3,4,5];
// every方法接受一个函数作为参数
const res3=arr4.every((item)=>{
return item>3;
})
console.log(res3); // false
const res4=arr4.every((item)=>{
return item>0;
})
console.log(res4); // true
⑥ findIndex
方法
javascript
// 用于查找数组中是否有满足条件的元素 返回下标
// 如果有,返回满足条件的元素的下标 如果没有,返回-1
let arr5 = [1,2,3,{
name:'jack',
age:18
}];
// findIndex方法接受一个函数作为参数
const res5=arr5.findIndex((item)=>{
return item>3;
})
console.log(res5); // -1
const res6=arr5.findIndex((item)=>{
return name = 'jack';
})
console.log(res6); // 0
⑦ find
方法
javascript
// 用于查找数组中是否有满足条件的元素 返回元素
// 如果有,返回满足条件的元素 如果没有,返回undefined
let arr6 = [1,2,3,{
name:'jack',
age:18
}];
// find方法接受一个函数作为参数
const res7=arr6.find((item)=>{
return item>2;
})
console.log(res7); // 3
const res8=arr6.find((item)=>{
return name = 'jack';
})
console.log(res8); // 1
⑧ filter
方法
javascript
// 用于查找数组中是否有满足条件的元素 过滤
// 如果有,返回满足条件的元素组成的数组 如果没有,返回空数组
let arr7 = [1,2,3,4,5];
// filter方法接受一个函数作为参数
const res9=arr7.filter((item)=>{
return item>3;
})
console.log(res9); // [4, 5]
const res10=arr7.filter((item)=>{
return item>5;
})
console.log(res10); // []