javascript高级

面向对象

对象----是由属性和方法组成的:是一个无序键值对的集合,指的是一个具体的事物

  • 属性:事物的特征,在对象中用属性来表示(常用名词)
  • 方法:事物的行为,在对象中用方法来表示(常用动词)

面向对象---- 把具体事务分解成一个个对象,然后由对象之间分工合作.易维护、易复用、易扩展,由于面向对象有 封装、继承、多态性的特性,可以设计出低 耦合的系统,使系统 更加灵活、更加易于维 护,但是性能比面向过程低.

面向过程---- 就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现, 使用的时候再一个一个的依次调用就可以了.性能比面向对象高,适合跟 硬件联系很紧密的东西,例 如单片机就采用的面向过程 编程。但是不易维护,不易服用,不易扩展

在 ES6 中新增加了类的概念,可以使用 class 关键字声明一个类,之后以这个类来实 例化对象。类抽象了对象的公共部分,它泛指某一大类(class)对象特指某一个, 通过类实例化一个具体的对象
语法:

//步骤1 使用class关键字 
class name {   // class body }      
//步骤2使用定义的类创建实例  注意new关键字 
var xx = new name(); 

创建类并添加属性和方法

		// 1. 创建类 class  创建一个 明星类  
        class Star { // 类的共有属性放到 constructor 里面    
            constructor(name, age) {
                this.name = name;
                this.age = age;
            }
            //‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐>注意,方法与方法之间不需 要添加逗号
            sing(song) {
                console.log(this.name + '唱' + song);
            }
        } 
        // 2. 利用类创建对象 new    
        var ldh = new Star('刘德华', 18);
        console.log(ldh);// Star {uname: "刘德华", age: 18}
        ldh.sing('冰雨'); // 刘德华唱冰雨

注意:

  1. 通过class 关键字创建类, 类名我们还是习惯性定义首字母大写
  2. 类里面有个constructor 函数,可以接受传递过来的参数,同时返回实例对象
  3. constructor 函数 只要 new 生成实例时,就会自动调用这个函数, 如果我们不写这个 函数,类也会自动生成这个函数
  4. 多个函数方法之间不需要添加逗号分隔
  5. 生成实例 new 不能省略
  6. 语法规范, 创建类 类名后面不要加小括号,生成实例 类名后面加小括号, 构造函数不需 要加function

类的继承

// 父类 
        class Father{    } 
        // 子类继承父类 
        class  Son  extends Father {   }
子类继承父类的属性和方法
class Father {
            constructor(surname) {
                this.surname = surname;
            }
            say() {
                console.log('你的姓是' + this.surname);
            }
        }
        class Son extends Father {
            // 这样子类就继承了父类的属性和方法 
        }
        var damao = new Son('刘');
        damao.say(); //结果为 你的姓是刘
super关键字
class Father {
            constructor(x, y) {
                this.x = x;
                this.y = y;
            }
            sum() {
                console.log(this.x + this.y);
            }
        }

        //子元素继承父类     
        class Son extends Father {
            constructor(x, y) {
                super(x, y); //使用super调用了父类中的构造函数               
            }
        }
        var son = new Son(1, 2);
        son.sum(); //结果为3

注意:

  1. 继承中,如果实例化子类输出一个方法,先看子类有没有这个方法,如果有就先执行 子类的

  2. 继承中,如果子类里面没有,就去查找父类有没有这个方法,如果有,就执行父类的这 个方法(就近原则)

  3. 如果子类想要继承父类的方法,同时在自己内部扩展自己的方法,利用super 调用 父类的构造函数,super 必须在子类this之前调用

  4. 时刻注意this的指向问题,类里面的共有的属性和方法一定要加this使用.

    1. constructor中的this指向的是new出来的实例对象
    2. 自定义的方法,一般也指向的new出来的实例对象
    3. 绑定事件之后this指向的就是触发事件的事件源
  5. 在 ES6 中类没有变量提升,所以必须先定义类,才能通过类实例化对象

静态成员和实例成员

实例成员就是构造函数内部通过this添加的成员 如下列代码中uname age sing 就是实例 成员,实例成员只能通过实例化的对象来访问

