从零开始学JS(每周一更)

JS语法基础(全)


前言

本文要记录的是js的一些基础内容,es6语法,dom,和bom

本文将会在每周进行更新,也是作为我自己学习js的一个笔记,主要是参考《JS高级程序设计》第4版这本书,有些书上感觉不对劲的内容也会参考一些大佬的博客,在后续进行引用时我都会声明,希望能对和我一样学习js的小伙伴有所帮助`


提示:以下是本篇文章正文内容

一、变量和数据类型

在JS中,有var,let和const三种修饰变量的关键字,下面将进行逐个介绍及比较。

1.1var声明关键字:

 // var的声明作用域
         function text(){
             var a=1;
             console.log(a);
         }
         console.log(a);//a is not defined
 // var的作用域是在一个函数作用域之中
  // var的声明提升
        function test2() {
            console.log(a);
            var a = 2;
        }
        test2();//undefined
 // 等价于:
         function text2(){
            var a;
             console.log(a);
             a=2;
         }
 //var提升的是变量的“声明”,不是变量的“赋值”。

1.2let声明

 // let的作用域
        function test3() {
            for (let i = 0; i < 3; i++) {
                console.log('我是let声明');
            }
            console.log(i);//i is not defined
        }
 // let的作用域是块级作用域,例如在if语句,for循环,while循环中起作用
 //let声明的变量不会再作用域中被提升,
        function test4() {
            console.log(name);//undefine
            var name = '我是var声明';
            console.log(age);
            let age = 18;
        }
        test4();//ReferenceError: can't access lexical declaration `age' before initialization(在初始化之前,不能使用变量age)
        

JS再解析代码时会注意到你将let在后面声明,但是它不让用,在let声明前执行的那段瞬间1也叫做“暂时性死区”

let 常用在定义for循环变量,或者其他的代码块中
var 常用于需要定义访问到全局对象的变量中,因为let在全局作用域中声明不会成为window对象的属性

1.3const声明变量

const的各种行为基本和let差不多,有一个区别就是使用const声明变量时
必须同时初始化变量,而且在后续不能对被const修饰的变量进行修改
const声明的限制只适用于它指向的变量的引用
例如

 const person = {
            name: '小红'
        };
        person.name = '小明';//小明
// 使用const声明对象时,并不会对它里面的对象属性进行const修饰

// const和let的声明不允许重复,而var允许重复声明,且会取var最后声明的变量的值

1.4数据类型

在js中有六种简单的数据类型: Undefined、Null、Boolean、Number、String

Symbol(ES6新增加的)

一种复杂数据类型:Object(本质上是由一组无序的名值对组成的)

1.4.1typeof 操作符

对一个值使用typeof操作符可能会返回下列某个字符:

“undefined”:如果这个值未定义
“boolean”:如果这个值是布尔值
“number”:如果这个值是数值
“string”:如果这个值是字符串
“object”:如果这个值是对象或者null
“function ”:如果这个值是函数

调用typeof null会返回"object",因为特殊值 null 被认为是一个空的对象引用

1.4.2 Undefined 类型

Undefined类型只有一个值,即特殊的 undefined,在使用 var 声明变量但未对其加以初始化时,这个变量的值就是 undefined,即

var msg;
cosnole.log(msg == undefined);  // true

另外,对未初始化的变量执行 typeof 和对未声明的变量执行 typeof 都会返回 undefined,如:

var msg;
// var age;
console.log(typeof msg);  // "undefined"
console.log(typeof age);  // "undefined"

1.4.3 Null 类型

Null 类型是第二个只有一个值的数据类型,这个值是 null,null 表示一个空对象指针,所以使用 typeof 返回的是“object”

实际上,undefined 是派生自 null 的,所有它们的相等性测试要返回true:

console.log(null == undefined);  // true

只要在意保存对象的变量还没有真正的保存对象,就应该明确地让该变量保存 null 值

1.4.4 Boolean 类型

虽然 Boolean 类型的字面值只有两个 true 和 false,但是ECMAScript所有类型的值都有与这两个Boolean值等价的值

1.4.5 Number 类型

最基本的数字字面量格式是十进制整数

var num = 55;

整数还可以使用八进制和十六进制。

