js函数总结

一.普通函数

二.匿名函数

调用时只需在fuction外面包裹一层小括号或者整体包裹一层小括号

(fuction(){}())  //此时function关键字后紧跟的小括号为形参,花括号后紧跟的小括号为实参,整体用大括号包裹

这也叫匿名函数的自运行,也就是说这个函数创建出来会立即执行

(function(greeting, name) {    
    // 这个函数现在可以使用greeting和name这两个实参了    
    console.log(greeting + ', ' + name + '!');    
})('Hello', 'World'); // 现在这个函数被立即执行了



输出结果:

其中下面写法是错的:

(function myFunction() {
 console.log("Hello, World!");

 } )(); 

/*原因是在立即执行函数表达式(IIFE)中,你不能直接定义一个具名函数并尝试立即执行它,因为JavaScript的解析器会将这种结构视为函数声明而不是函数表达式。

在函数声明中,函数名(如myFunction)是可选的,但如果你提供了函数名,并且这个函数声明不是作为赋值语句的一部分或不是某个对象字面量的方法,那么它就会被视为一个普通的函数声明。然而,函数声明不能出现在表达式上下文中(比如圆括号内),这会导致语法错误。
*/

 注意点:

两个匿名函数之间要用分号隔开,否则会报错

在 JavaScript 中,如果两个立即执行函数表达式没有用分号分隔,它们可能会被解析成一个不合法的表达式。这主要是因为 JavaScript 的自动分号插入机制可能会错误地处理这些函数表达式。

好处:两种写法的区别在于之前的变量x会造成命名污染,而匿名函数在内部形成一个封闭的作用域,变量x不会对外部造成命名污染

缺点:两个匿名函数代码块之间很难进行通信,互相访问对方内部的变量

具名函数和匿名函数的区别:

1.具名函数的调用可以写到任何位置

2.匿名函数只能先声明后调用

原因:在 JavaScript 中,具名函数的调用可以写在函数定义之前(与java不同),只要你使用的是函数声明(function 关键字)。这是因为函数声明会被提升(hoisted),所以在函数实际定义之前你就可以调用它。

示例:

// 函数声明会被提升
greet(); // 输出: Hello, world!

function greet() {
    console.log("Hello, world!");
}

然而,如果你使用函数表达式(例如通过赋值给变量),则必须在函数表达式定义之后调用,因为函数表达式不会被提升。

示例:

// 函数表达式不会被提升
greet(); // 会导致 TypeError: greet is not a function

var greet = function() {
    console.log("Hello, world!");
};

在这个例子中,greet 变量的赋值在函数调用之前还没有发生,所以会导致错误。

小括号的作用:

在这里,它实际上把这个匿名函数的声明变成一个优先计算的表达式,而表达式一定是要有结果的,这个结果就是函数本身,当拿到函数本身时,自然可以调用它

注:在函数的前面加上一些运算符号,也可以达到函数自运行的效果,其中加号效率最高

三.定时器-间歇函数

1.开启定时器

setInterval(function(){

},间隔时间)


//作用:每隔一段时间调用这个函数
//间歇时间单位是毫秒

可以看到setInterval在执行过程中会调用一个匿名函数,

当你使用 setInterval 函数时,如果你想要定时调用一个具名函数,并且这个函数需要带有实参,你可以使用一个闭包或箭头函数来实现。这是因为 setInterval 只接受一个函数作为参数,而不能直接传递带有参数的具名函数。

下面是当函数有名字时如何调用 

2.具名函数的调用

(1)具名函数没有参数
function fn() {
    console.log('Function called');
}


setInterval(fn,1000)

/* 注意以下:
setInterval(fn, 1000)
这种用法是正确的。fn 是一个函数引用,不带括号。
当 setInterval 每隔 1000 毫秒调用它时,fn 被当作函数调用。
它实际上是传递了函数的引用给 setInterval,定时器会每隔指定的时间间隔执行这个函数。

setInterval(fn(), 1000)
这种用法是不正确的,
因为它实际上会立即调用 fn 并将其返回值传递给 setInterval。
如果 fn 没有返回有效的函数(通常 fn() 返回 undefined),
setInterval 将尝试调用 undefined,这会导致错误。  */