	function Star(uname, age) {
            this.uname = uname;
            this.age = age;
            this.sing = function () {
                console.log('我会唱歌');
            }
        }
        var ldh = new Star('刘德华', 18);
        console.log(ldh.uname); //实例成员只能通过实例化的对象来访问

静态成员 在构造函数本身上添加的成员 如下列代码中 sex 就是静态成员,静态成员只能 通过构造函数来访问

		function Star(uname, age) {
            this.uname = uname;
            this.age = age;
            this.sing = function () {
                console.log('我会唱歌');
            }
        }
        var ldh = new Star('刘德华', 18);
        Star.sex = '男';
        console.log(Star.sex);//静态成员只能通过构造函数来访问
        console.log(ldh.uname); //实例成员只能通过实例化的对象来访问

构造函数

构造函数原型prototype,原型对象

构造函数的函数方法每次new一个对象的时候都会在内存中分配内存空间,非常浪费内存.
在这里插入图片描述
通过构造函数原型分配的函数方法是所有对象所共享的.
在javascript中规定,每一个构造函数都有一个prototype属性,指向一个对象,这个对象的所有属性和方法都会被构造函数所拥有.
可以把一些不变的方法,定义在prototype对象上,这样所有的实例对象就可以共享这些方法.

		function Star(uname, age) {
            this.uname = uname;
            this.age = age;
        }
        Star.prototype.sing = function () {
            console.log(this.uname + '会唱歌');
        }
        var ldh = new Star('刘德华', 18);
        ldh.sing(); //刘德华会唱歌

对象原型

对象都会有一个属性__proto__指向构造函数的prototype原型对象,我们可以使用构造函数prototype原型对象的属性和方法,就是因为对象有__proto__原型的存在.
__proto__对象原型和prototype原型对象是等价的.
__proto__对象原型的意义就在于为对象的查找机制提供一个方向,或者说一条路线,但是 它是一个非标准属性,因此实际开发中,不可以使用这个属性,它只是内部指向原型对象 prototype
在这里插入图片描述

constructor构造函数

  • 对象原型( __ proto __)和构造函数(prototype)原型对象里面都有一个属性 constructor 属性 ,constructor 我们–称为构造函数,因为它指回构造函数本身。
  • constructor 主要用于记录该对象引用于哪个构造函数,它可以让原型对象重新指向原来的构造函数。 一般情况下,对象的方法都在构造函数的原型对象中设置。如果有多个对象的方法,我们可 以给原型对象采取对象形式赋值,但是这样就会覆盖构造函数原型对象原来的内容,这样修 改后的原型对象 constructor 就不再指向当前构造函数了。此时,我们可以在修改后的 原型对象中,添加一个 constructor 指向原来的构造函数

如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用 constructor指回原来的构造函数如

		function Star(uname, age) {
            this.uname = uname;
            this.age = age;
        }
        Star.prototype = {
            constructor: Star,
            sing: function () {
                console.log(this.uname + '唱歌');
            },
            movie: function () {
                console.log(this.uname + '演电影');
            }
        }
        var ldh = new Star('刘德华', 18);
        var zxy = new Star('学友', 18);
        ldh.sing(); //刘德华唱歌
        zxy.movie();//学友演电影

在这里插入图片描述

原型链

每一个实例对象都有一个__proto__属性,指向构造函数的原型对象,构造函数的原型对象也是一个对象,也有__proto__属性,这样通过__proto__一层一层往上查找就形成了原型链.
在这里插入图片描述

原型链和成员的查找机制 (就近原则)

当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性。
如果没有就查找它的原型(也就是 __proto__指向的 prototype 原型对象)。
如果还没有就查找原型对象的原型(Object的原型对象)。
依此类推一直找到 Object 为止(null)。
__proto__对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线。

构造函数实例和原型对象三角关系

在这里插入图片描述
1.构造函数的prototype属性指向了构造函数原型对象
2.实例对象是由构造函数创建的,实例对象的__proto__属性指向了构造函数的原型对象
3.构造函数的原型对象的constructor属性指向了构造函数,实例对象的原型的 constructor属性也指向了构造函数

原型对象中this指向

构造函数中的this和原型对象中的this都指向new出来的实例对象.