1. 浮点数值

浮点数值,即数值中必须包括一个小数点,而且小数点后必须至少有一个数字。

由于保存浮点数值需要的内存空间是整数的两倍,所以有时会将浮点数值转换为整数值:

var floatNum1 = 1.;  // 解析为1
var floatNum2 = 10.0;  // 解析为10

对于极大或极小的值可以使用 e 表示法(科学计数法)

var bigFloatNum = 3.125e7;  // 等于31250000
var smallFloatNum = 3e-17;  // 等于0.00000000000000003

默认情况下,ECMAScript 会将小数点后带有6个零以上的浮点数值转换为 e 表示法,例如:
0.0000003会被转换为 3e-7

浮点整数的最高精度为17位小数,但是进行算术计算的时候精准度不如整数,例如:0.1 加 0.2 结果不是 0.3,而是 0.30000000000000004,这样的误差会导致无法测定特定的浮点数值。
2. 数值范围

ECMAScript中
最小的数值为:Number.MIN_VALUE,在大多数浏览器中该数值为5e-324;
最大的数值为:Number.MIN_VALUE ,在大多数浏览器中该数值为1.7976931348623157e+308

如果某次计算的结果值超出了数值范围,就会转换成特殊的 Infinity 值( -Infinity 负无穷或 Infinity 正无穷)

可以使用isFinite()函数判断是否为无穷数。
3. NaN

NaN(Not a Number)是一个特殊的数值,该数值表示一个本来要返回数值的操作没返回数值的情况(这样就不会抛出错误了)

NaN有两个特点:

任何涉及NaN的操作结果都是Nan
NaN与任何值都不相等,即使是和自己

由于以上特点,我们使用 isNaN() 函数来判断一个数是不是数值:

console.log(isNaN(NaN));    // true
console.log(isNaN(10));     // false(10是一个数值)
console.log(isNaN('10'));   // false(可以转换为数值10)
console.log(isNaN('blue'));  // true(不能转换为数值)
console.log(isNaN(true));   // false(不能转换为数值)
isNaN()同样适用于对象,基于对象调用时,首先会调用对象的valueOf()方法,然后再确定返回的值可否转换为数值。

4. 数值转换

有三个函数可以把非数值转换为数值:

Number()
parseInt()
parseFloat()

Number()可以用于任何数据类型,parseInt 和 parseFloat专门用于把字符串转换成数值

Number() 函数在转换时规则比较复杂且不够合理,所以更常用的是 parseInt() 函数
parseInt() 函数在转换字符串时有几个规则

它会忽略字符串前面的空格,直至找到第一个非空字符串。
如果第一个字符不是数字字符或负数,parseInt() 就会返回 NaN。
如果第一个字符是数字字符,parseInt 会继续解析第二个字符,知道解析完所有的字符或遇到一个非数字字符。

可以在转换时,指定第二个参数即转换使用的基数(即多少进制),来消除 parseInt() 在进制方面的困惑。

var num1 = parseInt("10", 2);   // 2(二进制)
var num2 = parseInt("10", 8);   // 8(八进制)
var num3 = parseInt("10", 10);  // 10(十进制)
var num4 = parseInt("10", 16);  // 16(十六进制)

parseFloat()也会从第一个字符开始解析每个字符,也是一直解析到字符末尾,或者解析到遇见一个无效的浮点数字字符为止。也就是说第一个小数点是有效的,第二个就是无效的,后面的字符会被忽略。

parseFloat()还有一个特点是会忽略前导的零。它只能解析十进制数值。

1.4.6 String类型

1.字符串的用法
字符串可以使用 ’ (单引号)," (双引号), `(反引号)来进行表示的。

 let fitstname='小明';
 let secondname='"小明"是我';

在使用字符串时,同其他语言一样,如果里层需要表示双引号,那么外层只能够用单引号进行表示

字符串中计算长度可以由length属性进行获取;

let str='abcde\u03a3';
console.log(str.length);//6

在这里输出的字符串长度是6,原因是转移字符 \u03a3只占用一个字符的空间

2.字符串的特点

let lang='java';
lang=lang+'script';

