JS基础-3

1.for in

对象中有多少组键值对,我们的FOR IN 循环就遍历多少次(不一定)
每一次循环KEY这个变量存储的都是当前循环这组键对值的属性名
1、KEY存储的值都是字符串格式的(不管属性名是否为数字)
2、在FOR IN 循环遍历的时候,大部分浏览器都是先把对象中的键值对进行排序(把数字属性名的排在前面,并且排列的时候按照数字由小到大排列),其次再把非数字的属性名按照之前编写的顺序排列,循环的时候按照重新排列的顺序依次遍历(小数算作字母不算作数字)

for (var key in obj)  {
console.log(typeof key) "string"
//key 属性名
console.log(obj.key)//获取obj中key这个属性对应的属性值 undefined <==>obj['key'] {‘key’:'key属性的值'}
console.log(obj[key]);每一次循环把key变量存储的值(当前遍历的属性名)获取到放在中括号中,获取OBJ对应属性的属性值
'key':字符串key
key:变量key,代表的是存储的值
}

obj['key']   obj.key  属性名是key
obj[key] 属性名不是key 而是key变量存储的值
复制代码

2. 数据类型转换

2.1 把其他数据类型转换为number类型

A) isNaNNumberparseIntparseFloat
true -->  1  false --> 0 '' --> 0  '12'-->12 '12px' --> NaN/12  '帅哥' --> NaN
null --> 0 undefined --> NaN
{} /$/ function() {} --> NaN
[]
//引用数据类型转换为数字
通过toString方法把数组转化为字符串,然后再调用Number把字符串转换为数字
[]  --> '' -->0  [12]  --> '12' -->12 [12,23] --> '12,23' --> NaN

B)在进行加减乘除数学运算的时候
1、加减乘除
2、除了加法有特殊性,其余的运算符都是数学运算,也就是遇到非数字类型,需要把其转换为number再进行运算
1- '1' = 0    10 * null = 0     10/undefined = NaN .  10 * [10] = 100
加法的特殊性:
在遇到字符串的时候 + 不是数学运算  而是字符串拼接 只要不遇到字符串就是数学运算
1+ '1' = '11'    null + '1' = 'null1'
字符串拼接:把其他值转为字符串然后再拼接 (toString)
其他数据类型的toString是直接把值用单(双)引号包起来即可,只有对象的有特殊性,对象.toString =' [object  object]'
(1).toString --> '1'  (true).toString ---> "true"   null  undefined 都有toString() 方法 但是浏览器不让用
[12,23].toString() "12,23"     /^$/.toString() "/^$/"   (function() {}).toString()  "function() {}"
({name:"hou"}).toString()  "[object object]"

1+ null + undefined + [] +'hou' + null + undefined + []  + 10

1 +null ---> 1   1 + true --->2    2 + undefined ---> NaN   
NaN + [] --->NaN + '' --->"NaN"
"NaN" + 'hou' + 'null' + 'undefined' + '' + '10'---> "NaNhounullundefined10"

复制代码

2.2 将其他数据类型转换为布尔类型

Boolean ! !!
条件判断,也是转换为布尔类型,然后判断真假
只有 0 NaN null undefined 空字符串 五个转换为false 其余都是true
[] ---> true  -1 ---> true
 if (box) {
    首先把box 变量存储的值获取到,转化为布尔类型,如果转化为true条件成立 反之不成立
}

if(3 +'3px')  {
//条件成立  3 + '3px' ---> '33px'  true
}

if(3- '3px')  {
// 条件不成立 3 - '3px' ---> NaN false
}
复制代码

2.3 使用"=="进行比较

在使用"=="进行比较的时候,如果左右两边数据类型不相同,浏览器会默认转换为相同的类型,然后再比较("==="不会这样操作)
对象和对象:比较的是空间地址,不是相同的空间,结果肯定是false
[] == [] false       var a = {} var b = a ; a==b true
对象和数字:把对象转换为数字
[] == 0  true
({}) == NaN false NaN和自己不相等和其他任何值都不相等
对象和字符串:把两边都转换为数字比较的
[] == ''  true
对象和布尔:把两边都转换为数字
[] == true 0==1 false
[] == false 0 ==0 true
![] == false --> 把数组变为布尔再取反 = false --> false == false --> true

