ES6学习笔记

 

一、块级作用域绑定

1、在函数作用域或全局作用域中使用var声明的变量,都会被提升至当前作用域顶部

2、块级声明:

用于声明在指定块的作用域之外无法访问的变量

块级作用域存在于:

1)、函数内部

2)、{}之间的区域

3、同一个作用域中不能用let、const声明已经存在的标识符,会 导致语法错误

4、使用const声明的是常量,其值必须进行初始化,并且不可更改,如果常量是对象,对象中的值可以修改

5、js在扫描代码发现变量声明时:

1)、如果是var :提升至作用域顶部

2)、如果是let或const:先放至TDZ(临时死区),访问TDZ中的变量会触发运行时错误

6、在for-in,for-of,循环中let和const每次迭代都会创建新得绑定值

7、在for循环中,let会创建新的绑定值,但const在for循环声明则可能引发报错

8、当var被用于全局作用域时,会创建一个新的全局变量作为全局对象的属性,即:var很可能会无意中覆盖一个已经存在的全局属性

9、在全局作用域中使用let或const,会在全局作用域下创建一个新的绑定,但不会添加为全局对象额属性

二、字符串与正则表达式

1、codePointAt():字符串转编码,此方法接受编码单元的位置而非字符位置作为参数

String.fromCodePoint():编码转字符串

2、normalize():标准化字符串,接受:‘NFC’、‘NFD’、‘NFKC’、‘NFKD’为参数

3、正则表达式u修饰符:从编码单元操作模式切换为字符模式

正则表达式y修饰符:

会影响正则表达式搜索过程中的sticky属性,当在字符串中开始字符匹配时,会通知搜索从正则表达式的lastIndex属性开始进行,如果在指定位置没能匹配成功,则停止匹配。

y修饰符会把上次匹配后面一个字符的索引保存在lastIndex中,如果该操作匹配的结果为空,则lastIndex会被重置为0

4、字符串中的子串识别

includes():检测是否包含指定文本

startWith():在起始位置检测是否包含该文本

endWith():在结束位置检测是否包含该文本

以上3个方法都接受两个参数:第一个参数指定要搜索的文本,第二个参数可选,指定一个开始搜索的位置的索引值。

ps:endWith方法则从这个索引值减去欲搜索文本的长度,开始正向匹配

5、repeat():该方法接受一个Number类型的数字,表示将字符串重复的次数

6、source属性:返回正则表达式的文本

flags属性:返回应用于当前正则表达式的修饰符

7、模板字面量

1)、ES6用反撇号代替双引号表示字符串

2)、ES5通常依靠数组或字符串拼接的方法实现多行字符串显示,ES6只需在代码中直接换行(或用\n),即可同步显示换行到结果中,但需要注意缩进,因为反撇号中的所有空白符都属于字符串中的一部分

8、字符串占位符:${},可以嵌套,通过模板字面量可以实现字符串拼接功能

9、标签

