ES6 规范

ES6新增API

ES6完整内容强烈建议参考:https://es6.ruanyifeng.com/

ES6的特性⽐较多,在 ES5 发布近 6 年(2009-11 ⾄ 2015-6)之后才将其标准化。两个发布版本之间,时间跨度很⼤,所以ES6中的特性⽐较多,也是⾯试的核⼼基础考点。 在这⾥列举⼏个常⽤的,其他内容请看这⾥:

  • 类;
  • 模块化;
  • 箭头函数;
  • 函数参数默认值;
  • 模板字符串;
  • 解构赋值;
  • 延展操作符;
  • 对象属性简写;
  • Promise;
  • Let与Const;

类(class)

对熟悉Java,object-c,c#等纯⾯向对象语⾔的开发者来说,都会对class有⼀种特殊的情怀。ES6 引⼊了class(类),让JavaScript的⾯向对象编程变得更加简单和易于理解。

class Animal {
constructor(name,color) {
this.name = name;
this.color = color;
}
toString() {
console.log('name:' + this.name + ',color:' + this.color);
}
}
var animal = new Animal('dog','white');
animal.toString();
console.log(animal.hasOwnProperty('name'));
console.log(animal.hasOwnProperty('toString'));
console.log(animal.__proto__.hasOwnProperty('toString'));
class Cat extends Animal {
constructor(action) {
// 如果没有置顶consructor,默认带super函数的constructor将会被添加
super('cat','white');
this.action = action;
}
toString() {
console.log(super.toString());
}
}
var cat = new Cat('catch') cat.toString();
console.log(cat instanceof Cat);
console.log(cat instanceof Animal);

模块化(Module)

ES5不⽀持原⽣的模块化,在ES6中模块作为重要的组成部分被添加进来。模块的功能主要由 export 和import 组成。每⼀个模块都有⾃⼰单独的作⽤域,模块之间的相互调⽤关系是通过 export 来规定模块对外暴露的接⼝,通过import来引⽤其它模块提供的接⼝。同时还为模块创造了命名空间,防⽌函数的命名冲突。

导出(export)

ES6允许在⼀个模块中使⽤export来导出多个变量或函数。
导出变量

export var name = 'Rainbow' 

注意:ES6不仅⽀持变量的导出,也⽀持常量的导出。
例如:export const sqrt =Math.sqrt; //导出常量

ES6将⼀个⽂件视为⼀个模块,上⾯的模块通过 export 向外输出了⼀个变量。⼀个模块也可以同时往外⾯输出多个变量。

var name = 'Rainbow';
var age = '24';
export {name, age};

导出函数

export function myModule(someArg) {
return someArg;
}

导⼊(import)

定义好模块的输出以后就可以在另外⼀个模块通过import引⽤。

import {myModule} from 'myModule';
import {name,age} from 'test';

注意:⼀条import 语句可以同时导⼊默认函数和其它变量。
import defaultMethod, { otherMethod } from ‘xxx.js’;

箭头(Arrow)函数

这是ES6中最令⼈激动的特性之⼀。 => 不只是关键字function的简写,它还带来了其它好处。箭头函
数与包围它的代码共享同⼀个 this,能帮你很好的解决this的指向问题。

箭头函数的结构

箭头函数的箭头=>之前是⼀个空括号、单个的参数名、或⽤括号括起的多个参数名,⽽箭头之后可以是⼀个表达式(作为函数的返回值),或者是⽤花括号括起的函数体(需要⾃⾏通过return来返回值,否则返回的是undefined)。

() => 1
v => v+1
(a,b) => a+b
() => {
alert("foo");
}
e => {
if (e == 0) {
return 0;
}
return 1000/e;
}

注意:不论是箭头函数还是bind,每次被执⾏都返回的是⼀个新的函数引⽤,因此如果你还需要函数的
引⽤去做⼀些别的事情(譬如卸载监听器),那么你必须⾃⼰保存这个引⽤。

卸载监听器时的陷阱

  • 错误的做法
class PauseMenu extends React.Component{
componentWillMount() {
AppStateIOS.addEventListener('change', this.onAppPaused.bind(this));
}
componentWillUnmount() {
AppStateIOS.removeEventListener('change', this.onAppPaused.bind(this));
}
onAppPaused(event) {}
}
  • 正确的做法