字符串和数字:字符串转换为数字
字符串和布尔:都转化为数字
布尔和数字: 布尔转换为数字

规律:两个等于号比较,左右两边数据值的类型不一样,浏览器会把两边的类型都转换为数字然后再比较,但是nullundefined除外
null == undefined --> true
null === undefined ---> false
null以及undefined 和其他任何值都不相等
复制代码

3.Math中的常用方法:

数学函数:但是它是对象数据类型的 typeof Math 'object'
Math对象中给我们提供了很多常用操作数字的方法
abs 取绝对值
ceil 向上取整
floor 向下取整
round 四舍五入 Math.round(12.3) -->12 Math(12.5) ---> 13 正数中5包含在向上
Math.round(-12.3) ---> -12  Math.round(-12.5) ---> -12 负数5 包含在向下    Math.round(-12.51)   -13
random 获取[0,1)之间的随机小数 很难获取到0
获取[0,10]之间的随机整数 Math.round(Math.random()*10)
获取[3,15]之间的随机整数 Math.round(Math.random()*12 + 3)
获取[n,m]之间的随机整数  Math.round(Math.random()*(m-n) + n)
max/min   获取一组值中的最大值和最小值
PI  获取圆周率
pow 获取一个值的多少次幂 Math.pow(10,2) --> 100
sqrt 开平方 Math.sqrt(100) ---> 10
复制代码

4.字符串常用方法:

Jstr.charAt(索引):返回指定索引位置的字符,和str[索引]的区别在于,当指定的索引不存在的时候,中括号的方式获取的是undefined,而charAt获取的是空字符串
str.charCodeAt(索引):在charAt基础上,把获取的字符变为unicode编码值(对应ASCII码表)
48-57:  0-9 
65-90:  A-Z
97-122: a-z
str.fromCharCode(十进制的unicode值):把值按照ASCII码表中的信息,转换为原有的字符,和charCodeAt正好对应
substr && substring && slice
实现字符串截取的三个办法
str.substr(n,m):从索引n开始 截取m个字符
str.substring(n,m):从索引n开始,截取到索引为m处(不包含m  即 m-1)把找到的部分截取
str.slice(n,m):和substring语法一样,区别在于slice支持以负数做索引
当索引是负数的时候,浏览器在处理的时候,是用字符串的总长度加上负数索引,然后按照正数处理操作
str.slice(-7,-3)  ===>    str.slice(str.length-7,str.length-3)
细节:
1、如果只传递了n(str.substr(n)/str.substring(n)),相当于从索引n开始一直截取到字符串的末尾
2、如果传递的索引超出最大限制,也是把能截取的部分截取掉即可
3、如果一个参数都不传递,相当于把整个字符串都截取(字符串的克隆)
str.toUpperCase:把字母全局大写
str.toLowerCase:把字母全局小写
indexOf && lastIndexOf
str.indexOf:获取当前字符在字符串中第一次出现位置的索引
str.lastIndexOf:获取的是当前字符最后一次出现位置的索引
如果当前字符在字符串中没有出现过,结果是-1 :我们根据这个规律可以验证一下当前字符串是否包含某个字符
if(str.indexOf('?') === -1) {
//没有出现过
}

if(str.indexOf('?') >=0) {
//出现过
}
str.split:按照某一个字符把字符串拆分成数组中的某一项,和数组的join方法是对应的
var str = 'name=shuaige&age=8'
str.split('=')
["name","shuaige&age","8"]
str.split(/=|&/g);//支持正则表达式
["name","shuaige","age","8"]
str.split('')
["n", "a", "m", "e", "=", "s", "h", "u", "a", "i", "g", "e", "&", "a", "g", "e", "=", "8"] 
str.split('  ')  不太一样 后边这个有空格 ["name=shuaige&age=8"]
var ary = [12,34,32]
ary.join('+')  "12+34+32" 
"12+34+32".split('+')  ==>  ["12","34","32"]  ary 的元素是数字 变成了字符串
str.replace:实现字符的替换
执行一次replace  只能替换一次 如果我们有好几个需要替换,在不使用正则的情况下我们需要执行很多次replace
有些需求即使执行很多次replace也实现不了,此时需要使用正则处理,真实项目中replace一般都是和正则搭配使用的
trim && trimLeft && trimRight
str.trimLeft:去除字符串开始的空格
str.trimRight:去除字符串结尾的空格
str.trim:去除字符串首尾的空格