在ECMAscript中字符串是不可变的,意思是它们一旦创建,值就不可变,如果要进行值的修改,那么就会销毁原先的字符串,然后将包含新值的另一个字符串保存到该变量上。

在这个代码段中,变量lang一开始包含字符串‘java’,之后lang被重新定义为java和script的组合,在这整个过曾中首先会给lang分配一个足够容纳10个字符的空间,然后填充金java和script,最后再对java和script两个字符串进行销毁,因为这两个字符串之后都没有用了。

这就是为什么JavaScript在拼接字符串时的速度会非常慢的原因了,所以在以后的程序设计中,尽量避免出现字符串拼接

3.转换为字符串
在js中有很多方法可以将非字符串类型转化为字符串,在这里介绍两种同时进行扩展

1.toString()方法

toString()方法可以将除了null和undefined两种数据类型的其他数据类型转换为字符串,同时在tostring()中的参数可以传入,以下为代码

        let num=10;
        console.log(num.toString(2))//2进制输出
        console.log(num.toString(8))//8进制输出
        console.log(num.tostring(16))//16进制输出
        

2.String()转型函数
如果你要确定一个值是不是undefined或null,你可以使用String()转型函数进行判断
String有以下特点:

1.如果值有tosring()方法,则会调用该方法并返回结果
2.如果值是null,则会返回"null"
3.如果值是undefined,则会返回"undefined"

此外,只需要在任意数据类型后面+“”,即可转换为字符串类型

4.模板字面量

ES引入模板字面量,对字符串的操作进行了增强。

1.多行字符串