class PauseMenu extends React.Component {
constructor(props) {
super(props);
this._onAppPaused = this.onAppPaused.bind(this);
}
componentWillMount() {
AppStateIOS.addEventListener('change', this._onAppPaused);
}
componentWillUnmount(){
AppStateIOS.removeEventListener('change', this._onAppPaused);
}

除上述的做法外,我们还可以这样做:

class PauseMenu extends React.Component{
componentWillMount() {
AppStateIOS.addEventListener('change', this.onAppPaused);
}
componentWillUnmount() {
AppStateIOS.removeEventListener('change', this.onAppPaused);
}
onAppPaused = (event) => {}
}

需要注意的是:不论是bind还是箭头函数,每次被执⾏都返回的是⼀个新的函数引⽤,因此如果你还需要函数的引⽤去做⼀些别的事情(譬如卸载监听器),那么你必须⾃⼰保存这个引⽤。

函数⼊参默认值

ES6⽀持在定义函数的时候为其设置默认值:

function foo(height = 50, color = 'red'){
}

不使⽤默认值:

function foo(height, color){
var height = height || 50;
var color = color || 'red';
}

这样写⼀般没问题,但当 参数的布尔值为false时,就会有问题了。⽐如,我们这样调⽤foo函数

foo(0, "")

因为 0的布尔值为false,这样height的取值将是50。同理color的取值为‘red’。
所以说, 函数参数默认值不仅能是代码变得更加简洁⽽且能规避⼀些问题。

模板字符串

ES6⽀持 模板字符串,使得字符串的拼接更加的简洁、直观。
不使⽤模板字符串:

var name = 'Your name is ' + first + ' ' + last + '.'

使⽤模板字符串:

var name = `Your name is ${first} ${last}.`

在ES6中通过 ${}就可以完成字符串的拼接,只需要将变量放在⼤括号之中。

解构

解构赋值语法是JavaScript的⼀种表达式,可以⽅便的从数组或者对象中快速提取值赋给定义的变量。

var foo = ["one", "two", "three", "four"];
var [one, two, three] = foo;
console.log(one);
console.log(two);
console.log(three);
//如果你要忽略某些值,你可以按照下⾯的写法获取你想要的值var [first, , , last] = fo
o;console.log(first); console.log(last);
//你也可以这样写var a, b;
[a, b] = [1, 2];
console.log(a);
console.log(b);

如果没有从数组中的获取到值,你可以为变量设置⼀个默认值。

var a, b;
[a=5, b=7] = [1];console.log(a); console.log(b);

通过解构赋值可以⽅便的交换两个变量的值。

var a = 1;
var b = 3;
[a, b] = [b, a];
console.log(a);
console.log(b);

获取对象中的值

const student = {
name:'xianzao',
age:'18',
city:'Shanghai'
};
const { name, age, city } = student;
console.log(name);
console.log(age);
console.log(city);

延展操作符(Spread operator)

延展操作符 … 可以在函数调⽤/数组构造时, 将数组表达式或者string在语法层⾯展开;
还可以在构造对象时, 将对象表达式按key-value的⽅式展开。

⽤法

  • 函数调⽤
myFunction(...iterableObj); 
  • 数组构造或字符串
[...iterableObj, '4', ...'hello', 6]; 
  • 构造对象时,进⾏克隆或者属性拷⻉(ECMAScript 2018规范新增特性)
let objClone = { ...obj };

应⽤场景

  • 在函数调⽤时使⽤延展操作符
function sum(x, y, z) {
return x + y + z;
}
const numbers = [1, 2, 3];
console.log(sum.apply(null, numbers));
console.log(sum(...numbers));
  • 构造数组
    没有展开语法的时候,只能组合使⽤ push,splice,concat 等⽅法,来将已有数组元素变成新数组的⼀部分。有了展开语法, 构造新数组会变得更简单、更优雅
const stuendts = ['Jine','Tom'];
const persons = ['Tony',... stuendts,'Aaron','Anna'];
conslog.log(persions)

和参数列表的展开类似, … 在构造字数组时, 可以在任意位置多次使⽤。

