js工具

1. 随机十六进制颜色

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 方法1:简单的十六进制随机颜色
function getRandomHexColor() {
return '#' + Math.floor(Math.random() * 16777215).toString(16).padStart(6, '0');
}

// 方法2:更完整的十六进制实现

function getRandomHexColor2() {
const letters = '0123456789ABCDEF';
let color = '#';
for (let i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}

2.this问题

箭头函数与普通函数在 this 的行为上有一些重要的区别。理解这些区别对于 JavaScript 编程尤为关键,尤其是在处理回调函数、事件处理器、和异步操作时。

1. 箭头函数的 this

箭头函数的 this 绑定是静态的,即箭头函数会继承外部上下文中的 this 值,而不会根据函数调用的方式来重新绑定 this

  • 静态绑定:箭头函数不会创建自己的 this,它会继承定义它的上下文中的 this。箭头函数的 this 取决于它在何时定义,而不是在何时调用。

  • **不会改变 this:箭头函数中的 this 是不可修改的,它会始终指向外部的 this**,即使在事件回调中或被传递给其他函数。

示例:箭头函数的 this

1
2
3
4
5
6
7
8
function MyObject() {
this.value = 10;
setTimeout(() => {
console.log(this.value); // this 指向 MyObject 实例
}, 1000);
}

const obj = new MyObject(); // 输出 10

在上面的例子中,setTimeout 内的箭头函数继承了外部 MyObjectthis,即使 setTimeout 是异步执行的,this 依然指向 MyObject 实例。

2. 普通函数的 this

普通函数(非箭头函数)中的 this 绑定是动态的,即它取决于函数调用的方式。

  • 调用时的上下文this 的值会在函数调用时决定。它可以通过不同的调用方式改变。例如,普通函数可以通过调用者(比如对象、类实例、函数调用方式等)来决定 this

示例:普通函数的 this

1
2
3
4
5
6
7
8
function MyObject() {
this.value = 10;
setTimeout(function() {
console.log(this.value); // this 指向 setTimeout 的执行环境,而不是 MyObject 实例
}, 1000);
}

const obj = new MyObject(); // 输出 undefined

在这个例子中,setTimeout 内的普通函数会将 this 绑定到 setTimeout 函数本身的执行上下文,而不是 MyObject 实例。所以 this.valueundefined,因为 setTimeout 函数并没有绑定到 obj 上。

3. 如何解决普通函数中的 this 问题

在普通函数中,如果我们希望 this 保持指向对象实例(例如 MyObject),可以通过以下几种方法来确保 this 的正确绑定:

3.1 使用 bind() 显式绑定 this

1
2
3
4
5
6
7
8
function MyObject() {
this.value = 10;
setTimeout(function() {
console.log(this.value); // 使用 bind 保证 this 指向 MyObject 实例
}.bind(this), 1000);
}

const obj = new MyObject(); // 输出 10

bind(this) 显式将 this 绑定到 MyObject 实例,使得 thissetTimeout 内指向 obj

3.2 使用箭头函数

1
2
3
4
5
6
7
8
function MyObject() {
this.value = 10;
setTimeout(() => {
console.log(this.value); // 箭头函数不会改变 this,指向 MyObject 实例
}, 1000);
}

const obj = new MyObject(); // 输出 10

在这个例子中,箭头函数自动绑定外部 this,确保 this 指向 MyObject 实例。


总结:箭头函数 vs 普通函数中的 this

特性 箭头函数 普通函数
this 绑定 静态绑定,继承外部上下文 动态绑定,根据调用方式决定
this 是否可改变 不可改变,始终指向创建时的上下文 可以改变,根据调用方式或显式绑定 (bind())
常用场景 需要 this 指向外部上下文(如回调函数、事件处理器) 需要 this 根据调用方式动态绑定(如对象方法、构造函数)

什么时候选择箭头函数 vs 普通函数?

  • 使用箭头函数

    • 当你需要 this 指向外部的上下文(例如,在事件回调、异步操作中)。
    • 当你希望避免 this 被动态改变,尤其是在方法内部使用 this 时。
  • 使用普通函数

    • 当你需要 this 根据调用方式动态绑定(例如,作为对象的方法,或使用构造函数时)。
    • 当你希望通过 bind()call()apply() 显式修改 this

3.class类的调用

在 JavaScript 中,class 关键字用于创建类,它是 ES6(ECMAScript 2015)引入的一项功能,提供了一种更简洁和面向对象的方式来定义构造函数和继承。class 语法让我们可以定义 对象的蓝图,并且在类中包含属性和方法。

下面是关于 JavaScript 中 class 的基础用法,包括如何定义、创建实例、继承等。

1. 创建类和实例化对象

基本语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}