		function Star(uname, age) {
            this.uname = uname;
            this.age = age;

        }
        var that;
        Star.prototype.sing = function () {
            that = this;
        }
        var ldh = new Star('刘德华', 18);
        ldh.sing();
        console.log(that === ldh);

子构造函数继承父构造函数中的属性

  1. 先定义一个父构造函数
  2. 再定义一个子构造函数
  3. 子构造函数继承父构造函数的属性(使用call方法)
		function Father(x, y) {
            this.x = x;
            this.y = y;
        }
        function Sun(x, y, z) {
            Father.call(this, x, y);
            this.z = z;
        }

        var s = new Sun(1, 2, 3);
        console.log(s.x);//1

借用原型对象继承方法

		// 1. 父构造函数 
        function Father(uname, age) {
            // this 指向父构造函数的对象实例   
            this.uname = uname;
            this.age = age;
        }
        Father.prototype.money = function () { console.log(100000); };
        // 2 .子构造函数    
        function Son(uname, age, score) {
            // this 指向子构造函数的对象实例       
            Father.call(this, uname, age);
            this.score = score;
        }
        // Son.prototype = Father.prototype;  这样直接赋值会有问题,如果修改了子原型对象,父原型对象也会跟着一起变化   
        Son.prototype = new Father();
        // 如果利用对象的形式修改了原型对象,别忘了利用constructor 指回原来的构造函数   
        Son.prototype.constructor = Son;
        // 这个是子构造函数专门的方法   
        Son.prototype.exam = function () {
            console.log('孩子要考试');
        }
        var son = new Son('刘德华', 18, 100);
        console.log(son);

在这里插入图片描述

ES5新增方法

forEach()

forEach() 方法用于遍历数组的每个元素,并将元素传递给回调函数。
注意: forEach() 对于空数组是不会执行回调函数的。
在这里插入图片描述

		var numbers = [4, 9, 16, 25];
        numbers.forEach((item, index) => {
            console.log('item:' + item + '--index:' + index);
        });
        // item: 4--index: 0
        // item: 9--index: 1
        // item: 16--index: 2
        // item: 25--index: 3

filter()

filter() 方法返回一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。

注意: filter() 不会对空数组进行检测。
注意: filter() 不会改变原始数组。
在这里插入图片描述

		var ages = [32, 33, 16, 40];
        var newArr = ages.filter((age) => {
            return age >= 18;
        });
        console.log(newArr);//[32, 33, 40]

find()

find() 方法返回通过测试(函数内判断)的数组的第一个元素的值。

find() 方法为数组中的每个元素都调用一次函数执行:
当数组中的元素在测试条件时返回 true 时, find() 返回符合条件的元素,之后的值不会再调用执行函数。
如果没有符合条件的元素返回 undefined
注意: find() 对于空数组,函数是不会执行的。
注意: find() 并没有改变数组的原始值。
在这里插入图片描述

		var ages = [3, 10, 18, 20];
        var age = ages.find((item) => {
            return item >= 18;
        });
        console.log(age);//18

findIndex()

findIndex() 方法返回传入一个测试条件(函数)符合条件的数组第一个元素位置。

findIndex() 方法为数组中的每个元素都调用一次函数执行:

当数组中的元素在测试条件时返回 true 时, findIndex() 返回符合条件的元素的索引位置,之后的值不会再调用执行函数。
如果没有符合条件的元素返回 -1
注意: findIndex() 对于空数组,函数是不会执行的。
注意: findIndex() 并没有改变数组的原始值。
在这里插入图片描述