小科普:上面例子另一种正确写法是setInterval('fn()',1000) 

虽然 setInterval('fn()',1000)  是有效的,推荐使用 setInterval(fn, 1000) 作为更安全、更高效的方式来定时调用函数。这种写法了解一下即可 

(2)具名函数有参数
function myFunction(arg) {
    console.log('Function called with:', arg);
}


// (1)使用箭头函数
const intervalId = setInterval(() => {
    myFunction('Hello');
}, 1000);

// (2)使用匿名函数
const intervalId = setInterval(function() {
    myFunction('Hello');
}, 1000);


//setInterval函数返回的是一个数字,即定时器的ID,在一个程序中不同的定时器返回不同且唯一的ID值

/*清除定时器
如果你需要停止定时调用,可以使用 clearInterval,并传入 setInterval 返回的定时器 ID。例如:

clearInterval(intervalId);  */

在这两种情况下,setInterval 每隔 1000 毫秒(1秒)就会调用 myFunction,并传递 'Hello' 作为参数。 

四.延时函数

setTimeout返回值也是一个数字

小知识:

在JavaScript中,使用window.setTimeout时,并不总是必须要指定window。由于window是全局对象,在浏览器环境中,所有全局变量和函数实际上都是window对象的属性。因此,在全局作用域下,你可以直接调用setTimeout而不需要前缀window

然而,在一些特定情况下,明确指定window可能是有益的或必要的,例如:

避免命名冲突:如果你的代码(或你使用的库)中有一个与全局函数同名的局部变量或函数,使用window.setTimeout可以确保你调用的是全局的setTimeout函数,而不是被局部变量或函数覆盖的版本。

// 在全局作用域中,BOM的setTimeout是可用的  
  
// 定义一个与BOM setTimeout同名的函数(注意:这通常不是一个好主意)  

function setTimeout(message, delay) {  
    // 这里不会调用BOM的setTimeout,而是定义了一个新的函数体  
    console.log("Custom setTimeout called with message:", message);  
    // 注意:这里没有实现延迟执行的功能,只是为了演示  
}  
  
// 在这个点上,如果你尝试调用setTimeout而不通过window对象,它将调用你定义的函数 
 
setTimeout("This will not be delayed", 1000); // 调用了你定义的setTimeout  
  
// 但是,你可以通过window对象来访问BOM的setTimeout  
window.setTimeout(() => {  
    console.log("This is the real setTimeout from BOM, with delay");  
}, 2000);  

延时函数和间歇函数的区别:

五.回调函数

1.定义

回调函数(Callback Function)是一种特殊的函数,它作为参数传递给另一个函数(通常是异步函数或需要完成某种工作后再执行特定操作的函数),并在某个时间点(如异步操作完成时)被这个父函数(或称为调用者函数)调用。

2.与普通函数的区别 

回调函数与普通函数的主要区别在于它们的使用方式和目的:

  1. 使用方式
    • 普通函数:你可以直接调用一个普通函数,通过传递参数给它来获取结果或执行某些操作。普通函数的调用是同步的,即代码执行会等待函数执行完成后再继续执行后续的代码。
    • 回调函数:回调函数不是直接调用的,而是作为参数传递给另一个函数。这个父函数会在某个特定时刻(如异步操作完成)调用回调函数。回调函数的调用通常是异步的,即父函数不会等待回调函数执行完成就继续执行后续的代码。
  2. 目的
    • 普通函数:普通函数的主要目的是执行一段代码来完成某个具体的任务,并可能返回结果给调用者。
    • 回调函数:回调函数的主要目的是在某个特定事件或条件满足时被执行,以完成一些后续的操作或处理。它们常用于异步编程中,以便在异步操作(如网络请求、文件读写、定时器事件等)完成后执行某些操作。
  3. 执行时机
    • 普通函数:执行时机是确定的,即在你调用它的那一刻开始执行。
    • 回调函数:执行时机是不确定的,它取决于父函数的逻辑和异步操作完成的时间。

3.案例结合

在JavaScript中,回调函数是一种常用的编程模式,

特别是在处理异步操作、事件处理、定时器等场景时。