1)、标签:模板字面量第一个反撇号(`)前面标注的字符串

2)、标签可以是一个函数,

function tag(literals,...substitutions){

}

第一个参数是一个数组,包含js解释过后的字面量字符串;它之后的所有参数都是每一个占位符的解释值。substitutions的值总是比literals少一个

3)、String.raw()标签:返回转义前的原生字符

三、函数

1、js中无论在函数定义中声明了多少形参,都可以传入任意数量的参数,对于函数的命名参数,如果不显示传值,则其值默认为undefined。

2、ES6中如果没为参数传入值或传入undefined,则可为其提供一个初始值,可以为任意参数指定默认值,初始值可以通过函数执行来得到,在引用参数默认值时,先定义的参数不能访问后定义的参数,在已指定默认值的参数后可以继续声明无默认值参数。null是一个合法的传入值

3、ES5非严格模式下,arguments对象会随参数的变化而变化;严格模式下不变。ES6无论严格与非严格模式下,arguments对象都不会碎参数的变化而变化。未传入参数时,其默认值为undefined

4、不定参数...

不定参数为一个数组,包含着自它之后传入的所有参数,通过此数组名即可逐一访问里面的参数

1)、每个参数最多只能声明一个不定参数,而且一定要放在所有参数的末尾

2)、不定参数不能用于对象字面量setter之中

3)、不论是否使用不定参数,arguments对象总是包含所有传入函数的参数

5、增强的Function函数

1)、Function构造函数接受字符串形式的参数,分别为函数的参数及函数体

2)、支持在创建函数时定义默认参数和不定参数,默认参数需用=添加默认值,不定参数使用...

6、展开运算符

1)、使用展开运算符可向Math.max(),函数中传入一个数组,再在数组前添加不定参数中使用的...符号。eg:Math.max(...arr);

2)、展开运算符可以与其他正常传入的参数混合使用,eg:Math.max(...arr,0);

7、js有两个不同的内部方法:Call与Construct。通过new关键字调用函数,执行的是Construct,不通过new调用的函数,执行的是Call

8、ES6中new.target属性可以用来判断该函数是否通过new关键字调用

9、块级函数

1)、ES5不支持在代码块中声明一个块级函数

2)、ES6支持在代码块中声明一个块级函数

1】、在严格模式下:块级函数会被提升至块的顶部

2】、在非严格模式下:块级函数会被提升至外围函数或全局作用域的顶部

四、箭头函数

1、箭头函数没有this、super、arguments和new.target绑定,箭头函数中的this、super、arguments和new.target值均由外围最近一层非箭头函数决定

2、箭头函数不能通过new关键字调用

3、箭头函数没有原型

4、当箭头函数只有一个参数时,可以直接写参数名,箭头紧随其后

5、如果要传入两个或两个以上的参数,要在参数的两侧添加一对小括号

6、如果函数没有参数,也要在声明的时候写一组没有内容的小括号

7、如果想创建一个空函数,需要写一对没有内容的花括号 ()=》{}

8、如果想让函数向外返回一个对象字面量,需要将该字面量包裹到小括号内

9、箭头函数始终可以访问外围函数的arguments对象

10、尾调用指的是:函数作为另一个函数的最后一条语句被调用

尾调用优化可以邦之函数保持一个更小的调用栈,从而减少内存的使用,避免栈溢出错误

1)、尾调用不访问当前帧栈的变量

2)、在函数内部,尾调用是最后一条语句

3)、尾调用的结果作为函数值返回

五、扩展对象的功能性

1、ES6当对象的属性和本地变量同名时,不必再写冒号和值,简单的只写属性名即可

function createPerson(name,age){

return {

name,

age

};

}

2、ES6中为对象字面量添加函数可以消除冒号和function关键字

var person = {

sayName(){

return this.name;

}

}

3、ES6中,在对象字面量中可以使用外部定义的变量做属性名称,但需要用【】表示

let lastName = “last name”;

let person = {

[lastName]:"Zakas";

}

4、新增方法

1】、Objet.is():弥补===的不准确运算

Object.is(+0,-0)//false

Object.is(NaN,NaN)//true

2】、Object.assign();浅复制及对象属性的合并,该方法接受一个接收对象和任意数量的源对象

若右多个源对象具有同名属性,则排位靠后的源对象会覆盖排位靠前的。

5、ES6不在检查属性重复性,对于每一组重复属性,都会选取最后一个取值

6、自有属性枚举顺序:数值字在先,字符串键在后,数值键总是按照升序排列,字符串键按照插入的顺序排列,最后加入动态加入的属性

7、通过Object.setPrototypeOf()方法可以任意修改对象的原型,在对象被创建后依然可以修改,该方法接受两个参数:被改变原型的对象,替代第一个参数中原型的对象

8、super相当于指向对象原型的指针,super的所有引用都通过[[HomeObject]]属性来确定后续的运行过程

六、解构

定义:将一个数据解构分解为更小的部分的过程,解构能够从对象或者数组里取出数据存为变量

1、对象解构:

1】、let node = {

type:"Identifier",

name:""foo

};

let { type, name} = node;

2】、使用let、var、const解构声明变量,必须提供初始化程序,即初始化等号后面的对象

3】、使用解构语法可以为多个变量赋值:({ type, name} = node)

4】、使用的结构赋值表达式时,如果指定的局部变量名称在对象中不存在,那么这个局部变量会被赋值为undefined,当指定的属性不存在时,可以随意定义一个默认值,在属性名称后添加等号=与相应的默认值即可:let{ type, name, value= true} = node;

5】、可以使用不同命名的局部变量来存储对象的值:

let { type:localType, name:localName } = nade;//读取type的属性值,并存储进localType变量

6】、解构对象支持嵌套

2、数组结构

1】、解构赋值:当通过let、var、const声明解构数组的绑定时,必须要提供一个初始化程序

let colors = ["red","green","blue"];

let [ firstColor, secondColor ] = colors;

let [ , , thirdColor] = colors;

2】、代替swap函数,交换两个变量的值

let a = 1, b = 2;

[ a, b] = [ b, a];

console.log(a);//2

console.log(b);//1

3】、可以在数组解构赋值表达式中,为数组的任意位置添加默认值

let [ firstColor,secondColor = "green"] = colors;

4】、嵌套解构数组:

let [ firstColor, [ secondColor ] ] = colors;

5】、不定元素:必须作为最后一个条目存在

ES5中,开发者常用concat()方法实现克隆数组,concat()方法设计初衷是连接两个数组,如果调用时不传递参数,就会返回当前数组的副本。

ES6中则可以通过不定元素实现:let [...clonedColors] = colors;

3、解构参数

1】、

function setCookie ( name, value, { secure , path , domain , expires }){

......................;

}

setCookie ("type","js",{

secure:true;

expires:60000

});

2】、默认情况下,如果调用函数时,不提供被解构的参数将导致程序抛出错误,可以为其提供默认值来解决这个问题

function setCookie ( name, value, { secure , path , domain , expires } = { }){

......................;

}

3】、可以为解构参数逐个提供默认值

function setCookie ( name, value,

{

secure = false ,

path = "/" ,

domain = "example.com" ,

expires = new Date(Date.now()+360000000)

}

){

......................;

}

4】、3】中如果调用setCookie ("type","js");会导致程序跑出错误

const setCookieDefaults = {

secure = false ,

path = "/" ,

domain = "example.com" ,

expires = new Date(Date.now()+360000000)

}

function setCookie ( name, value,

{

secure = setCookieDefaults.secure ,

path = setCookieDefaults.path ,

domain = setCookieDefaults.domain ,

expires = setCookieDefaults.expires

} = setCookieDefaults

){

......................;

}

七、Set集合

1、Set集合常被用于检查对象中是否存在某个键名,而Map集合常被用于获取已存在的信息

2、对象属性的键名必须是字符串,而Set与Map的键名可以是任意类型。

3、Set是一种有序、唯一的列表,其常用方法包含:

    has():检测Set集合中是否存在某个值

    add():向Set集合中添加元素

     delete():从Set集合中删除某个元素

     clear():清空Set集合中的全部元素

     forEach(function(参数1,参数2,参数3){

         。。。。。。。

})

//参数1:Set集合中下一次索引的位置,参数2:与第一个参数一样的值,参数3:被遍历的Set集合本身

let set = new Set([1,2]);

set.forEach(function(value,key,ownerSet){

    console.log(key+" "+value);

});//1,1 2,2

4、Set集合转数组,有去重效果,使用...展开福

eg:function setDelRepeat(arr){

                return [...new Set(arr)];

              }

        setDelRepeat([1,2,2,3,5,5,5]);//[1,2,3,5]

5、WeakSet,弱Set

       1】、弱Set只存储对象的弱引用,集合中的弱引用如果是对象唯一的引用,则会被回收并释放相应内存,避免内存泄漏  

       2】、弱Set的构造函数的参数仅支持对象类型,不支持原始类型,会报错

     3】、弱Set支持:has()、delete()、add()方法

     4】、弱Set不支持:size属性、forEach方法、不可迭代不能使用for-of循环

八、Map集合

1、Set集合常被用于检查对象中是否存在某个键名,而Map集合常被用于获取已存在的信息

2、对象属性的键名必须是字符串,而Set与Map的键名可以是任意类型。

3、Map键名的等价性判断通过Object.is()实现,5与“5”不一样

4、Map常用方法:

        set(key,value):向集合添加新的元素

        get(key):获取集合中对应键的值,如果该键在集合中不存在,则返回undefined

        has(key):检测指定的键名在集合中是否存在

        delete(key):从集合中移除指定键名及其对应的值

        clear():移除集合中所有的键值对

        forEach(function(参数1,参数2,参数3){

            .............................

        });

        //参数1:集合中下一次索引的位置,参数2:值对应的键名,参数3:Map集合本身

        let map = new Map([["name","nike"],["age",19]]);

               map.forEach(function(value,key,ownerMap){

                console.log(key+" "+value);

        });

        //name nike

     //age 19

5、Map支持批量添加数据,初始化Map使用数组包裹数组的方式

        eg: let map = new Map([["name","nike"],["age",19]]);

6、WeakMap 弱Map

        1】、弱Map集合中的键名必须是一个非null对象,值可以是任意类型,无序

        2】、弱Map集合中保存的是这些对象的弱引用,如果在弱引用之外不存在其他的强引用,引擎的垃圾回收机制会自动回收这个对象,同时也会移除集合中的键值对,但如果只有键遵循这个规则,而键名对应的值如果是一个对象,则保存的是对象的强引用,不会触发垃圾回收机制。

        3】、弱Map支持:has()、delete()、set()、get()

        4】、弱Map不支持:size属性、键名枚举、clear()

7、WeakMap与Map的适用场景

        WeakMap:只用对象作为键名的集合

        Map:非常需要使用size属性、clear()、forEach方法

九、迭代器和生成器

1、迭代器:是一种特殊对象,它具有一些专门为迭代过程设计的专有接口,所有的迭代器对象都有一个next()方法,每次调用都返回一个结果对象。结果对象有两个属性,一个是value:表示下一个将要返回的值;另一个是done,在没有更多可返回数据时,返回true。

2、生成器:是一种返回迭代器的函数,通过function关键字后的星号(*)来表示,使用yield关键字暂停

3、yield:每当执行完一条yield语句后,函数就会自动停止;yield关键字可以返回任何值或表达式;yield关键字只能在生成器内部使用,在其他地方使用, 会导致程序抛出错误,即使是在生成器内部的其他函数

4、创建生成器的两种方法(不能用箭头函数创建生成器):

    1】、function *fun(){}

    2】、函数表达式:let fun = function *(items){}

5、Symbol是ES6中新增的一种原始数据类型,表示独一无二的值,其最大的用途是用来定义对象的唯一属性名,通过Symbol.iterator可以访问对象的默认迭代器。

        使用Symbol.iterator可以检测对象是否为可迭代对象

        function isIterable(obj){

            treturn typeof obj[Symbol.iterator] === "function";    

    }

6、默认情况下,开发者定义的对象都是不可迭代对象,但利用Symbol.iterator可将其变为可迭代对象

        let collection = {

            items :[],

            *[Symbol.iterator](){

                for(let item of items){

                    yield item;

                }

            }

        };

7、集合对象内建迭代器

        entries():返回一个迭代器,其值为多个键值对,Map默认

        values():返回一个迭代器,其值为集合的值,数组和Set默认

        keys():返回一个迭代器,其值为集合中的所有键名

8、ES5规定可以使用[]访问字符串中的字符

9、Dom标准中有一个NodeList类型,代表页面文档中所有元素的集合,ES6中NodeList也拥有了默认迭代器

10、高级迭代器功能

        1】、给迭代器传递参数:在next()方法中传递参数,则这个参数的值将会替代生成器内部上一条yield语句的返回值

              2】、在迭代器中抛出错误,next和throw就像是生成器的两条指令,调用next方法命令迭代器继续执行,调用throw方法也会命令迭代器继续执行,但同时也会抛出一个错误                       

           3】、生成器返回语句:生成器中的return表示所有操作已经完成,属性done被置为true,如果同时提供了相应的值,则属value会被设置为这个值。return后面的yield语句将不再执行。展开运算符与for-of循环语句会直接忽略return语句指定的任何返回值,只要done为true,就立刻停止读取其他的值。

            4】、委托生成器,使用yield语句添加*,将两个迭代器合二为一

 

                           

11、异步任务执行

        ES5实现异步:回调函数或者setTimeout();

        ES6实现异步:通过生成器

               

 

十、js中的类

1、使用class关键字定义一个类,使用constructor关键字创建构造函数,自有属性只能在类的构造函数中创建。

2、类与自定义类型之间的区别:

        1】、函数声明可以被提升,而类声明与let声明类似,不能被提升;真正执行类声明语句之前,它们会一直存在于临时死区

        2】、类声明中的所有代码将自动运行在严格模式下

        3】、在自定义类型中,通过Object.defineProperty()方法手动指定某个方法不可枚举,而类中所有方法都不可枚举

        4】、每个类都有一个名为[[Construct]]的内部方法,通过关键字new调用不含[[Construct]]的方法会报错

        5】、只能使用new关键字调用类的构造函数

        6】、类内类名不可更改

3、类的两种存在形式:声明形式、表达式形式

        声明形式:class Person{}

        表达式形式:let PersonClass = class Person{}//Person只能在内部使用

4、可以通过变量给类定义的方法命名:      

5、在类中可以定义生成器

6、使用static定义静态成员,必须直接使用类名访问静态成员,不可以在实例中访问静态成员

7、通过extends及super关键字实现继承

        1】、继承时,子类中如果指定了构造函数,则必须调用super;如果选择不使用构造函数,则当创建新的类实例时会自动调用super并传入所有参数

        2】、只能在存在继承关系的子类的构造函数中调用super

        3】、js可以实现多继承       

        4】、如果父类中有静态成员,那么子类也可以用

        5】、使用Symbol.species可以定义当子类的方法返回实例时,应该返回的类型

               

8、子类方法总是会覆盖父类中的同名方法,如果需要调用父类的该方法,可以使用super.mains();

9、在类的构造函数中,可以通过new.target属性来随着类被调用的多种方式而做出不同的对于,最常见的是创建一个抽象父类,如果直接实例化这个类会抛出错误,但是可以通过其他的类去实例化它。

                   

十一、改进的数组功能

1、新增方法    

    1】、Array.of():不论参数是什么类型,总会创建一个包含所有参数的数组

    2】、Array.from():

            接收可迭代对象或类数组对象作为第一个参数,最终返回一个数组;

            如果想要进一步转化数组,可以提供一个映射函数作为Array.from()的第二个参数;

            如果用映射函数处理对象,也可以给Array.from()方法传入第三个参数来表示映射函数的this值。

            可以处理类数组对象和可迭代对象,也就是说该方法能够将所有含有Symbol.iterator属性的对象转换为数组

            eg1:

                let helper = {

                    diff :1,

                    add(value){

                        return value + this.diff;

                    }

                };

                function translate(){

                        return Array.from(arguments,helper.add,helper);

                }

                console.log(translate(1,2,3));//2,3,4

            eg2:

                let numbers = {

                    *[Symbol.iterator](){

                        yield 1;

                        yield 2;

                        yield 3;

                    }

                };

                let numbers = Array.from(numbers,(value)=>value+1);

                console.log(numbers);//2,3,4

   3】、find(回调函数,指定函数中的this值(可选参数)):返回查找到的值

   4】、findIndex(回调函数,指定函数中的this值(可选参数)):返回查找到的值的索引

       let numbers = [20,25,66,85,9];

       console.log(numbers.find(n=>n>33));//66

       console.log(numbers.findIndex(n=>n>33));//2

       ps:find/findIndex一旦回调函数返回true,会立刻停止搜索数组剩余的部分

    5】、fill(value,开始索引(可选),结束索引(可选)):用指定的值填充一个或多个数组元素

            eg:let numbers = [1,2,3,4];

                    console.log(numbers,fill(0,1,3).toString());//1,0,0,1

    6】、copyWithin(开始填充值的索引位置,开始复制值的索引位置,限制被重写元素的数量(可选)):从数组中复制元素的值

            eg:let numbers = [1,2,3,4];

                    console.log(numbers,copyWithin(2,0).toString());//1,2,1,2

            默认情况下,copyWithin会一直复制直到数组末尾的值。

            fill、copyWithin的所有参数都接受负数值,并且会自动与数组长度相加来作为最终使用的索引

2、定型数组

     1】、定型数组:一种用于处理数值类型数据的专用数组

     2】、数组缓冲区:一段可以包含特定数量字节的内存地址,let buffer = new ArrayBuffer(10);

     3】、视图:用来操作内存的接口,DataView类型是一种通用的数组缓冲区视图

                let buffer = new ArrayBuffer(10);

                view = new DataView(buffer);

                new DataView(buffer,byteOffset,byteLength)

    4】、ES6中的特定类型视图:定型数组是视图                                                  Int8Array,Uint8Array,Uint8ClampedArray,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array,

    5】、创建定型数组的方法:

            1)、传入DataView构造函数可接受的参数来创建新的定型数组,分别为:数组缓冲区、可选的比特偏移量、可选的长度值

                        let buffer = new ArrayBuffer(10),

                              view1 = new Int8Array(buffer,5,2);

            2)、调用构造函数时传入一个数字

                        let ints = new Int16Array(2);

            3)、调用构造函数时传入:定型数组、可迭代对象、数组、类数组对象

    6】、定型数组和数组的相似之处:

            1)、均支持不改变数组长度的基本方法

            2)、展开运算符能够将可迭代对象转换为普通数组,也能将定型对象转换为普通数组

            3)、可通过Symbol.species确认方法的返回值是定型数组而非普通数组

    7】、定型数组与普通数组的差别

            1)、定型数组不是普通数组,它不继承自Array

            2)、定型数组不可改变数组大小,普通数组传入未知索引会改变大小

            3)、对于会改变定型数组长度的方法均不可用,如push

            4)、新增方法set():将其他数组复制到已有的定型数组,subarray():提取已有定型数组的一部分作为一个新的定型数组

十二、Promise

Promise是异步编程的一种解决方案,比传统的解决方案-回调函数和事件更合理和更强大。所谓Promise简单来说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果,从语法上说,Promise是一个对象,从它可以获取异步操作的消息,Promise对象的状态不受外界影响。

 

三种状态:

        pengding: 进行中

        fulfilled:    已经成功

        rejected:   已经失败 

 

状态改变:Promise对象的状态改变,只有两种可能:这两种情况只要发生,状态就凝固了,这时就成为resolved(已定型)

        从pengding变为fulfilled

        从pengding变为rejected

基本用法:ES6规定,Promise对象是一个构造函数,用来生成Promise实例

const promise = new Promise(function(resolve,reject){ 

    if(/*异步操作成功*/)

    {

      resolve(value);

    }

    else

    {

      reject(error);

    }

});

 

resolve的作用是把Promise对象的状态从未完成变为成功,在异步操作成功时调用,并将异步操作的结果,作为参数传递出去

 

reject的作用是把Promise对象的状态从未完成变成失败,在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去

Promise实例生成以后,可以用then方法分别指定resolve状态和resolve的回调函数

promise.then(function(value){

    //success },function(error){

    //failure 

});

 

例子:

function timeout(ms){

    return new Promise((resolve,reject)=>{

    setTimeout(resolve,ms,'done');

     });

}

timeout(100).then((value)=>{

    console.log(value);

});

 

let promise = new Promise(function(resolve,reject){ 

    console.log('Promise');

    resolve();

});

promise.then(function(){

    console.log('resolved');

});

console.log('Hi!');

//Promise

//Hi! 

//resolved

 

//异步加载图片 

function loadImageAsync(url){ 

    return new Promise(function(resolve,reject){

    const image = new Image();

    image.onload = function(){ 

        resolve(image);

    };

    image.onerror = function(){ 

        reject(new Error('error');

   };

    image.src = url;

   });

}

 

下面是一个用Promise对象实现的 Ajax 操作的例子。

const getJSON = function(url) { 

const promise = new Promise(function(resolve, reject){

    const handler = function() { 

        if (this.readyState !== 4) {

            return;

        } 

        if (this.status === 200) {

             resolve(this.response);

        }else {

            reject(new Error(this.statusText));

        }

    };    

    const client = new XMLHttpRequest();

    client.open("GET", url);

    client.onreadystatechange = handler;

    client.responseType = "json";

    client.setRequestHeader("Accept", "application/json");

    client.send();

    });

    return promise;

};

getJSON("/posts.json").then(function(json) { 

    console.log('Contents: ' + json);

}, function(error) { 

    console.error('出错了', error);

});

 

 

 

 

 

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值