2. 第二部分:javascript高级函数和对象

2.1 函数进阶
2.1.1 高阶函数
JavaScript 中的高阶函数是指可以接受一个或多个函数作为参数,并/或者返回一个函数的函数。这种函数可以用来实现许多有用的编程模式,如函数式编程和回调函数。

以下是一些常见的 JavaScript 高阶函数示例:

  1. map()函数:接受一个函数作为参数,并将该函数应用于数组的每个元素,并返回一个新的经过函数处理的数组。
const numbers = [1, 2, 3, 4, 5];
const squaredNumbers = numbers.map(function (num) {
  return num * num;
});
console.log(squaredNumbers); // 输出:[1, 4, 9, 16, 25]
  1. filter()函数:接受一个函数作为参数,并使用该函数对数组进行筛选,返回一个包含满足条件的元素的新数组。
const numbers = [1, 2, 3, 4, 5];
const evenNumbers = numbers.filter(function (num) {
  return num % 2 === 0;
});
console.log(evenNumbers); // 输出:[2, 4]
  1. reduce()函数:接受一个函数作为参数,并使用该函数将数组的元素归约为单个值。
const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce(function (acc, num) {
  return acc + num;
}, 0);
console.log(sum); // 输出:15
  1. forEach()函数:接受一个函数作为参数,并对数组的每个元素执行该函数。
const numbers = [1, 2, 3, 4, 5];
numbers.forEach(function (num) {
  console.log(num);
});
// 输出:
// 1
// 2
// 3
// 4
// 5

这些只是高阶函数的一些示例,JavaScript 中还有许多其他的高阶函数,如sort()every()some()等,它们都能提供更多的功能和灵活性,以便在 JavaScript 中实现复杂的逻辑和算法。
2.1.2 闭包和作用域链
闭包(Closure)和作用域链(Scope Chain)是 JavaScript 中重要的概念,它们密切相关并经常一起讨论。

作用域(Scope)是指变量、函数和对象的可访问范围。在 JavaScript 中,每个函数都会创建一个新的作用域。作用域链是指在函数嵌套的情况下,内部函数可以访问外部函数的变量和函数,形成一个作用域的链条。

闭包是指在函数内部创建的函数,并且可以访问其外部函数的变量和作用域,即使外部函数已经执行完毕,闭包仍然保留对外部函数作用域的引用。闭包使得函数可以记住并访问在其词法作用域之外的变量。

下面是一个示例来说明闭包和作用域链的概念:

function outerFunction() {
  var outerVariable = "I am outside!";

  function innerFunction() {
    console.log(outerVariable); // 内部函数可以访问外部函数的变量
  }

  return innerFunction;
}

var myFunction = outerFunction();
myFunction(); // 输出:'I am outside!'

在上面的例子中,innerFunction是在outerFunction内部定义的,并且可以访问outerFunction的变量outerVariable。尽管outerFunction已经执行完毕,但通过返回innerFunction,我们创建了一个闭包,innerFunction仍然可以访问和引用外部作用域中的变量。

作用域链是由函数的定义位置和调用位置共同决定的。当访问变量时,JavaScript 引擎首先在当前作用域中查找,如果找不到,则继续沿着作用域链向上查找,直到找到变量或到达全局作用域。这种机制保证了内部函数可以访问外部函数的变量。

闭包和作用域链在 JavaScript 中具有广泛的应用,可以用于封装私有变量、实现模块化、处理异步操作等场景。但同时需要注意避免内存泄漏,因为闭包会持有外部作用域的引用,导致外部作用域中的变量无法被垃圾回收。
2.1.3 函数式编程
函数式编程是一种编程范式,它将计算视为函数应用的连续转换和组合,强调使用纯函数和避免可变状态和副作用。在 JavaScript 中,函数式编程可以通过以下几个方面来实现:

  1. 纯函数(Pure Functions):纯函数是指在相同的输入下,总是产生相同的输出,并且没有副作用的函数。它们不依赖于外部状态,也不修改外部状态。纯函数对于给定的输入只关心输出,不产生额外的影响。