  • 数组拷⻉
var arr = [1, 2, 3];
var arr2 = [...arr];
arr2.push(4);
console.log(arr2)

展开语法和 Object.assign() ⾏为⼀致, 执⾏的都是浅拷⻉(只遍历⼀层)。

  • 连接多个数组
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
var arr3 = [...arr1, ...arr2]; //等同于var arr4 = arr1.concat(arr2);
  • 在ECMAScript 2018中延展操作符增加了对对象的⽀持
var obj1 = { foo: 'bar', x: 42 };
var obj2 = { foo: 'baz', y: 13 };
var clonedObj = { ...obj1 };
var mergedObj = { ...obj1, ...obj2 };
  • 在React中的应⽤
    通常我们在封装⼀个组件时,会对外公开⼀些 props ⽤于实现功能。⼤部分情况下在外部使⽤都应显示的传递 props 。但是当传递⼤量的props时,会⾮常繁琐,这时我们可以使⽤ …(延展操作符,⽤于取出参数对象的所有可遍历属性) 来进⾏传递。
<CustomComponent name ='Jine' age ={21} />

使⽤ … ,等同于上⾯的写法

const params = {
name: 'Jine',
age: 21
}
<CustomComponent {...params} />

配合解构赋值避免传⼊⼀些不需要的参数

var params = {
name: '123',
title: '456',
type: 'aaa'
}
var { type, ...other } = params;
<CustomComponent type='normal' number={2} {...other} />
// 等同于<CustomComponent type='normal' number={2} name='123' title='456' />

对象属性简写

在ES6中允许我们在设置⼀个对象的属性的时候不指定属性名。
不使⽤ES6

const name = 'Ming',
age = '18',
city = 'Shanghai';
const student = {
name: name,
age: age,
city: city
};
console.log(student);

对象中必须包含属性和值,显得⾮常冗余。
使⽤ES6

const name = 'Ming',
age = '18',
city = 'Shanghai';
const student = {
name,
age,
city
};
console.log(student);

对象中直接写变量,⾮常简洁。

Promise

Promise 是异步编程的⼀种解决⽅案,⽐传统的解决⽅案callback更加的优雅。它最早由社区提出和实现的,ES6 将其写进了语⾔标准,统⼀了⽤法,原⽣提供了Promise对象。
不使⽤ES6嵌套两个setTimeout回调函数:

setTimeout(function() {
console.log('Hello');
setTimeout(function() {
console.log('Hi');
},
1000);
},
1000)

使⽤ES6

var waitSecond = new Promise(function(resolve, reject) {
setTimeout(resolve, 1000);
});
waitSecond.then(function() {
console.log("Hello");
return waitSecond;
}).then(function() {
console.log("Hi");
});

上⾯的的代码使⽤两个then来进⾏异步编程串⾏化,避免了回调地狱

⽀持let与const

在之前JS是没有块级作⽤域的,const与let填补了这⽅便的空⽩,const与let都是块级作⽤域。
使⽤var定义的变量为函数级作⽤域:

{ var a = 10;}
console.log(a);

使⽤let与const定义的变量为块级作⽤域

{ let a = 10;}
console.log(a);

ESNext新增API

ES7新特性(2016)

ES2016添加了两个⼩的特性来说明标准化过程:

  • 数组includes()⽅法,⽤来判断⼀个数组是否包含⼀个指定的值,根据情况,如果包含则返回true,
    否则返回false。
  • a ** b指数运算符,它与 Math.pow(a, b)相同。

Array.prototype.includes()

includes() 函数⽤来判断⼀个数组是否包含⼀个指定的值,如果包含则返回 true,否则返回 false。
includes 函数与 indexOf 函数很相似,下⾯两个表达式是等价的:

arr.includes(x) arr.indexOf(x) >= 0 

接下来我们来判断数字中是否包含某个元素:
在ES7之前的做法
使⽤ indexOf()验证数组中是否存在某个元素,这时需要根据返回值是否为-1来判断:

let arr = ['react', 'angular', 'vue'];
if (arr.indexOf('react') !== -1) {
console.log('react存在');
}

使⽤ES7的includes()
使⽤includes()验证数组中是否存在某个元素,这样更加直观简单:

let arr = ['react', 'angular', 'vue'];
if (arr.includes('react')) {
console.log('react存在');
}

指数操作符

在ES7中引⼊了指数运算符 **, **具有与 Math.pow(…)等效的计算结果。
不使⽤指数操作符
使⽤⾃定义的递归函数calculateExponent或者Math.pow()进⾏指数运算:

function calculateExponent(base, exponent) {
if (exponent === 1) {
return base;
} else {
return base * calculateExponent(base, exponent - 1);
}
}
console.log(calculateExponent(2, 10));
console.log(Math.pow(2, 10));

使⽤指数操作符
使⽤指数运算符**,就像+、-等操作符⼀样:

console.log(2**10); 

ES8 新特性(2017)