// 方法
greet() {
console.log(`你好,我是 ${this.name},今年 ${this.age} 岁。`);
}
}

// 创建实例
const person1 = new Person('张三', 25);
person1.greet(); // 输出:你好,我是 张三,今年 25 岁。

解释:

  • **constructor()**:是一个特殊的方法,用于初始化类的实例。当你用 new 关键字创建类的实例时,constructor 会被自动调用。
  • this 指向类的实例,允许我们为实例设置属性。
  • 实例方法:类中定义的普通方法(如 greet)是实例方法,它们可以在实例化的对象上调用。

2. 类的继承

JavaScript 中的类支持继承,可以使用 extends 关键字来继承另一个类。

示例:继承父类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Animal {
constructor(name) {
this.name = name;
}

speak() {
console.log(`${this.name} makes a noise.`);
}
}

class Dog extends Animal {
constructor(name, breed) {
// 调用父类的构造函数
super(name);
this.breed = breed;
}

// 重写父类的方法
speak() {
console.log(`${this.name} barks.`);
}
}

const dog = new Dog('Buddy', 'Golden Retriever');
dog.speak(); // 输出:Buddy barks.

解释:

  • extends 关键字用于让一个类继承另一个类。
  • super() 用于调用父类的构造函数,必须在子类构造函数中调用。
  • 子类可以重写(覆盖)父类的方法,如示例中的 speak()

3. getter 和 setter

JavaScript 类还可以定义访问器方法,即 getter 和 setter,用于读取和设置对象的属性值。

示例:使用 getter 和 setter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}

// getter
get getAge() {
return this.age;
}

// setter
set setAge(age) {
if (age > 0) {
this.age = age;
}
}

greet() {
console.log(`你好,我是 ${this.name},今年 ${this.age} 岁。`);
}
}

const person = new Person('李四', 30);
console.log(person.getAge); // 输出 30

person.setAge = 35;
console.log(person.getAge); // 输出 35

解释:

  • getter:用来获取属性的值(getAge)。
  • setter:用来设置属性的值(setAge)。当给 setAge 赋值时,它会被自动调用。

4. 静态方法

静态方法是定义在类本身上的方法,而不是实例对象上。静态方法可以通过类名直接调用。

示例:使用静态方法

1
2
3
4
5
6
7
8
9
10
11
12
class MathUtil {
static add(x, y) {
return x + y;
}

static multiply(x, y) {
return x * y;
}
}

console.log(MathUtil.add(2, 3)); // 输出 5
console.log(MathUtil.multiply(2, 3)); // 输出 6

解释:

  • 静态方法使用 static 关键字定义,不能在类的实例上调用,只能通过类名调用。

5. 实例方法与静态方法的区别

  • 实例方法:通过类实例来调用,this 指向类的实例。
  • 静态方法:通过类本身来调用,this 指向类本身,而不是类的实例。

6. 类字段(类的属性)

在类中,你可以直接定义实例属性,也可以使用静态属性。

示例:定义实例属性和静态属性

1
2
3
4
5
6
7
8
9
10
11
12
13
class Person {
static species = 'Homo sapiens'; // 静态属性

constructor(name, age) {
this.name = name; // 实例属性
this.age = age; // 实例属性
}
}

const person1 = new Person('王五', 40);
console.log(person1.species); // undefined,因为这是静态属性
console.log(Person.species); // 输出 'Homo sapiens',可以通过类名访问

解释:

  • 实例属性:使用 thisconstructor 中定义,属于每个实例。
  • 静态属性:使用 static 关键字定义,属于类本身,可以通过类名访问。

7. 私有字段(在较新的 JavaScript 版本中)

在最新的 JavaScript 版本中,可以使用 # 来定义类的私有属性和方法。

示例:私有字段和方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Person {
#name; // 私有字段

constructor(name, age) {
this.#name = name; // 初始化私有字段
this.age = age;
}

#greet() { // 私有方法
console.log(`你好,我是 ${this.#name}`);
}

showGreeting() {
this.#greet(); // 通过公共方法调用私有方法
}
}

const person1 = new Person('赵六', 30);
person1.showGreeting(); // 输出:你好,我是 赵六

// 以下代码会报错,因为 #name 和 #greet 是私有的
// console.log(person1.#name);
// person1.#greet();

解释:

  • 使用 # 声明私有字段或方法,这些字段或方法只能在类的内部访问,外部无法访问。

总结:

JavaScript class 提供了一种更为现代化的方式来创建和管理对象,具有以下特性:

  • 构造函数(constructor)用于初始化对象。
  • 支持方法、静态方法、getter 和 setter。
  • 可以使用 extends 来继承类。
  • 支持私有字段和方法(在较新的 JavaScript 版本中)。
  • 支持实例方法和静态方法的区分。