模板字面量的基础语法是用反引号(`)替换字符串的单、双引号。


let and = `hello world`

在ES6中使用模板字面量语法,很方便地实现多行字符串地创建,想在字符串中添加新的一行,只需要在代码中直接换行就行。

let msg = `hello
world`
console.log(msg)
// 输出结果
hello
world

2.字符串占位符

在模板字面量中,可以将js变量或合法的js表达式嵌入占位符并将其作为字符串的一部分输出来。占位符由一个左侧的"${“和右侧的”}"符号组成,中间可以包含变量或js表达式。

let name = '张三'
let msg = `hello ${name}`
console.log(msg) //hello 张三

3.String.raw()方法

往往用来充当模板字面量的处理函数,返回一个斜杠都被转义(即斜杠前面再加一个斜杠)的字符串,对应于替换变量后的模板字面量

简单的说就是可以返回原生的代码。例如转移字符 \n不会被解释为换行而只是单纯的输出 \n

// String.raw()方法
        console.log(String.raw`小明\n小军`);//小明\n小军,\n不解释

        let name = "Bob";
        String.raw`Hi\n${name}!`;//"Hi\nBob!"

        let name_s=`小明\n小军`;
        console.log(String.raw`小红\n${name_s}`);//小红\n小明
                                                //小军
        //内插表达式正常运行

1.4.7 objecct类型

因为在后续将会有专门一章介绍对象,所在在这一章我并不会将它将的太过于详细,只是作为简单的了解。什么是对象?怎么创建对象?怎么访问对象的属性?

1.什么是对象?

Object 是 JavaScript 的一种 数据类型 。它用于存储各种键值集合和更复杂的实体。通俗的说,对象是拥有属性和方法的数据。
例如在现实生活中:一辆汽车是一个对象。汽车有它的属性,如重量和颜色等,方法有启动停止等:

2.怎么创建对象

创建对象有两种方法,在这里放上代码,可以很快的理解到对象的创建。

  //方法一
        let person = new Object();
        person.name = "小明";
        person.age = 18;
        person.address = "北京";

        //方法二
        let obj={
            name:"小军",
            age:20,
            address:"上海"
        }

3.怎么访问对象?

你已经简单的知道了对象怎么创建了,而且觉得第二种创建对象的方法显然更好用。那么先到到了下一步,我创建了对象,我该怎么去访问我对象中的属性。

 //访问对象属性
        //方法一
        console.log(person.name);//小明
        console.log(obj.name);//小军

        //方法二

        console.log(person["age"]);//18
        console.log(person["address"])//北京

以上有两种访问对象的方式,建议使用第二种方式。方括号语法的主要优点是可以通过变量来访问属性,例如:

  let proName="name";
  console.log(person[proName]);//小明

方括号语法可以进行动态编程。

4.我该怎么去获得对象的全部属性?

你创建了对象,也会去访问单个对象,那么你要是忘记了你之前创建的对象有哪些属性可怎么办,在这里有一种遍历对象属性的 for…in语句,请看以下代码:

 for (var k in person) {
            console.log(k);
            k:对象的属性名或 数组的索引,都是字符串
            console.log(person[k]);
            //person[k]:对象obj属性值或数组obj的元素值
            //注意:不管person是普通对象还是数组,都不需要引号 person['k']
        }
        //输出
        name
        小明
        age
        18
        address
        北京

1.4.8 Symbol类型

在这一小节,我将会简单的介绍symbol,同时,也不会详细的对这个类型进行介绍,只是让自己在这一章的学习中对这个数据类型有所印象。

学习Symbol,我有几个疑问。什么是Symbol类型?它有什么用?怎么去创建?怎么去使用?

1.什么是Symbol类型?

symbol 是一种基本数据类型 (primitive data type)。Symbol()函数会返回symbol类型的值,该类型具有静态属性和静态方法。它的静态属性会暴露几个内建的成员对象;它的静态方法会暴露全局的symbol注册,且类似于内建对象类,但作为构造函数来说它并不完整,因为它不支持语法:“new Symbol()”。。

这是MDN上对于Symbol的解释,Symbol简单的解释就是独一无二,世界上有很多人,比如有很多人都叫张伟,那么我们是怎么把那些张伟分清楚的呢?那我们可以通过他的身份证号来判断这是哪个张伟,因为身份证号 是独一无二的,symbol也是如此

2.symbol有什么用

消除直接的 == 比较

一般我们写代码

if(name == "小明"){
	xxxx
}else if(name == "小军"){
   xxxx
}

这段代码转自 https://blog.csdn.net/qq_33270597/article/details/110822546 大佬的文章

但是这种代码很不雅观,也很难维护,此时就可以用Symbol变量了

const nameList = {
	Tom: Symbol(),
	Laly: Symbol()
}
if(name == nameList.Tom){
	xxxx
}else if (name == nameList.Laly){
	xxxx
}

3.symbol怎么去创建?

const s1 = Symbol();
const s2 = Symbol();
// Symbol中的标识一般放number、string
console.log(s1 === s2); // false

在 Symbol() 函数中可以传递一个字符串参数,表示对 Symbol 值的描述,主要是方便对不同 Symbol 值的区分。但是需要注意的是,由于 Symbol 值的唯一性,任何通过 Symbol() 函数创建的 Symbol 值都是不相同的,即使传递了相同的字符串。

 let a1=Symbol("Name");
 let a2=Symbol("Name");
 console.log(a1==a2);

通过以上的学习,我们简单学习了七大数据类型,下一章我将会记录关于操作符的使用,这一章节会讲的比较粗略。

二.操作符

在这一章中,我准备讲解JS的基本操作符,不要小看这一章节,不同数据类型相加相减会出现什么样的现象是很重要的,这一章节还会介绍位运算操作符,对于理解JS语法有很大的帮助

2.1.1加法+

如果有任意一操作数是NAN,则返回NAN
如果是Infinity+Infinity,则返回Infinity
如果是-Infinity + -Infinity ,则返回-Infinity
如果是Infinity + -Infinity,则返回NaN
如贵是数字+字符串,则返回字符串

var box=1+2                       //3
var box=1+NaN                     //NaN,有一个为NaN就为NaN
var box=Infinity+Infinity      //Infinity
var box=-Infinity + -Infinity    //-Infinity
var box=Infinity + -Infinity     //NaN,正无穷和负无穷相加为NaN
var box=100+ '100';            //100100,有一个为字符串相加为字符串拼接
var box='你的年龄是'+10+20;       //你的年龄是1020,后面的整形已转换成字符串
var box='你的年龄是'+(10+20);     //你的年龄是30,后面的整形先相加在转换吃字符串
var box=10+20+'是你的年龄';       //30是你的年龄,先相加,在转换成字符串
var box=10+对象;              //10[object Object],如果有toString()或valueOf()则返回10+返回的值

2.1.2减法-

减法和加法很相似,具体看代码实现

var box=100-70;  //30
var box=-100-70; //-170
var box=-100--70; //-30
var box=-100-NaN   //NaN
var box=Infinity-Infinity   //NaN
var box=-Infinity--Infinity //NaN
var box=Infinity--Infinity //Infinity
var box=-Infinity-Infinity //-Infinity
var box=100-true;     //99,true转换成数值为1
var box=100-'';      //100,''转换为0

2.1.3除法



var box=100/70   ///1.42....
var box=100/NaN   ///NaN
var box=Infinity/Infinity  ///NaN
var box=-Infinity/Infinity  ///NaN
var box=-Infinity/-Infinity  ///NaN
var box=100/true       ///100,true转换成1
var box=100/''          /// Infinity
var box=100/null         ///Infinity
var box=100/'lee'         ///NaN
var box=100/对象            ///NaN ,如果有toString()或valueOf(),返回10/返回值

2.2位运算符

2.2.1按位非 ~

按位非运算符·,即使求补数,对数值取反并且减1。

代码来自MDN

const a = 5;     // 00000000000000000000000000000101
const b = -3;    // 11111111111111111111111111111101

console.log(~a); // 11111111111111111111111111111010
// expected output: -6

console.log(~b); // 00000000000000000000000000000010
// expected output: 2

2.2.2按位与&

全是1才为1,有0为0

const a = 5;        // 00000000000000000000000000000101
const b = 3;        // 00000000000000000000000000000011

console.log(a & b); // 00000000000000000000000000000001
// expected output: 1

2.2.3按位或|*

将运算数以二进制表示, 对应位有一个为1, 则结果为1, 否则为0.
0001
| 0011
0011

在JS中可以应用与对浮点数向下取整

let num = 1.6 | 0; // 1

2.2.4异或^

只要有1就为1,全为1或者全为0则为0
0001
^ 0011
0010

 let n = 10;
 let m = 15;
 console.log(n ^ m);  //5

此外,异或运算符最直接的一个用途就是可以实现两数交换,看代码

let num1 = 1, num2 = 2;
num1 ^= num2;
num2 ^= num1;
num1 ^= num2;
console.log(num1);  // 2
console.log(num2);  // 1

还有几个位运算操作符在这里将不再作为介绍。

三.语句

在这里只会对JS的一些语句进行介绍,像for循环,while循环和if都已在其他语言中学过,而且语法基本一致

for…in语句

在前面对于对象的简单介绍中提过for…in语句,它可以遍历对象,还可以遍历数组(数组就是一个特殊的对象),在这里只介绍遍历数组这个方面,遍历对象可以往前面看。


        let arr=[1,2,3,4,5,6,7];
        for(let x in arr){
            console.log(x,arr[x]);
            //x代表下标,arr[x]代表对应下标的值
        }

但是使用for...in遍历数组的方法在很多书上都不推荐,所以尽量少用

那么我一定要遍历数组,不用for循环的前提下,可以选择for…of语句

for…of语句

for-of可以简单、正确地遍历数组

let number = [10, 20, 30];

for (let value of number) {
    value += 1;
    console.log(value);//11 21 31
}


如果你不想修改语句块中的变量 , ``也可以使用const代替let。

let number = [10, 20, 30];

for (const value of number) {
  console.log(value);//10 20 30
}

迭代字符串
 let str=['Tom','Judy']
        for(const value of str){
            console.log(value);//Tom Judy
        }

此外,对于for…of的循环,可以由break, throw continue 或return终止。

 let number=[1,2,3,4,5,6,7];
       for(const value of number){
            if(value==6)break;
            console.log(value);//1 2 3 4 5
            //当遍历到数组元素为6时已经跳出循环
       }

四.变量,作用域与内存

4.1原始值和引用值

原始值
原始值就是最简单的数据,例如Number,String,symbol,undefined,这些简单数据类型都是属于原始值
引用值
引用值在js中是由多个值构成的对象,也就是object类型

引用值是保存在内存中的对象,与其他语言不同,JS不允许直接访问内存地址,音痴不能直接操作对象所在的内存空间。

4.1.1动态属性

先看引用值:

       let person=new Object();
       person.name='小明';
       console.log(person.name);//'小明'

在这里发现,对于引用值而言,可以随时添加,修改和删除其属性方法。

但是对于原始值来说,原始值不能有属性。

        let name='小军';
        name.age=18;
        console.log(name.age);//undefined

在这段代码中,给字符串name定义了一个age属性,然后再下一行输出,
结果输出是undefined,说明只有引用值可以动态添加后面可以使用的属性。

4.1.2 复制值

在这里只作简单的介绍。
原始值的在JS的存储方式是栈存储,在通过变量把一个原始值赋值给另一个变量时,原始值会被复制到新变量的位置。
引用值:
对于上面的操作来说,把一个变量复制给另外一个变量,存储在变量中的值也会被复制到新变量所在的位置,区别就是两个变量的指针实际上指向的是同一个位置,所以引用值在JS的存储方式是堆存储。

4.1.3 instanceof操作符

typeof也能够判断一个变量是否为原始类型,但是如果值是对象或者null,都会返回object,而这时候使用instanceof就可以对引用值起到细分的区别

instanceof操作符的使用

        //instanceof操作符的使用
      
        let x=null;
        let arr=[];
        
        console.log(typeof x);//object
        console.log(typeof arr);//object
        console.log(x instanceof Object);//false
        console.log(arr instanceof Array);//true

4.2执行文与作用域

每次当控制器转到可执行代码的时候,就会进入一个执行上下文。执行上下文可以理解为当前代码的执行环境,它会形成一个作用域。JavaScript中的运行环境大概包括三种情况。
全局环境:JavaScript代码运行起来会首先进入该环境
函数环境:当函数被调用执行时,会进入当前函数中执行代码
eval(不建议使用)

当代码在执行过程中,遇到以上三种情况,都会生成一个执行上下文,放入栈中,而处于栈顶的上下文执行完毕之后,就会自动出栈。

var color = 'blue';

function changeColor() {
  var anotherColor = 'red';

  function swapColors() {
    var tempColor = anotherColor;
    anotherColor = color;
    color = tempColor;
  }

  swapColors();
}

changeColor();

我们用ECStack来表示处理执行上下文组的堆栈。我们很容易知道,第一步,首先是全局上下文入栈。
在这里插入图片描述

全局上下文入栈之后,其中的可执行代码开始执行,直到遇到了changeColor(),这一句激活函数changeColor创建它自己的执行上下文,因此第二步就是changeColor的执行上下文入栈。
在这里插入图片描述
changeColor的上下文入栈之后,控制器开始执行其中的可执行代码,遇到swapColors()之后又激活了一个执行上下文。因此第三步是swapColors的执行上下文入栈。
在这里插入图片描述
在swapColors的可执行代码中,再没有遇到其他能生成执行上下文的情况,因此这段代码顺利执行完毕,swapColors的上下文从栈中弹出。
在这里插入图片描述
swapColors的执行上下文弹出之后,继续执行changeColor的可执行代码,也没有再遇到其他执行上下文,顺利执行完毕之后弹出。这样,ECStack中就只剩下全局上下文了。
在这里插入图片描述
整个过程:
在这里插入图片描述
这段理解来自简书 https://www.jianshu.com/p/a6d37c77e8db

以下代码也可以帮助对于作用域链的理解

        var color='blue';
        function changecolor(){
            let anothercolor='red';
            function swapcolor(){
                let tempcolor=anothercolor;
                anothercolor=color;
                color=tempcolor;
                //这里可以访问color和anothercolor,tempother
            }
            //这里可以访问color和anothercolor,但访问不到tempcolor
            swapcolor();
        }
        //这里只能访问到color
        changecolor();

4.3垃圾回收

在JS中使用垃圾回收的策略是标记清理和引用计数

在JS中垃圾回收层序会周期性运行,如果内存中分配了很多的变量,很可能会造成性能上的损失。对于提升性能方面,有以下几种策略可以尝试

1.通过const和let声明提升性能

const和let声明都属于块级作用域,相比于var能够尽早的回收内存

  • 16
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 11
    评论
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

灵均666

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

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

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

打赏作者

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

抵扣说明:

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

余额充值