  • async/await;
  • Object.values();
  • Object.entries();
  • String padding: padStart()和 padEnd(),填充字符串达到当前⻓度;
  • 函数参数列表结尾允许逗号;
  • Object.getOwnPropertyDescriptors();
  • ShareArrayBuffer和 Atomics对象,⽤于从共享内存位置读取和写⼊;

async/await

ES2018引⼊异步迭代器(asynchronous iterators),这就像常规迭代器,除了 next()⽅法返回⼀个
Promise。因此 await可以和 for…of循环⼀起使⽤,以串⾏的⽅式运⾏异步操作。例如

async function process(array) {
for await(let i of array) {
doSomething(i);
}
}

Object.values()

Object.values()是⼀个与 Object.keys()类似的新函数,但返回的是Object⾃身属性的所有值,不包括继承的值。
假设我们要遍历如下对象 obj的所有值

const obj = {a: 1, b: 2, c: 3};

不使⽤Object.values() :ES7

const vals = Object.keys(obj).map(key = >obj[key]);
console.log(vals);

使⽤Object.values() :ES8

const values=Object.values(obj1);console.log(values);

从上述代码中可以看出 Object.values()为我们省去了遍历key,并根据这些key获取value的步骤。

Object.entries()

Object.entries()函数返回⼀个给定对象⾃身可枚举属性的键值对的数组。
接下来我们来遍历上⽂中的 obj对象的所有属性的key和value:
不使⽤Object.entries() :ES7

Object.keys(obj).forEach(key = >{
console.log('key:' + key + ' value:' + obj[key]);
})
//key:b value:2

使⽤Object.entries() :ES8

for (let[key, value] of Object.entries(obj1)) {
console.log(`key: $ {
key
}
value: $ {
value
}`)
}
//key:b value:2

String padding

在ES8中String新增了两个实例函数 String.prototype.padStart和 String.prototype.padEnd,允许将空
字符串或其他字符串添加到原始字符串的开头或结尾。

String.padStart(targetLength,[padString])

  • targetLength:当前字符串需要填充到的⽬标⻓度。如果这个数值⼩于当前字符串的⻓度,则返回当
    前字符串本身;
  • padString:(可选)填充字符串。如果字符串太⻓,使填充后的字符串⻓度超过了⽬标⻓度,则只保留
    最左侧的部分,其他部分会被截断,此参数的缺省值为 " ";
console.log('0.0'.padStart(4, '10')) console.log('0.0'.padStart(20))

String.padEnd(targetLength,padString])

  • targetLength:当前字符串需要填充到的⽬标⻓度。如果这个数值⼩于当前字符串的⻓度,则返回当
    前字符串本身。
  • padString:(可选) 填充字符串。如果字符串太⻓,使填充后的字符串⻓度超过了⽬标⻓度,则只保留
    最左侧的部分,其他部分会被截断,此参数的缺省值为 " ";
console.log('0.0'.padEnd(4,'0')) console.log('0.0'.padEnd(10,'0'))

函数参数列表结尾允许逗号

主要作⽤是⽅便使⽤git进⾏多⼈协作开发时修改同⼀个函数减少不必要的⾏变更。

Object.getOwnPropertyDescriptors()

Object.getOwnPropertyDescriptors() 函数⽤来获取⼀个对象的所有⾃身属性的描述符,如果没
有任何⾃身属性,则返回空对象。
函数原型:

Object.getOwnPropertyDescriptors(obj)

返回 obj对象的所有⾃身属性的描述符,如果没有任何⾃身属性,则返回空对象。