		var ages = [3, 10, 18, 20];
        var age = ages.findIndex((item) => {
            return item >= 18;
        });
        console.log(age);//2

from()

定义和用法
from() 方法用于通过拥有 length 属性的对象或可迭代的对象来返回一个数组。
如果对象是数组返回 true,否则返回 false。
在这里插入图片描述

		var myArr = Array.from("RUNOOB");
        console.log(myArr);//["R", "U", "N", "O", "O", "B"]

map()

定义和用法
map() 方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。
map() 方法按照原始数组元素顺序依次处理元素。

注意: map() 不会对空数组进行检测。
注意: map() 不会改变原始数组。
在这里插入图片描述

		var numbers = [4, 9, 16, 25];
        var newArr = numbers.map((item) => {
            return item * 2;
        });
        console.log(newArr);//[8, 18, 32, 50]

reduce()

定义和用法
reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。
reduce() 可以作为一个高阶函数,用于函数的 compose。

注意: reduce() 对于空数组是不会执行回调函数的。
在这里插入图片描述

		var numbers = [1, 2, 3, 4];
        var sum = numbers.reduce((total, num) => {
            return total + num
        });
        console.log(sum);//10

some()

some() 方法用于检测数组中的元素是否满足指定条件(函数提供)。
some() 方法会依次执行数组的每个元素:

如果有一个元素满足条件,则表达式返回true , 剩余的元素不会再执行检测。
如果没有满足条件的元素,则返回false。
注意: some() 不会对空数组进行检测。
注意: some() 不会改变原始数组。
在这里插入图片描述

		var ages = [3, 10, 18, 20];
        var flag = ages.some((item) => {
            return item >= 30;
        });
        console.log(flag);//false

trim()

定义和用法
trim() 方法用于删除字符串的头尾空格。
trim() 方法不会改变原始字符串。
在这里插入图片描述

 		var str = "       Runoob        ";
        console.log(str.trim());//Runoob

Object.keys()

Object.keys() 方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和使用 for…in 循环遍历该对象时返回的顺序一致 。如果对象的键-值都不可枚举,那么将返回由键组成的数组。
在这里插入图片描述

// simple array
var arr = ['a', 'b', 'c'];
console.log(Object.keys(arr)); // console: ['0', '1', '2']

// array like object
var obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.keys(obj)); // console: ['0', '1', '2']

// array like object with random key ordering
var anObj = { 100: 'a', 2: 'b', 7: 'c' };
console.log(Object.keys(anObj)); // console: ['2', '7', '100']

// getFoo is a property which isn't enumerable
var myObj = Object.create({}, {
  getFoo: {
    value: function () { return this.foo; }
  } 
});
myObj.foo = 1;
console.log(Object.keys(myObj)); // console: ['foo']

Object.defineProperty

Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。

该方法允许精确添加或修改对象的属性。通过赋值操作添加的普通属性是可枚举的,能够在属性枚举期间呈现出来(for…in 或 Object.keys 方法), 这些属性的值可以被改变,也可以被删除。这个方法允许修改默认的额外选项(或配置)。默认情况下,使用 Object.defineProperty() 添加的属性值是不可修改的。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

		var obj = {
            name: 'zs',
            age: 18
        }
        Object.defineProperty(obj, 'sex', {
            enumerable: true,
            configurable: false,
            writable: false,
            value: "男"
        });
        var arr = Object.keys(obj);
        console.log(arr);//["name", "age"],enumerable改为true时结果为["name", "age", "sex"]

函数内部的this指向

在这里插入图片描述

改变函数内部this指向

call()方法-------apply()方法--------bind()方法

1.三个方法都可以改变函数的this指向.
在这里插入图片描述
以上出了 bind 方法后面多了个 () 外 ,结果返回都一致!
由此得出结论,bind 返回的是一个新的函数,你必须调用它才会被执行。