(1). 定时器
setTimeout和setInterval是JavaScript中用于设置定时器的函数,它们都接受一个回调函数作为参数。
setTimeout:在指定的时间后执行一次回调函数。
setInterval:按照指定的时间间隔重复执行回调函数。
(2). 数组方法
JavaScript的数组对象提供了多个接受回调函数作为参数的方法,用于遍历、映射、过滤等操作。


forEach:遍历数组,对数组的每个元素执行一次提供的函数。


map:创建一个新数组,其结果是该数组中的每个元素是调用一次提供的函数后的返回值。即返回一个新数组


filter:创建一个新数组,其包含通过所提供函数实现的测试的所有元素。


reduce:对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值。


(3). 事件处理
在Web开发中,事件处理是回调函数的一个重要应用场景。当用户与页面交互(如点击按钮、输入文本等)时,可以注册回调函数来处理这些事件。
addEventListener:为指定元素添加事件监听器,当事件发生时执行回调函数。
(4). 自定义回调函数
除了上述内置函数和API外,开发者还可以在自己的函数中定义和使用回调函数,以实现更复杂的逻辑和更灵活的代码结构。

六.闭包函数

1.定义

闭包指的是满足以下几个条件的函数:

  1. 必须有一个内嵌函数。
  2. 内嵌函数必须引用外部函数中的变量。
  3. 外部函数的返回值必须是内嵌函数。

具体来说,闭包包含两个主要部分:

  1. 内部函数:这个函数定义在另一个函数(外部函数)的内部。内部函数可以访问外部函数的局部变量,这是普通函数做不到的。

  2. 外部函数的局部变量:即使外部函数已经执行完毕,它的局部变量也不会被销毁,因为内部函数还在使用它们。这些局部变量和内部函数一起,形成了一个闭包。

 2.外部作用域的含义

外部作用域,在编程中,指的是一个变量或函数定义时所处的、比当前作用域更广泛的作用域。简单来说,就是当前作用域能够“看到”或“访问”到的外部层级的变量或函数的作用范围。

假设有两个函数outerFunctioninnerFunction,其中innerFunctionouterFunction内部定义的。在innerFunction中,如果尝试访问一个变量,而这个变量在innerFunction内部没有定义,但在outerFunction中定义了,那么这个变量就位于innerFunction的外部作用域中,即outerFunction的作用域。

function outerFunction() {  
    let outerVariable = 'I am outer';  
  
    function innerFunction() {  
        console.log(outerVariable); // 访问外部作用域中的变量  
    }  
  
    innerFunction(); // 输出: I am outer  
}  
  
outerFunction();
function outer_function() {  
    let outer_variable = "外部变量"; // 定义在外部函数中的变量,属于外部作用域  
  
    function inner_function() {  
        // 内部作用域中的变量  
        let inner_variable = "内部变量";  
        // 内部函数处理一些逻辑,并返回结果  
        let result = `内部处理结果: ${inner_variable}`; 
        return result; // 返回处理结果  
    }  
  
    // 调用内部函数并获取其返回值  
    let inner_result = inner_function();  
    // 外部作用域可以访问内部函数的返回值,但不能直接访问内部变量  
    console.log(inner_result); // 输出: 内部处理结果: 内部变量  
  
    // 注意:外部作用域不能直接访问 inner_variable  
    // 尝试访问会导致 ReferenceError: inner_variable is not defined  
    // console.log(inner_variable); // 这是错误的  
}  
  
// 调用外部函数  
outer_function();  
  
// 外部作用域也不能直接访问内部函数本身,除非通过某种机制(如闭包)将其传递到外部作用域  
// 尝试直接调用 inner_function 会导致 ReferenceError: inner_function is not defined  
// inner_function(); // 这是错误的

3.闭包的基本格式

4.闭包的作用

1.封闭数据,提供操作;在局部作用域内定义变量,避免变量污染全局作用域。(即减少全局变量的定义)

2.外部可以访问函数内部的变量且不能随意篡改函数内部变量值;因为返回的函数对象保留对外层的引用而又无法清除,所以可以持久局部变量(外层函数)

5.闭包的应用:实现数据的私有

比如,我们要做个统计函数调用次数,函数调用一次,就++

基本写法:

let count=0
function fn(){
    count++
    console.log(`函数被调用${count}次`)
}