const obj2 = {
name: 'Jine',
get age() {
return '18'
}
};
Object.getOwnPropertyDescriptors(obj2)
// age: {
// enumerable: true,
// set: undefined
// name: {
// enumerable: true,
// writable:true
// }

SharedArrayBuffer对象

SharedArrayBuffer 对象⽤来表示⼀个通⽤的,固定⻓度的原始⼆进制数据缓冲区,类似于 ArrayBuffer对象,它们都可以⽤来在共享内存(shared memory)上创建视图。与 ArrayBuffer 不同的是,
SharedArrayBuffer 不能被分离。

new SharedArrayBuffer(length) 

babel的使⽤

babel介绍

官⽹:https://www.babeljs.cn/

Babel 是⼀个⼯具链,主要⽤于将采⽤ ECMAScript 2015+ 语法编写的代码转换为向后兼容的JavaScript 语法,以便能够运⾏在当前和旧版本的浏览器或其他环境中。下⾯列出的是 Babel 能为你做的事情:

  • 语法转换;
  • 通过 Polyfill ⽅式在⽬标环境中添加缺失的特性 (通过引⼊第三⽅ polyfill 模块,例如 core-js);
  • 源码转换(codemods);
// Babel 输⼊: ES2015 箭头函数
[1, 2, 3].map(n => n + 1);
// Babel 输出: ES5 语法实现的同等功能
[1, 2, 3].map(function(n) {
return n + 1;
});

ES2015 及更新版本

Babel 通过语法转换器来⽀持新版本的 JavaScript 语法。
这些 插件 让你现在就能使⽤新的语法,⽆需等待浏览器的⽀持

JSX 与 React

Babel 能够转换 JSX 语法!通过和 babel-sublime ⼀起使⽤还可以把语法⾼亮的功能提升到⼀个新的⽔平。
通过以下命令安装此 preset

npm install --save-dev @babel/preset-react

并将 @babel/preset-react 添加到你的 Babel 配置⽂件中。

export default function DiceRoll(){
const [num, setNum] = useState(getRandomNumber());
const getRandomNumber = () => {
return Math.ceil(Math.random() * 6);
};
return (
<div>
Your dice roll:
{num}
</div>
);
};

类型注释 (Flow 和 TypeScript)

Babel 可以删除类型注释。
通过以下命令安装 flow preset

npm install --save-dev @babel/preset-flow
// @flow
function square(n: number): number {
return n * n;
}

或通过以下命令安装 typescript preset

npm install --save-dev @babel/preset-typescript
function Greeter(greeting: string) {
this.greeting = greeting;
}

插件化

Babel 构建在插件之上。使⽤现有的或者⾃⼰编写的插件可以组成⼀个转换管道。通过使⽤或创建⼀个preset 即可轻松使⽤⼀组插件。
可以使⽤ generator-babel-plugin ⽣成⼀个插件模板

// ⼀个插件就是⼀个函数
export default function({ types: t }) {
return {
visitor: {
Identifier(path) {
let name = path.node.name; // 反转字符串: JavaScript -> tpircSavaJ
path.node.name = name
.split("")
.reverse()
.join("");
},
},
};
}

可调试

由于 Babel ⽀持 Source map,因此你可以轻松调试编译后的代码

符合规范

Babel 尽最⼤可能遵循 ECMAScript 标准。不过,Babel 还提供了特定的选项来对标准和性能做权衡。

代码紧凑

Babel 尽可能⽤最少的代码并且不依赖太⼤量的运⾏环境。
有些情况是很难达成的这⼀愿望的,因此 Babel 提供了 “assumptions” 选项,⽤以在符合规范、⽂件⼤⼩和编译速度之间做折中。

babel的使⽤

概览

整个配置过程包括:

  1. 运⾏以下命令安装所需的包(package):
npm install --save-dev @babel/core @babel/cli @babel/preset-env 
  1. 在项⽬的根⽬录下创建⼀个命名为 babel.config.json 的配置⽂件(需要 v7.8.0 或更⾼版本),并将
    以下内容复制到此⽂件中:
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"edge": "17",
"firefox": "60",
"chrome": "67",
"safari": "11.1"
},
"useBuiltIns": "usage",
"corejs": "3.6.5"
}
]
]
}