字符串应用之queryURLParameter
var url = "https://www.baidu.com/s?wd=node&rsv_spt=1&issp=1"
目标:把问号传递的参数值分别的解析出来
var obj ={wd:'javascript',rsv_spt:1,issp:1}
方案一:
function queryURLParameter(url) {
    //url:传递的参数(我们当前要解析的URL地址)
    var quesIndex = url.indexOf('?')//获取?的索引位置
    var obj = {};
    if(quesIndex === -1) {//url中没有问号传参:直接返回空对象
    return obj;
    }
    url = url.substr(quesIndex+1);//截取了"wd=node&rsv_spt=1&issp=1"  ?后面的
    var ary = url.split('&');//拆分成数组["wd=node","rsv_spt=1","issp=1"]
    for(var i = 0; i < ary.length; i++) {
        var curAry = ary[i].split('='); //["wd","node"]
        obj[curAry[0]] = curAry[1];
    }
    return obj;
}

方案二:待分析
String.prototype.myQueryURLParameter = function myQueryURLParameter()  {
    var obj = {},
    reg = /([^=?&]+)=([^=?&]+)/g;
    this.replace(reg,function() {
    var arg = arguments;
    obj[arg[1]] = arg[2];
    });
    return obj;
}

真实项目中的验证码:
真实项目中的验证码一般都是后台处理的,后台返回给客户端展示的是一个图片(图片中包含了验证码)
1、字母+ 数字
2、问答
312306 选择图片
4、成语填空
5、图片拼图
6、滑动拖拽

letter-spacing:5px 文本字体之间的间距
cursor:pointer;//小手鼠标
函数操作
var codeBox = document.getElementById('codeBox');
==>生成4位随机验证码
var areaStr = "0123456789qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM";
var result = '';
for(var i = 0; i < 4; i++) {
    //==>随机获取一个0-61之间的整数:作为接下来获取字符的索引
    var ran = Math.round(Math.random()*61);
    // =>根据索引获取一个随机字符
    var char = areaStr.charAt(ran);
    //验证一下新获取的CHAR字符是否已经在RESULT中存在了,如果存在了我们不存储,重新再获取一遍,反之才累加到RESULT中
    //不区分大小写  不能同时存在A a  所以比较的时候 需要都转换为小写
    if(result.toLowerCase().indexOf(char.toLowerCase())>-1) {
        i--; // i必须减减 才能重新 获取一遍
        continue;
    }
    // ==>把每一次循环获取的字符存放在最后结果中
    result+=char;
}
codeBox.innerHTML = result;