fn()  //1
fn()  //2

//但是这个count是个全局变量,很容易被修改

闭包写法:

function fn(){
            let count=0
            function fun(){
                count++
            console.log(`函数被调用${count}次`)
                
            }
            return fun
        }
        const result1 =fn()
        const result2 =fn()
        result1()  //函数被调用1次
        result1()  //函数被调用2次
        result2()  //函数被调用1次

/*
可以看到const一个变量把fn()给这个变量,
它再调用其实只是创建了一个实例,并不能改变fn函数内部的变量值(count),
只是可以访问这个变量并拿到这个变量值
*/

可以看到,闭包就是能让某个函数(fun)和某些变量(count)联系起来,只能这个函数使用 (fun),但又不是这个函数的局部变量,因为局部变量每次调用会初始化,这些变量不会

 若改成以下:

function fn(){
            let count=0
            count++
            return count
        }
        const result1 =fn()
        const result2 =fn()
       console.log(result1);  //1
       console.log(result1);  //1
       console.log(result2);  //1

每次调用count都会初始化

这时闭包函数的好处体现在:

1.不会修改局部变量count,每次调用的都是一个fun函数实例,可以对每个实例进行特定的操作

就是通过一个方法给外部调用函数里面的变量

2. 调用的fun函数实例因为变量不是fun函数内部定义的局部变量,所以每个实例重复调用不会初始化

   function fn(){
            let count=0
            function fun(){
                count++
            console.log(`函数被调用${count}次`);
                
            }
            return fun
        }
       
        const result1 =fn()
//每次调用 fn() 都会创建一个新的 count 变量(以及一个新的 fun 函数)
       fn()()  //函数被调用1次
       fn()()  //函数被调用1次


//result1 和 result2 实际上是两个不同的 fun 函数实例,每个都封装了它们自己的 count 变量。
       result1()  //函数被调用1次
       result1()  //函数被调用2次

七.构造函数

1.定义

构造函数的定义与普通函数类似,但它通常用于初始化新创建的对象。构造函数的命名习惯上是以大写字母开头,但这并不是强制性的,只是一种约定俗成的做法,有助于区分构造函数和普通函数。

2.为什么要使用构造函数

举个例子,我们要录入一个班中每一位同学的个人信息,那么我们可以创建一些对象,比如:

var p1 = { name: 'zs', age: 16, gender: '男', hobby: 'basketball' };
var p2 = { name: 'ls', age: 16, gender: '女', hobby: 'dancing' };
var p3 = { name: 'ww', age: 16, gender: '女', hobby: 'singing' };
// ...

像上面这样,我们可以把每一位同学的信息当做一个对象来处理。但是,我们会发现,我们重复地写了很多无意义的代码。比如 name、age、gender、hobby 。如果这个班上有60个学生,我们得重复写60遍。

这个时候,构造函数的优势就体现出来了。我们发现,虽然每位同学都有 name、gender、hobby这些属性, 但它们的属性值都是不同的,那我们就把这些属性当做构造函数的参数传递进去。此时,我们就可以创建以下的函数:

function Person(name, gender, hobby) {
    this.name = name;
    this.gender = gender;
    this.hobby = hobby;
    this.age = age;
}

当创建上面的函数以后, 我们就可以通过 new 关键字调用,也就是通过构造函数来创建对象了。

var p1 = new Person('zs', '男', 'basketball');
var p2 = new Person('ls', '女', 'dancing');
var p3 = new Person('ww', '女', 'singing');
// ...

3.小知识

function Person(hobby) {
 this.hobby = hobby;
}

当一个函数创建好以后,我们并不知道它是不是构造函数,即使像上面的例子一样,函数名为大写,我们也不能确定。只有当一个函数以 new 关键字来调用的时候,我们才能说它是一个构造函数。就像下面这样: 

var zs = new Person("running");

此时,构造函数会有以下几个执行过程:

(1) 当以 new 关键字调用时,会创建一个新的内存空间,标记为 Person的实例。

(2)函数体内部的 this 指向该内存

因此每当创建一个实例,都会创建一个新的内存空间

3.构造函数的返回值

构造函数执行过程的最后一步是默认返回 this 。言外之意,构造函数的返回值还有其它情况。