function square(x) {
  return x * x;
}

const result = square(5); // 纯函数调用
  1. 不可变数据(Immutable Data):在函数式编程中,数据是不可变的,即一旦创建,就不能修改。当需要修改数据时,实际上是创建一个新的数据副本,并在副本上进行操作。这有助于避免意外的副作用和数据竞争。
const numbers = [1, 2, 3, 4, 5];
const doubledNumbers = numbers.map((num) => num * 2); // 创建新的数组副本
  1. 高阶函数(Higher-order Functions):高阶函数是指可以接受一个或多个函数作为参数,并/或者返回一个函数的函数。它们可以用来组合和转换函数,使代码更具表现力和可重用性。
function multiplyBy(factor) {
  return function (x) {
    return x * factor;
  };
}

const triple = multiplyBy(3); // 返回一个函数
const result = triple(5); // 调用返回的函数
  1. 函数组合(Function Composition):函数组合是将多个函数组合成一个新的函数的过程。可以使用函数组合运算符(如composepipe)将多个函数按特定顺序组合起来,形成一个新的函数。
const add = (x) => x + 1;
const multiply = (x) => x * 2;

const combined = compose(multiply, add); // 组合函数
const result = combined(3); // 调用组合后的函数
  1. 延迟执行(Lazy Evaluation):函数式编程鼓励延迟执行,即在需要时才计算结果。这可以通过使用惰性计算、生成器、迭代器等技术来实现,以避免不必要的计算。
function* generateNumbers() {
  let i = 0;
  while (true) {
    yield i++;
  }
}

const numbers = generateNumbers(); // 创建生成器
const result = numbers.next().value; // 惰性计算,只计算下一个值

函数式编程提供了一种声明式的编程风格,强调代码的可读性、可维护性和可测试性。它可以使代码更加模块化、可组合和

易于推理。在 JavaScript 中,许多库(如 Lodash、Ramda)提供了丰富的函数式编程工具和函数,可以更方便地编写函数式风格的代码。
2.2 对象和原型
2.2.1 对象创建和构造函数
在 JavaScript 中,对象可以通过多种方式进行创建。以下是两种常见的方式:字面量对象创建和构造函数创建。

  1. 字面量对象创建:
    使用字面量语法直接创建对象,可以在大括号 {} 内指定对象的属性和方法。
// 创建一个空对象
const obj = {};

// 创建带有属性和方法的对象
const person = {
  name: "John",
  age: 30,
  greet: function () {
    console.log("Hello!");
  },
};
  1. 构造函数创建:
    使用构造函数创建对象,构造函数是一个普通的函数,通过 new 关键字调用,并在函数内部使用 this 关键字引用新创建的对象。
// 创建一个构造函数
function Person(name, age) {
  this.name = name;
  this.age = age;
  this.greet = function () {
    console.log("Hello!");
  };
}

// 使用构造函数创建对象
const person = new Person("John", 30);

在上面的示例中,Person 是一个构造函数,通过 new 关键字创建了一个 Person 类型的对象,并传递了相应的参数。构造函数内部使用 this 关键字来引用新创建的对象,并设置对象的属性和方法。

使用构造函数创建的对象具有相同的属性和方法,因此它们共享相同的原型。可以通过在构造函数的原型对象上定义属性和方法,实现对象之间的共享和原型继承。

// 在构造函数原型上定义方法
Person.prototype.introduce = function () {
  console.log(`My name is ${this.name} and I'm ${this.age} years old.`);
};

// 调用构造函数原型上的方法
person.introduce(); // 输出:My name is John and I'm 30 years old.