点击盒子生成4个随机验证码
function queryCode() {.....//生成4个随机数}
    codeBox.onclick = queryCode;
    //点击盒子重新生成验证码(此处不加小括号:这块只是把函数绑定给元素的点击事件,方法还没有执行,点击的时候才执行)
    禁止用户选中文字 ==》-webkit-user-select: none 

// 写代码先写思路 --->从索引上下功夫  比如选择完 就删掉该字符 但是 Math.random()* 61 61 也要随之发生变化
复制代码

5.节点操作

DOM: document object model 文档对象模型,提供一些属性和方法可以让我们去操作DOM元素
获取DOM元素的方法
document.getElementById  一个元素对象
[context].getElementsByTagName 元素集合
[context].getElementsByClassName 通过元素类名获取元素集合
document.getElementsByName 通过name属性值获取节点集合
document ===>说明上下文不能发生变化
[context] ===> 说明上下文可以发生变化
document.documentElement 获取整个HTML对象
document.body 获取整个BODY对象
document.head 获取整个HEAD对象
[context].querySelector 通过选择器获取一个元素对象
[context].querySelectorAll  通过选择器获取元素集合

getElementById ==> 此方法的上下文只能是document
一个HTML页面中元素的ID理论上是不能重复的
1、如果页面中的ID重复了,我们获取的结果是第一个ID对应的元素对象
2、在IE7及更低版本浏览器中,会把表单元素的name值当作id来识别使用(项目中尽量不要让表单的name和其他元素的id相同)
3、如果我们把JS放在结构的下面,我们可以直接使用ID值来获取这个元素(不需要通过getElementsById获取),而且这种方式会把页面中所有ID是他的元素都获取到(元素对象/元素集合) ===> 不推荐
获取页面中ID值为#box1的所有元素标签
var allList = document.getElementsByTagName('*'); 
var result = [];
for(var i = 0 ; i < allList.length; i ++) {
var item = allList[i];
item.id === 'box1' ? result.push(item):null;
}

getElementsByTagName
上下文可以自己来指定
获取到的结果是一个元素集合(类数组集合)
1、获取的结果是集合,哪怕集合中只有一项,我们想要操作这一项(元素对象),需要先从集合中获取出来,然后再操作
var bodyBox = document.getElementsByTagName('body');
bodyBox[0].getElementsByTagName('div');
2、在指定的上下文中,获取所有子子孙孙元素中标签名叫做这个的(后代筛选)

getElementsByClassName
上下文也可以随意指定
获取的结果也是一个元素集合(类数组集合)
1、真实项目中我们经常会通过样式类名来获取元素,getElementsByClassName这个方法在IE6-8浏览器中是不兼容的
getElementsByName
通过元素的Name属性值获取一组元素(类数组:节点集合NodeLIst)
它的上下文也只能是document
IE浏览器只能识别表单元素的name属性值,所以我们这个方法一般都是用来操作表单元素的

document.documentElement/document.body
获取HTML或者BODY(一个元素对象)
document.documentElement.clientWidth || document.body.clientWidth//获取当前浏览器窗口可视区域的宽度(当前页面一屏幕的宽度)
clientHeight是获取高度

querySelector /querySelectorAll ---->性能没有这么好
在IE6-8下不兼容,而且也没有什么特别好的办法处理它的兼容,所以这两个方法一般多用于移动端开发使用
querySelector:获取一个元素对象
querySelectorAll :获取的是一个元素集合
只要是css支持的选择器,这里大部分都支持

document.querySelectorAll('#div1');//根据ID获取所有的元素
document.querySelectorAll('.box');//根绝className获取元素集合
document.querySelectorAll('div');//根据标签获取元素集合
document.querySelectorAll('body>div');//子代选择器 body下的 div
document.querySelectorAll('#box2 li');//后代选择器 
document.querySelectorAll('div [name=xxxxx]');

DOM的节点
node:节点,浏览器认为一个HTML页面中的所有内容都是节点(包括标签、注释、文字文本等)
元素节点:HTML标签
文本节点:文字内容(高版本浏览器会把空格和换行也当作文本节点)
注释节点:注释内容
document文档节点

元素节点
nodeType:1
nodeName: 大写标签名(在部分浏览器的怪异模式下,我们写的标签名是小写,它获取的就是小写)
nodeValue: null
[curEle].tagName:获取当前元素的标签名(获取的标签名一般都是大写)

文本节点 ---  空格 换行  属于文本 节点
nodeType:3
nodeName: #text
nodeValue:文本内容

注释节点
nodeType:8
nodeName: #comment
nodeValue:注释内容

文档节点
nodeType:9
nodeName: #document
nodeValue:null

document.body.childNodes

节点:用来描述页面中每一部分之间关系的,只要我可以获取页面中的一个节点,那么我就可以通过相关的属性和方法获取页面中的所有节点
childNodes:获取当前元素所有子节点(节点集合:类数组)
注:不仅仅是元素子节点,文本、注释等都会包含在内;
子节点说明只是在儿子辈分中查找

children:获取所有的元素子节点(元素集合) 在IE6-8下获取的结果和标准浏览器中有区别(IE6-8中会把注释节点当作元素节点获取到)
parentNode:获取当前元素的父节点(元素对象)
previousSibling nextSibling
previousSibling:获取当前节点的上一个哥哥节点(不一定是元素节点也可能文本或者注释)
nextSibling:获取当前节点的下一个弟弟节点

previousElementSibling nextElementSibling
previousElementSibling:获取当前节点的上一个哥哥元素节点(IE6-8不兼容)
nextElementSibling:获取当前节点的下一个弟弟元素节点(IE6-8不兼容)

firstChild lastChild
firstChild:当前元素所有子节点中的第一个(也不一定是元素节点,可能是文本和注释)
lastChild:当前元素所有子节点中的最后一个

firstElementChild   lastElementChild(IE6-8不兼容)

DOM的增删改
真实项目中,我们偶尔会在JS中动态创建一些HTML标签,然后把其增加到页面中
document.createElement
在JS中动态创建一个HTML标签
容器.appentChild(新元素)
把当前创建的新元素增加到容器的末尾位置
insertBefore
容器.insertBefore(新元素,老元素)
在当前容器中,把新创建的元素增加到老元素之前

//真实项目中很多需求是通过动态创建元素来完成的,其中有一个需求:解析一个URL地址每一部分的信息(包含问号传递的参数)
1、纯字符串拆分截取
2、编写强大的正则,捕获到需要的结果
3、通过动态创建一个A标签,利用A标签的一些内置属性来分别获取每一部分的内容
var link = document.createElement('a');
link.herf = 'http://www.zhufengpeixun.cn/stu/?name=zxt&age=27#teacher';
//此处地址就是我们需要解析的URL
hash:存储的是哈希值 '#teacher'
hostname:存储的是域名 'www.zhufengpeixun.cn'
pathname:存储的是请求资源的路径名称'/stu/'
protocol:协议'http:'
search:存储的是问号传递的参数值,没有传递是空字符串''  ===>'?name=zxt&age=27'

function queryURLParameter(url) {
var link = document.createElement('a');
link.href = url;

var search = link.search,
obj = {};
if(search.lenght === 0)  return;
search = search.substr(1).split(/&|=/g);
//拆分完之后 奇数项是key  偶数项是value ["name","zxt","age","27"]
for(var i = 0; i < search.length; i +=2) {
var key = search[i];
value = search[i+1];
obj[key] = value;
}
link = null;
return obj;
}

removeChild
容器.removeChild(元素)
在当前容器中把某一个元素移除掉
replaceChild
容器.replaceChild(新元素,老元素)
在当前容器中,拿新元素替换老元素
cloneNode
元素.cloneNode(false/true)
把原有的元素克隆一份一模一样的
false:只克隆当前元素本身
true:深度克隆,把当前元素本身以及元素的所有后代都进行克隆
[set/get/remove] Attribute
给当前元素设置/获取/移除 属性的(一般操作的都是它的自定义属性)

box.setAttribute('myIndex',0);
box.getAttribute('myIndex');
box.removeAttribute('myIndex');

使用xxx.index 和xxx.setAttribute('index',0)这两种设置自定义属性的区别
xxx.index:是把当前操作的元素当作一个普通对象,为其设置一个属性名(和页面中的HTML标签没关系)
xxx.setAttribute:把元素当作特殊的对象来处理,设置的自定义属性是和页面结构中的DOM元素映射在一起的

JS中获取的元素对象,我们可以把它理解为两种角色:
与页面HTML结构无关的普通对象
与页面HTML结构存在映射关系的元素对象

元素对象中的内置属性,大部门都和页面的标签存在映射关系:
xxx.style.backgroundColor = 'xxx' 此时不仅把JS中对象对应的属性值改变了,而且也会映射到页面的HTML标签上(标签中有一个style行内样式、元素的样式改变了)

xxx.className='xxx' 此时不仅是把JS对象中的属性值改了,而且页面中的标签增加了class样式类(可以看见的)

元素对象的自定义属性:xxx.index = 0
仅仅是把JS对象中增加了一个属性名(自定义的),和页面中的HTML没啥关系(在结构上看不见)
xxx.setAttribute:通过这种方式设置的自定义属性和之前提到的内置属性差不多,都是和HTML结构存在映射关系的(设置的自定义属性可以呈现在结构上)

获取当前元素的上一个哥哥元素节点
首先获取当前元素的上一个哥哥节点,判断当前获取的节点是否为元素节点(nodeType ===1),如果不是,基于当前获取的节点,找他的上一个哥哥节点。。。(找几次不知道)一直到找到的节点是元素节点为止,
如果在查找过程中,发现没有上一个哥哥节点了(找到头了),则不再继续查找
function pre(curEle) {
var p = curEle.previousSibling;
//p 为null 结束 p的nodeType ===1 结束
while(p && p.nodeType !==1)  {
p = p.previousSibling;//p: p!=null
}
return p;
}
//扩展
next:获取下一个弟弟元素节点
function next(curEle) {
var p = curEle.nextSibling;
//p 为null 结束 p的nodeType ===1 结束
while(p && p.nodeType !==1)  {
p = p.nextSibling;//p: p!=null
}
return p;
}

prevAll:获取所有的哥哥元素节点
function prevAll(curEle) {
var arr = [];
var p = curEle.previousSibling;
//p 为null 结束 
while(p)  {
// 如果是元素节点  放到数组中
if(p.nodeType ===1)  {
arr.push(p);
}
p = p.previousSibling;//p: p!=null
}
return arr;
}

nextAll:获取所有的弟弟元素节点
function nextAll(curEle) {
var arr = [];
var p = curEle.nextSibling;
//p 为null 结束 说明找到了尽头
while(p )  {
if(p.nodeType  ===1)  {
arr.push(p);
}
p = p.nextSibling;//p: p!=null
}
return arr;
}
siblings:获取所有的兄弟元素节点
//既要往前找 又要往后找
function siblings(curEle) {
var arr = [];
var p = curEle.previousSibling;
var n = curEle.nextSinling;
while(p)  {
// 如果是元素节点  放到数组中
if(p.nodeType ===1)  {
arr.push(p);
}
p = p.previousSibling;//p: p!=null
}

while(n )  {
if(n.nodeType  ===1)  {
arr.push(n);
}
n = n.nextSibling;//p: p!=null
}
return arr;

}
index:获取当前元素在兄弟中的排名索引

function index() {
var arr = [];
var index = 0;
var p = curEle.previousSibling;
//p 为null 结束 
while(p)  {
// 如果是元素节点  放到数组中
arr.push(p);
p = p.previousSibling;//p: p!=null
}
index = arr.length;
return index;

}
复制代码

6.日期操作

Date:日期类,通过它可以对时间进行处理
var time = new Date()//获取当前客户端本机时间(当前获取的时间不能作为重要的参考依据)
获取的结果是一个日期格式的对象
typeof new Date() --->'object'
time.getFullYear() 获取四位整年数
time.getMonth() 获取月(0-11 代表1-12月)
time.getDate() 获取日
time.getDay()  获取星期(0-6 代表周日到周六)
time.getHours() 获取小时
time.getMinutes() 获取分钟
time.getSeconds() 获取秒
time.getMilliseconds() 获取毫秒
time.getTime() 获取当前日期距离"1970-01-01 00:00:00"的毫秒差

var time = new Date('2017-10-22');
当new Date中传递了一个时间格式的字符串,相当于把这个字符串转换为标准的时间对象格式(转换完成后 就可以调取上面我们讲的那些方法了)
时间格式的字符串
"2017-10-22"(IE下识别不了)
"2017/10/22"
"2017/10/22 16:15:34"
1508659621314 ===>传递的距离1970的那个毫秒差 (数字格式 不能是字符串)

var timeBox = document.getElementById('timeBox');
function computed() {
var nowTime = new Date();
var targeTime = new Date("2017/10/22 17:00:00");
var spanTime = targeTime - nowTime;//获取两个时间的毫秒差
//已经到达考试的时间:此时我们可以提示开始考试
if(spanTime <=0) {
timeBox.innerHTML = '开始考试'window.clearInterval(timer);
return;
}
//还没有到达考试时间:在总毫秒中计算出还有多少小时、分钟、秒
var hour = Math.floor(spanTime/(1000*60*60));
//把小时占据的毫秒去除掉,剩下的值中计算还有多少分钟
spanTime -= hour*60*1000;
var minute = Math.floor(spanTime/(1000*60));
spanTime -= minute*60*1000;
var second = Math.floor(spanTime/1000);

//不足十 补零
hour < 10 ? hour = '0'+ hour:null;
minute<10?minute = '0'+ minute :null;
second < 10 ?second = '0' + second :null;

timeBox.innerHTML = hour + ':' + minute + ':' + second;
}
//以后每隔1s都重新执行computed
var timer = window.setInterval(computed,1000);
复制代码
  • 动力: 这是我的学习笔记(来源于视频或者查阅文档),您能从中得到收获和进步,是我分享的动力,帮助别人,自己也会更快乐
  • 期望: 不喜勿喷,谢谢合作!如果涉及版权请及时联系我,马上删除!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值