(1) 没有手动添加返回值,默认返回 this

function Person1() {
 this.name = 'zs';
}

var p1 = new Person1();

(2) 手动添加一个基本数据类型的返回值,最终还是返回 this

function Person2() {
 this.age = 18;
 return 20;
}

var p2 = new Person2();
console.log(p2.age);   // 18
p2: {
 age: 18
}

如果上面是一个普通函数的调用,那么返回值就是20

 (3) 手动添加一个对象的返回值,最终返回该对象

function Person4() {
  this.gender = '男';
  return { gender: '女性' };
}

var p4 = new Person4();
console.log(p4.gender);  // '女性'
  • Person4函数首先通过this.gender = '男';为新创建的对象设置了一个gender属性,其值为'男'
  • 然后,函数通过return { gender: '女性' };显式地返回了一个新的对象,这个对象有一个gender属性,其值为'女性'
  • 由于Person4函数显式地返回了一个对象,因此JavaScript引擎不会返回原本通过new关键字创建的那个新对象(即this所指向的对象),而是返回Person4函数内部通过return语句返回的那个新对象。
  • 因此,当你通过var p4 = new Person4();创建Person4的实例时,p4实际上引用的是Person4函数内部返回的那个对象,而不是原本通过new关键字创建的那个只设置了gender'男'的对象。

 总结

  • 如果构造函数返回一个对象,则这个对象会替代函数原本要返回的新创建的对象(即this所指向的对象)。
  • 如果构造函数没有显式返回一个对象(包括返回undefinednull),则默认返回this所指向的新创建的对象。

 4.用new和不用new调用函数,有什么区别?

(1)使用new操作符调用函数

function Person(name){
  this.name = name;
  this.say = function(){
    return "I am " + this.name;
  }
}

var person1 = new Person('nicole');
person1.say(); // "I am nicole"

用new调用构造函数,最大特点为,this对象指向构造函数生成的对象,所以,person1.say()会返回字符串: “I am nicole”。

(2)直接调用函数

如果直接调用函数,那么,this对象指向window,并且,不会默认返回任何对象(除非显性声明返回值)。

还是拿Person函数为例,直接调用Person函数:

var person1 = Person('nicole');
person1; // undefined
window.name; // nicole

八.箭头函数

1.基本写法

const fn= () => {
  console.log('我是箭头函数')
}

fn()

2.箭头函数的一些用法

1. 省略包含参数的小括号

如果只有一个参数,那也可以不用括号。只有没有参数,或者多个参数的情况下,才需要使用括号:

// 有效
let sum = (x) => {
	return x;
};
// 有效
let sum1 = x => {
	return x;
};
// 没有参数需要括号
let sum2 = () => {
	return 1;
};
// 有多个参数需要括号
let sum3 = (x, y) => {
	return x + y;
};
2. 省略包含函数体的大括号

如果函数体只有一行代码,可以写到一行上,并且无需写return直接返回值

// 有效
let sum = (x, y) => {
	return x + y;
};
// 有效
let sum1 = (x, y) => x + y; // 相当于 return x + y;
// 无效的写法
let sum2 = (x, y) => return x + y;
3.直接返回一个对象
const fn = (uname) => ({uname: uname })

//因为对象的大括号和函数的大括号冲突了,所以用小括号包起来以区分开

console.log(fn('刘德华'))

输出结果: 

{uname: '刘德华'}

3.箭头函数参数

1.普通函数有arguments动态参数

注:

  • arguments对象包含了传递给函数的每个参数,第一个参数在arguments[0]中,第二个在arguments[1]中,依此类推。
  • arguments是自动可用的,无需特别声明。它仅在函数体内有效。
  • arguments不是一个真正的数组,而是一个类数组对象,因此它没有数组的所有方法(如pushpop等)
function sum() {  
  let total = 0;  
  for (let i = 0; i < arguments.length; i++) {  
    total += arguments[i];  
  }  
  return total;  
}  
  
console.log(sum(1, 2, 3)); // 输出: 6  
console.log(sum(10, 20)); // 输出: 30  
console.log(sum(1, 100, 1000)); // 输出: 1101

 在这个例子中,sum函数没有定义任何参数,但是它可以接收任意数量的参数。函数内部,我们通过遍历arguments对象来计算所有参数的总和。