通过使用构造函数和原型,可以实现更好的代码复用和扩展性,使对象拥有共享的属性和方法,同时每个对象也可以具有自己的属性和方法。
2.2.2 原型和原型链
在 JavaScript 中,每个对象都有一个原型(prototype),并且原型之间可以形成一个原型链(prototype chain)。原型链是 JavaScript 实现继承的机制之一。

  1. 原型(prototype):
  • 每个 JavaScript 对象都有一个原型,它可以是另一个对象或 null。
  • 对象的原型可以通过 __proto__ 属性访问(不推荐使用)或使用 Object.getPrototypeOf(obj) 方法获取。
  • 原型是对象共享属性和方法的存储位置,当对象访问属性或方法时,如果对象本身没有该属性或方法,它会沿着原型链向上查找。
  1. 原型链(prototype chain):
  • 原型链是由对象的原型构成的链条,每个对象都有一个指向其原型的链接。
  • 当对象访问属性或方法时,如果对象本身没有该属性或方法,它会沿着原型链向上查找,直到找到该属性或方法或到达原型链的末端(null)。
  • 原型链的顶端是 Object.prototype,它是大多数对象的最终原型。

以下是一个示例,说明原型和原型链的概念:

// 定义一个构造函数
function Person(name) {
  this.name = name;
}

// 在构造函数原型上定义方法
Person.prototype.greet = function () {
  console.log(`Hello, my name is ${this.name}.`);
};

// 创建对象实例
const person = new Person("John");

// 访问对象属性
console.log(person.name); // 输出:John

// 访问对象方法
person.greet(); // 输出:Hello, my name is John.

// 通过原型链访问对象方法
console.log(person.toString()); // 输出:[object Object]

在上面的例子中,person 对象实例通过原型链继承了 Object.prototype 上的 toString 方法。当对象本身没有 toString 方法时,它会沿着原型链向上查找并调用继承的 toString 方法。

原型链的概念使得 JavaScript 中的对象可以实现继承和共享属性与方法的特性。通过在构造函数的原型上定义属性和方法,可以使所有通过该构造函数创建的对象共享这些属性和方法。同时,原型链也提供了一种方式,让对象在没有某个属性或方法时,能够查找并使用它们。
2.2.3 ES6 中的类和继承
在 ECMAScript 6(ES6)中引入了类(class)和继承的语法糖,使得在 JavaScript 中实现面向对象编程更加直观和易用。

类(Class)是一种用于创建对象的模板或蓝图,它定义了对象的属性和方法。可以通过关键字 class 声明一个类,并使用 constructor 方法定义构造函数。

以下是一个示例展示如何使用类创建对象:

class Person {
  constructor(name) {
    this.name = name;
  }

  greet() {
    console.log(`Hello, my name is ${this.name}.`);
  }
}

const person = new Person("John");
person.greet(); // 输出:Hello, my name is John.

在上面的例子中,Person 类定义了一个构造函数 constructor 和一个方法 greet。通过 new 关键字可以创建 Person 类的对象实例,并调用该对象的方法。

继承(Inheritance)允许一个类继承另一个类的属性和方法,并且可以在此基础上进行扩展。在 ES6 中,可以使用关键字 extends 来实现类的继承。

以下是一个示例展示如何在 ES6 中实现类的继承:

class Animal {
  constructor(name) {
    this.name = name;
  }

  eat() {
    console.log(`${this.name} is eating.`);
  }
}

class Dog extends Animal {
  bark() {
    console.log(`${this.name} is barking.`);
  }
}

const dog = new Dog("Max");
dog.eat(); // 输出:Max is eating.
dog.bark(); // 输出:Max is barking.

在上面的例子中,Dog 类继承了 Animal 类,通过 extends 关键字指定了父类。子类可以访问父类的属性和方法,并可以在子类中定义自己的属性和方法。

类和继承提供了一种更直观、更清晰的方式来实现面向对象编程,使得代码更易读、易扩展和易维护。它们在 JavaScript 中成为了一种常见的编程范式,尤其在构建大型应用程序时非常有用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

有志青年(top)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值