如果你使⽤的是 Babel 的旧版本,则⽂件名为 babel.config.js 。

const presets = [
[
"@babel/preset-env",
{
targets: {
edge: "17",
firefox: "60",
chrome: "67",
safari: "11.1",
},
useBuiltIns: "usage",
corejs: "3.6.4",
},
],
];
module.exports = { presets };
  1. 运⾏此命令将 src ⽬录下的所有代码编译到 lib ⽬录:
./node_modules/.bin/babel src --out-dir lib

CLI 命令⾏的基本⽤法

你所需要的所有的 Babel 模块都是作为独⽴的 npm 包发布的,并且(从版本 7 开始)都是以 @babel 作为冠名的。这种模块化的设计能够让每种⼯具都针对特定使⽤情况进⾏设计。 下⾯我们着重看⼀下 @babel/core 和 @babel/cli 。

核⼼库

Babel 的核⼼功能包含在 @babel/core 模块中。通过以下命令安装:

npm install --save-dev @babel/core

你可以在 JavaScript 程序中直接 require 并使⽤它:

const babel = require("@babel/core"); babel.transformSync("code", optionsOb
ject);
CLI 命令⾏⼯具

@babel/cli 是⼀个能够从终端(命令⾏)使⽤的⼯具。下⾯是其安装命令和基本⽤法:

npm install --save-dev @babel/core @babel/cli
./node_modules/.bin/babel src --out-dir lib

这将解析 src ⽬录下的所有 JavaScript ⽂件,并应⽤我们所指定的代码转换功能,然后把每个⽂件输出到 lib ⽬录下。由于我们还没有指定任何代码转换功能,所以输出的代码将与输⼊的代码相同(不保留原代码格式)。我们可以将我们所需要的代码转换功能作为参数传递进去。

插件和预设(preset)

代码转换功能以插件的形式出现,插件是⼩型的 JavaScript 程序,⽤于指导 Babel 如何对代码进⾏转
换。你甚⾄可以编写⾃⼰的插件将你所需要的任何代码转换功能应⽤到你的代码上。例如将 ES2015+ 语法转换为 ES5 语法,我们可以使⽤诸如 @babel/plugin-transform-arrow-functions 之类的官⽅插件:

npm install --save-dev @babel/plugin-transform-arrow-functions

./node_modules/.bin/babel src --out-dir lib --plugins=@babel/plugin-transfo
rm-arrow-functions

现在,我们代码中的所有箭头函数(arrow functions)都将被转换为 ES5 兼容的函数表达式了:

const fn = () => 1; // converted to var fn = function fn() { return 1; };

但是我们的代码中仍然残留了其他 ES2015+ 的特性,我们希望对它们也进⾏转换。我们不需要⼀个接⼀个地添加所有需要的插件,我们可以使⽤⼀个 “preset” (即⼀组预先设定的插件)。
就像插件⼀样,你也可以根据⾃⼰所需要的插件组合创建⼀个⾃⼰的 preset 并将其分享出去。J对于当前的⽤例⽽⾔,我们可以使⽤⼀个名称为 env 的 preset。

npm install --save-dev @babel/preset-env
./node_modules/.bin/babel src --out-dir lib --presets=@babel/env

如果不进⾏任何配置,上述 preset 所包含的插件将⽀持所有最新的 JavaScript (ES2015、ES2016
等)特性。但是 preset 也是⽀持参数的。我们来看看另⼀种传递参数的⽅法:配置⽂件,⽽不是通过终端控制台同时传递 cli 和 preset 的参数。

配置

根据你的需要,可以通过⼏种不同的⽅式来使⽤配置⽂件。另外,请务必阅读我们关于如何 配置 Babel的深⼊指南以了解更多信息。
现在,我们⾸先创建⼀个名为 babel.config.json 的⽂件(需要 v7.8.0 或更⾼版本),并包含如下内容:

{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"edge": "17",
"firefox": "60",
"chrome": "67",
"safari": "11.1"
}
}
]
]
}

现在,名为 env 的 preset 只会为⽬标浏览器中没有的功能加载转换插件。

babel在线使⽤

针对ES6语法,可以查看以下链接

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值