2.箭头函数没有arguments动态参数,但是有剩余参数...args

1.基本概念

剩余参数通过三个连续的点号(...)后跟一个标识符来表示。这个标识符在函数内部会作为一个数组,包含所有传递给函数的除已明确声明的参数之外的额外参数。这使得函数能够灵活地处理不确定数量的参数,而无需提前声明具体的参数个数。

function functionName(param1, param2, ...restParams) {  
    // 函数体内的代码  
    // 使用 restParams 数组进行操作  
}

其中,param1param2 是函数的位置参数,它们接收函数调用时传递的前几个值。...restParams 是剩余参数,它收集除了位置参数之外的所有参数,并将它们作为一个数组存储在 restParams 中。

2.特点 

  1. 数组形式:剩余参数在函数内部是以数组的形式存在的,因此可以使用数组的所有方法(如 pushmapreduce 等)对其进行操作。
  2. 灵活性:剩余参数使得函数能够处理任意数量的参数,提高了函数的灵活性和复用性。
  3. 位置限制:在函数定义中,剩余参数必须是最后一个参数。如果在其后面还有其他参数,JavaScript引擎会抛出语法错误。
  4. arguments的区别:虽然arguments对象也包含了传递给函数的所有参数,但它不是一个真正的数组,而是一个类数组对象。而剩余参数是一个真正的数组,可以直接使用数组的方法。

举例: 

const getSum = (...arr) => {
    let sum = 0
    for(let i = 0; i < arr.length; i++){
        sum += arr[i]
    } 
    return sum       
}

const result1=getSum(2,3) 
console.log(result1)    //5

const result2=getSum(2,3,4)  
console.log(result2)     //9

4箭头函数this指向

在箭头函数出现之前,每一个新函数根据它是被如何调用的来定义这个函数的this值。

箭头函数不会创建自己的this,它只会从自己的作用域链的上一层沿用this

let num = 11;
const obj1 = {
	num: 22,
	fn1: function() {
		let num = 33;
		const obj2 = {
			num: 44,
			fn2: () => {
				console.log(this.num);
			}
		}
		obj2.fn2();
	}
}
obj1.fn1(); // 22

fn2中得到的结果为:22

原因箭头函数没有this,箭头函数的this是继承父执行上下文里面的this ,这里箭头函数的执行上下文是函数fn1(),所以它就继承了fn1()的this,obj1调用的fn1,所以fn1的this指向obj1, 所以obj1.num 为 22。

如果fn1也是个箭头函数呢?

let num = 11; // 
const obj1 = {
	num: 22,
	fn1: () => {
		let num = 33;
		const obj2 = {
			num: 44,
			fn2: () => {
				console.log(this.num);
			}
		}
		obj2.fn2();
	}
}
obj1.fn1();  //undefined

上述结果为undefined,因为fn1也是一个箭头函数,所以它就只能继续向上找也就是window了。

实际上,这里发生的是,当 obj2.fn2 试图访问 this.num 时,由于 fn2 是一个箭头函数,它继承了外层(即 obj1.fn1)的 this 上下文,但 obj1.fn1 作为一个箭头函数又继承了全局的 this 上下文。

那为什么是undefined而不是11呢?

这里设计到var和let声明变量的区别:

全局作用域下,使用 var 声明的变量会成为 window 对象的属性。这是因为全局作用域在浏览器中实际上就是 window 对象,所以任何在全局作用域中声明的 var 变量都会附加到 window 对象上。

let 和 const 声明的变量具有块级作用域,并且不会成为全局对象(在浏览器中即 window 对象)的属性,因此在全局作用域中使用 let 或 const 声明的变量不会附加到 window 对象上。这是它们与 var 的一个重要区别,也是为什么在现代JavaScript开发中更推荐使用 let 和 const 的原因之一。

若把let改成var:

var num = 11; // 
const obj1 = {
	num: 22,
	fn1: () => {
		let num = 33;
		const obj2 = {
			num: 44,
			fn2: () => {
				console.log(this.num);
			}
		}
		obj2.fn2();
	}
}
obj1.fn1(); //11


                        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值