2,对比call 、bind 、 apply 传参情况下
在这里插入图片描述
call 、bind 、 apply 这三个函数的第一个参数都是 this 的指向对象,第二个参数差别就来了:

call 的参数是直接放进去的,第二第三第 n 个参数全都用逗号分隔,直接放到后面 obj.myFun.call(db,‘成都’, … ,‘string’ )。-------应用场景: 经常在子构造函数继承父构造函数时使用

apply 的所有参数都必须放在一个数组里面传进去 obj.myFun.apply(db,[‘成都’, …, ‘string’ ])。------应用场景: 经常跟数组有关系

bind 不会调用函数,但是能改变函数内部this 指向,返回的是原函数改变this之后产 生的新函数,它 的参数和 call 一样。-----应用场景:不调用函数,但是还想改变this指向,比如改变定时器内部的this指向

闭包

闭包直观来说就是形成一个不销毁的栈环境,延伸变量的作用范围.
是一种保护私有变量的机制,在函数执行时形成私有的作用域,保护里面的私有变量不受外部干扰.
自调用函数就是一种闭包现象

var add = (function () {
    var counter = 0;
    return function () {return counter += 1;}
})();
 
add();
add();
add();
 
// 计数器为 3

变量 add 指定了函数自我调用的返回字值。

自我调用函数只执行一次。设置计数器为 0。并返回函数表达式。

add变量可以作为一个函数使用。非常棒的部分是它可以访问函数上一层作用域的计数器。

这个叫作 JavaScript 闭包。它使得函数拥有私有变量变成可能。

计数器受匿名函数的作用域保护,只能通过 add 方法修改。

递归

递归:一个函数在函数内部调用其本身,那么这个函数就是递归函数.简单来说就是函数内部自己调用自己就是递归函数.
注意:递归函数的作用跟循环一样,如果函数内部不加return退出条件,很容易发生"栈溢出"错误(stack overflow)

//利用递归函数求1~n的阶乘 1 * 2 * 3 * 4 * ..n 
	function fn(n){
		//结束条件
		if (n === 1) {
			return n;
		}
		return n * fn(n - 1);
	}
	console.log(fn(4));//24
/*利用递归函数求斐波那契数列(兔子序列)  1、1、2、3、5、8、13、21...  用户输入一个数字 n 就可以求出 这个数字对应的兔子序列值
 我们只需要知道用户输入的n 的前面两项(n‐1 n‐2)就可以计算出n 对应的序列值*/
	function fn(n){
		//结束条件
		if (n===1 || n===2) {
			return 1;
		}
		return fn(n - 1) + fn(n - 2);
	}
	console.log(fn(5));//5

正则表达式

正则表达式用于对字符串进行模式匹配及检索替换,是对字符串执行模式匹配的强大工具。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

ES6新增语法

动态计算属性

在这里插入图片描述

let 和 const

这两个关键字都是用来声明变量的.
let 声明的变量只在 let 命令所在的代码块内有效。
const 声明一个只读的常量,一旦声明,常量的值就不能改变。

在 ES6 之前,是没有块级作用域的概念的。
ES6 可以使用 let 关键字来实现块级作用域。
let 声明的变量只在 let 命令所在的代码块 {} 内有效,在 {} 之外不能访问。

{ 
    let x = 2;
}
console.log(x)// 这里不能使用 x 变量

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
const 关键字
const 用于声明一个或多个常量,声明时必须进行初始化,且初始化后值不可再修改

const PI = 3.141592653589793;
PI = 3.14;      // 报错
PI = PI + 10;   // 报错

const定义常量与使用let 定义的变量相似:
二者都是块级作用域
都不能和它所在作用域内的其他变量或函数拥有相同的名称

两者还有以下两点区别:
const声明的常量必须初始化,而let声明的变量不用
const 定义常量的值不能通过再赋值修改,也不能再次声明。而 let 定义的变量值可以修改。

// 错误写法
const PI;
PI = 3.14159265359;

// 正确写法
const PI = 3.14159265359;

