一、函数类型
1、函数的定义
简述:四种方式
①、函数声明
function sum(a, b) {
return a + b;
}
②、函数表达式
const sum = function(a, b) {
return a + b;
};
③、构造函数
(不常用,一般都用其他三种方式)
function Person(name, age) {
this.name = name;
this.age = age;
this.describe = function() {
return `${this.name} is ${this.age} years old.`;
};
}
const alice = new Person('Alice', 30);
console.log(alice.describe()); // Alice is 30 years old.
④、箭头函数
(ES6新增)
const sum = (a, b) => a + b;
2、函数的提升
简述:指函数声明会被提升到其作用域的顶部,而函数表达式则不会。
范例:
console.log(sum(2, 3)); // 输出5,因为函数声明被提升了
function sum(a, b) {
return a + b;
}
console.log(sum(2, 3)); // 报错,因为函数表达式没有被提升
const sum = function(a, b) {
return a + b;
};
3、函数的参数
- JS中的参数在内部是用一个伪数组(arguments )来表示的。
- JS中,所有函数的参数都是按值传递的。
- JS中,函数名本身就是变量,所以函数还可以当作值来用,也就是将函数像参数一样传递。
- 这些参数在函数内部作为局部变量使用。
function exampleFunction() {
// 使用arguments对象来访问所有传入的参数
console.log('Number of arguments:', arguments.length);
console.log('All arguments:', arguments);
// 参数是按值传递的,所以下面的修改不会影响到外部变量
arguments = 100; // 尝试修改传入的第一个参数的值
console.log('Modified first argument inside function:', arguments);
// 函数名作为变量,可以将函数作为参数传递或使用
function innerFunction(x) {
return x * 2;
}
// 调用内部函数,并打印结果
console.log('Result of innerFunction:', innerFunction(5));
// 将内部函数作为返回值,演示函数可以作为值使用
return innerFunction;
}
let num = 5;
// 调用exampleFunction,并传入num作为参数
let returnedFunction = exampleFunction(num);
// 打印原始num的值,验证按值传递
console.log('Original num outside function:', num); // 5,未被修改
// 调用返回的函数,并打印结果
console.log('Result of returned function:', returnedFunction(10)); // 20
4、函数的调用方式
简述:可以 作为函数、作为方法、作为构造函数以及通过call
和apply
方法进行调用。
function greet(name) {
console.log('Hello, ' + name + '!');
}
// 作为函数调用
greet('Alice');
// 作为方法调用
const person = {
name: 'Bob',
greet: function() {
console.log('Hello, ' + this.name + '!');
}
};
person.greet();
// 作为构造函数调用
function Person(name) {
this.name = name;
this.greet = function() {
console.log('Hello, ' + this.name + '!');
};
}
const bob = new Person('Bob');
bob.greet();
// 使用call和apply方法调用
greet.call({ name: 'Charlie' }, 'Charlie'); // 第一个参数是this的上下文
greet.apply({ name: 'Diana' }, ['Diana']); // 第二个参数是参数数组
5、函数的内部属性
①、arguments属性
简述:一个伪数组(大部分都是用于调用其长度,不具备正常数组的各种方法,无法通过数组方法改变自身)
arguments中的属性:
- length属性(用的多):arguments.length获取长度
- callee属性(用的比较少):arguments.callee引用自身函数
范例:
function factorial(n) {
if (n <= 1) {
return 1;
} else {
// 使用arguments.callee来引用当前函数,实现递归
return n * arguments.callee(n - 1);
}
}
console.log(factorial(5)); // 输出:120
②、this属性
简述:这是个指向性属性,this
的指向通常取决于函数的调用方式。(有点容易迷惑人,不过一旦理解,还是很简单的)
以下列举几种常见的出现场景,以供理解:
全局上下文中的this
介绍:在全局执行环境中(任何函数体外部),this
指向全局对象
console.log(this); // 在浏览器中输出Window对象
函数调用中的this
介绍:普通函数调用中,this
通常指向全局对象。
注意:在严格模式('use strict'
)下,this
的值为undefined
。
function showThis() {
console.log(this);
}
showThis(); // 非严格模式下输出Window对象,严格模式下输出undefined
方法调用中的this
介绍:当函数作为对象的方法被调用时,this
指向调用该方法的对象。
var obj = {
name: 'Object',
showName: function() {
console.log(this.name);
}
};
obj.showName(); // 输出'Object'
构造函数中的this
介绍:构造函数中,this
指向通过new
关键字创建的新对象。
function Person(name) {
this.name = name;
this.showName = function() {
console.log(this.name);
};
}
var person = new Person('John');
person.showName(); // 输出'John'
箭头函数中的this
介绍:箭头函数不绑定自己的this
,它会捕获其所在上下文的this
值作为自己的this
值。
var obj = {
name: 'Object',
showName: function() {
var innerFunc = () => {
console.log(this.name);
};
innerFunc();
}
};
obj.showName(); // 输出'Object'
通过call
、apply
和bind
方法显式设置this
介绍:可以使用call
、apply
和bind
方法来显式设置函数调用时的this
值。
var name = 'Global';
var obj = {
name: 'Object'
};
function showName() {
console.log(this.name);
}
showName(); // 输出'Global'
showName.call(obj); // 输出'Object',使用call方法设置this
showName.apply(obj); // 输出'Object',使用apply方法设置this
var boundShowName = showName.bind(obj);
boundShowName(); // 输出'Object',使用bind方法设置this
6、函数闭包
①、字面含义
简单理解:一个内部函数访问定义在外部函数中的变量。
②、常出现的格式
(在函数内返回另一个函数 )
function createCounter() {
let count = 0;
return function() {
count += 1;
return count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
③、常用于什么情况
- 数据封装和私有变量
- 创建模块
- 实现回调函数和高阶函数
- 模拟类的私有方法和属性
④、会导致的问题
- 内存泄漏:由于闭包可以维持外部函数的变量,因此可能导致这些变量不被垃圾回收机制回收。
- 性能问题:创建闭包可能会比创建普通函数占用更多内存,并且可能导致额外的性能开销。
⑤、怎么解决
避免不必要的闭包:只在需要时才使用闭包。
及时解除引用:对于不再需要的闭包,可以通过将变量设置为null
来解除引用,从而允许垃圾回收机制回收内存。
function createClosure() {
let counter = 0;
return function increment() {
counter++;
console.log(counter);
};
}
let increment = createClosure();
increment(); // 输出 1
increment(); // 输出 2
// 当不再需要闭包时,解除引用
increment = null;
使用弱持有(Weak Hold):在某些情况下,可以使用WeakMap或WeakSet来弱持有对象,这样当对象不再被其他方式引用时,可以被垃圾回收。
const weakMap = new WeakMap();
function createObject() {
let obj = {
name: "MyObject",
// 其他属性和方法...
};
// 使用WeakMap弱持有对象
weakMap.set(obj, "SomeValue");
return obj;
}
let obj = createObject();
console.log(weakMap.has(obj)); // 输出 true
// 当不再需要对象时,由于没有其他引用,它将被垃圾回收
// 同时,WeakMap中的条目也将自动被移除
obj = null;
⑥、作用域链在闭包中的副作用
先语:上述提到闭包会导致的两个问题,其实就是在作用域链上的,但除却那两点之外,最为核心的点是——意外访问外部变量——这个问题。
- 由于闭包可以访问其外部作用域中的变量,就可能导致在闭包内部意外地访问或修改了不应该被访问或修改的外部变量。(看懂下方代码你就能真正理解)
function createCounters() {
let count = 0; // 这个变量现在被所有返回的闭包共享
return function createCounter() {
return function increment() {
count++; // 所有闭包都访问并修改同一个外部变量count
console.log(count);
};
};
}
let createCounter = createCounters();
let counter1 = createCounter();
let counter2 = createCounter();
counter1(); // 输出 1
counter1(); // 输出 2
counter2(); // 输出 3,这里展示了意外访问:counter2访问并修改了counter1的外部变量
二、Date类型
先语:本来想分开写的,但Date类型属实没多少好说的,也就是乱七八糟的无用方法多点,在实际开发里面用到最多的也就是前端向后端传递时间数据时,更改一下时间格式。
概述
用于表示日期和时间。
创建Date对象
(为了方便我直接放代码,更直观一点)
// 使用当前日期和时间创建Date对象
let now = new Date();
console.log(now);
// 使用字符串创建Date对象
let specificDate = new Date('2023-04-01T12:00:00');
console.log(specificDate);
// 使用时间戳创建Date对象
// 时间戳是指自1970年1月1日00:00:00 UTC以来经过的毫秒数
let timestamp = 1672531200000; // 对应2023-01-01T00:00:00Z
let dateFromTimestamp = new Date(timestamp);
console.log(dateFromTimestamp);
// 使用年、月、日等参数创建Date对象
// 注意:月份是从0开始的,0代表1月,11代表12月
let detailedDate = new Date(2023, 3, 1, 12, 0, 0);
console.log(detailedDate);
使用注意事项
- 在处理不同时区和夏令时时要特别注意。
- 日期格式化需自行实现或使用库函数。
Date包含的方法
(方法是挺多的,实际有用的也就是个转换时间戳的方法,其他的看看就行,有场景需要使用的时候再找吧)
获取日期和时间部分
-
getFullYear()
:获取年份let date = new Date(); console.log(date.getFullYear()); // 输出当前年份
-
getMonth()
:获取月份,月份从0开始计数let date = new Date(); console.log(date.getMonth()); // 输出当前月份,0代表1月
-
getDate()
:获取日(月份中的某一天)let date = new Date(); console.log(date.getDate()); // 输出当前日
-
getHours()
:获取小时let date = new Date(); console.log(date.getHours()); // 输出当前小时
-
getMinutes()
:获取分钟let date = new Date(); console.log(date.getMinutes()); // 输出当前分钟
-
getSeconds()
:获取秒数let date = new Date(); console.log(date.getSeconds()); // 输出当前秒数
-
getMilliseconds()
:获取毫秒数let date = new Date(); console.log(date.getMilliseconds()); // 输出当前毫秒数
-
getDay()
:获取星期几,0代表星期日,6代表星期六let date = new Date(); console.log(date.getDay()); // 输出当前是星期几
-
getTime()
:获取时间戳,表示自1970年1月1日00:00:00 UTC以来的毫秒数let date = new Date(); console.log(date.getTime()); // 输出当前时间戳
设置日期和时间部分
-
setFullYear(year)
:设置年份let date = new Date(); date.setFullYear(2025); console.log(date); // 输出修改后的日期
-
setMonth(month)
:设置月份,月份从0开始计数let date = new Date(); date.setMonth(11); // 设置为12月 console.log(date);
-
setDate(date)
:设置日(月份中的某一天)let date = new Date(); date.setDate(15); console.log(date);
-
setHours(hours)
:设置小时let date = new Date(); date.setHours(14); console.log(date);
-
setMinutes(minutes)
:设置分钟let date = new Date(); date.setMinutes(30); console.log(date);
-
setSeconds(seconds)
:设置秒数let date = new Date(); date.setSeconds(0); console.log(date);
-
setMilliseconds(milliseconds)
:设置毫秒数let date = new Date(); date.setMilliseconds(123); console.log(date);
-
setTime(time)
:设置时间戳,表示自1970年1月1日00:00:00 UTC以来的毫秒数let date = new Date(); date.setTime(1672531200000); // 设置为2023-01-01T00:00:00Z console.log(date);
其他方法
-
toString()
:将日期转换为字符串let date = new Date(); console.log(date.toString());
-
toISOString()
:将日期转换为ISO格式的字符串let date = new Date(); console.log(date.toISOString());
-
valueOf()
:返回日期的原始值,即时间戳let date = new Date(); console.log(date.valueOf()); // 与 date.getTime() 相同
示范案例
这里模拟一个处理后端传递过来时间戳,和一个前端将时间格式转换成时间戳的示范
后端传递过来前端处理时间戳:
// 假设你有一个时间戳
let timestamp = 1705772800000; // 这个时间戳对应的是2024年5月18日(具体值可能因时区而异)
// 创建一个Date对象
let date = new Date(timestamp);
// 使用getFullYear、getMonth和getDate方法获取年、月、日
let year = date.getFullYear();
let month = date.getMonth() + 1; // getMonth()返回的月份是从0开始的,所以要加1
let day = date.getDate();
// 格式化日期为'YYYY-MM-DD'
let formattedDate = `${year}-${month.toString().padStart(2, '0')}-${day.toString().padStart(2, '0')}`;
// 输出格式化后的日期
console.log(formattedDate); // 输出'2024-05-18'
前端转换成时间戳格式传递给后端:
// 创建一个Date对象,表示2024年5月18日
let date = new Date('2024-05-18');
// 使用getTime方法获取时间戳
let timestamp = date.getTime();
// 输出时间戳
console.log(timestamp);