并非真正的常量
const 的本质: const 定义的变量并非常量,并非不可变,它定义了一个常量引用一个值。使用 const 定义的对象或者数组,其实是可变的。下面的代码并不会报错:

// 创建常量对象
const car = {type:"Fiat", model:"500", color:"white"};
 
// 修改属性:
car.color = "red";
 
// 添加属性
car.owner = "Johnson";
// 创建常量数组
const cars = ["Saab", "Volvo", "BMW"];
 
// 修改元素
cars[0] = "Toyota";
 
// 添加元素
cars.push("Audi");

但是我们不能对常量对象/数组重新赋值:

const car = {type:"Fiat", model:"500", color:"white"};
car = {type:"Volvo", model:"EX60", color:"red"};    // 错误
const cars = ["Saab", "Volvo", "BMW"];
cars = ["Toyota", "Volvo", "Audi"];    // 错误

在这里插入图片描述

解构赋值

解构赋值是对赋值运算符的扩展.可以针对对象或数组进行模式匹配,分解之后对其中的变量进行赋值.
在这里插入图片描述
在这里插入图片描述

对象模型的解构(Object)

let { foo, bar } = { foo: 'aaa', bar: 'bbb' };
// foo = 'aaa'
// bar = 'bbb'
 
let { baz : foo } = { baz : 'ddd' };//foo是别名
// foo = 'ddd'

在这里插入图片描述

箭头函数

ES6中新增了箭头函数,就是函数的一种简写形式,箭头函数表达式的语法比普通函数表达式更简洁。

(参数1, 参数2,, 参数N) => { 函数声明 }

(参数1, 参数2,, 参数N) => 表达式(单一)
相当于:(参数1, 参数2,, 参数N) =>{ return 表达式; }
当只有一个参数时,圆括号是可选的:

(单一参数) => {函数声明}
单一参数 => {函数声明}
没有参数的函数应该写成一对圆括号:

() => {函数声明}

箭头函数不仅仅让代码更加简洁,当我们使用箭头函数的时候,箭头函数会默认绑定外层this的值,在箭头函数中this的指向和和外层的this指向是一样的.
箭头函数是不能提升的,所以需要在使用之前定义。
使用 const 比使用 var 更安全,因为函数表达式始终是一个常量。

剩余运算符

剩余运算符允许我们将一个不定数量的参数表示为一个数组,不定参数定义方式,这 种方式很方便的去声明不知道参数情况下的一个函数

function sum (first, ...args) {      
	console.log(first); // 10      
	console.log(args); // [[20,30], {name:'40',age:50}]   
}  
sum(10, [20,30], {name:'40',age:50});

剩余运算符和解构配合使用

let students = ['wangwu', 'zhangsan', 'lisi']; 
let [s1, ...s2] = students;  
console.log(s1);  // 'wangwu'  
console.log(s2);  // ['zhangsan', 'lisi']

可以将数组或者对象转为用逗号分隔的参数序列

 let ary = [1, 2, 3];  
...ary  // 1, 2, 3  
console.log(...ary);    // 1 2 3,相当于下面的代码  console.log(1,2,3);

可以合并数组

// 方法一   
let ary1 = [1, 2, 3];  
let ary2 = [3, 4, 5];  
let ary3 = [...ary1, ...ary2];  
// 方法二   
ary1.push(...ary2);

将类数组或可遍历对象转换为真正的数组
let oDivs = document.getElementsByTagName(‘div’);
oDivs = […oDivs];

set数据结构

ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的 值。
Set本身是一个构造函数,用来生成 Set 数据结构

let set = new Set()
Set函数可以接受一个数组作为参数,用来初始化。
const set = new Set([1, 2, 3, 4, 4]);//{1, 2, 3, 4}

add(value):添加某个值,返回 Set 结构本身
delete(value):删除某个值,返回一个布尔值,表示删除是否成功
has(value):返回一个布尔值,表示该值是否为 Set 的成员
clear():清除所有成员,没有返回值

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值