JS基础题

1.数据类型判断?

六种数据类型:
number,string, boolean,undefined,null,object
ES6:symbol ES10:bigint
数据类型判断:
1.typeof : 判断基本数据类型,对引用数据类型不起作用。2.instanceof : 判断 new关键字创建的引用数据类型。
不考虑null 和 undefined(这两个比较特殊)以对象字面量创建的基本
数据类型。
3. constructor :似乎完全可以应对基本数据类型和引用数据类型,但如果声明了一个构造函数,并且把他的原型指向了Array 的原型,所以这种情况下,constructor 也显得力不从心。
4.Object.prototype.toString.call()完美的解决方案。
(扩展)
5.jQuery方法:
jquery.type() 如果对象是null跟undefined,直接返回"null"和"undefined",
注意:
在使用时,一定要引入jquery文件,不然会报错,jQuery is not defined。
(扩展)
6.有局限的判断:
严格运算符======出现在我们的条件判断中,比如判断一个变量是否为空,变量是否为数据等。

2.浅拷贝与深拷贝

值传递与地址传递:
值传递:javaScript有5种基本的数据类型,分别是:boolean,null,
undefine,string和number。这些基本数据类型在赋值的时候是通过值传递的方式,在栈内存中进行操作。

地址传递:引用数据类型是通过地址传递,指向堆内存的首地址。
浅拷贝和深拷贝一定是引用数据类型有的
浅拷贝概念:
只拷贝地址,不拷贝内容。
浅拷贝是会将对象的每个属性进行依次复制,但是当对象的属性值是引用类型时,实质复制的是其引用,当引用指向的值改变时也会跟着变化。
浅拷贝的实现:
1.object.assign
2.扩展运算符…
3.array.prototype.slice()
4.array.prototype.concat() 等

深拷贝概念:
深拷贝,深拷贝的值如果是非基本数据类类型,则递归至基本数据类型后再复制,深拷贝复制的值与原来的值相互隔离互不干扰。
深拷贝最简单的实现:
1.利用数组的Array.prototype.forEach进copy;
2.用for…in实现遍历和复制;
3.JSON.parse(JSON.stringify(obj));
缺陷:对象的属性值是函数时,无法拷贝;
原型链上的属性无法拷贝;
不能正确的处理Date类型的数据;
不能处理RegExp;
会忽略symbol;
会忽略undefined;
4.(必会)递归函数实现深拷贝;
实现一个deepClone函数(深拷贝 完美):
在这里插入图片描述
Object的hasOwnProperty()方法返回一个布尔值,判断对象是否包含特定的自身(非继承)属性。

3.闭包?

是什么:
当一个函数的返回值是另外一个函数,而返回的那个函数如果调用了其父函数的内部变量,且返回的那个函数在外部被执行,就产生了闭包。
闭包是一个环境,具体指的就是外部函数—高阶函数
闭包的三个特性:
1:函数套函数;
2:内部函数可以直接访问外部函数的内部变量或参数;
3:变量或参数不会被垃圾回收机制回收;
闭包的优点:
1:变量长期驻扎在内存中;
2:避免全局变量的污染;
3:私有成员的存在;
闭包缺点:
1.常驻内存;增大内存的使用量;使用不当会造成内存的泄露。
2.不能及时释放内存
3.对捕获的变量是引用,不是复制
4.父函数每调用一次,会产生不同的闭包
使用场景:
1.闭包应用场景之setTimeout。
(原生的setTimeout传递的第一个函数不能带参数,通过闭包可以实现传参效 果)
2.闭包应用场景之回调。
(定义行为,然后把它关联到某个用户事件上(点击或者按键)。代码通 常会作为一个回调(事件触发时调用的函数)绑定到事件)
3.闭包应用场景之封装私有变量。
(在返回的对象中,实现了一个闭包,该闭包携带了局部变量sum,并且从外 部根本无法访问到变量)
4.闭包应用场景之函数防抖节流。
函数防抖:
是指在函数被高频触发时当停止触发后延时n秒再执行函数(即每次触发都清理延时函数再次开始计时)
一般用于resize,scroll,mousemove等。防抖主要是用户触发最后一次事件,延迟一段事件触发。
函数节流:
原理-函数被高频率触发时延时n秒后才会再次执行。节流会规定时间内触发一次事件。

4.垃圾回收机制?

浏览器的 Javascript 具有自动垃圾回收机制
(GC:Garbage Collecation),也就是说,执行环境会负责管理代码执行过程中使用的内存。其原理是:垃圾收集器会定期(周期性)找出那些不在继续使用的变量,然后释放其内存。但是这个过程不是实时的,因为其开销比较大并且GC时停止响应其他操作,所以垃圾回收器会按照固定的时间间隔周期性的执行。

5.js的预处理?

预解析:先使用再定义 定义通过var 定义变量
1.对var 声明的变量进行预处理,只声明不定义;
2.对声明式函数进行预处理,既声明又定义。
同名声明式函数,后者覆盖前者;
用function定义会解析成字符串

6.for in 和for of 有什么区别?

1.在遍历属性的时候推荐使用for in 在遍历数组的时候推荐使用for of

var arr = [99,88,66,77];
for(let i in arr){
    console.log(i);   //0,1,2,3
}
for(let i of arr){
    consoel.log(i);   //99,88,66,77
}

2.for in 循环输出的是key(即下标) for of循环输出的是value(即数组的值)
3.fot of 是弥补修复了es5中for in的不足
4.for of不能循环普通对象,需要object.keys搭配使用

var person={
    name:'coco',
    age:22,
    locate:{
        country:'China',
        city:'beijing',
    }
}
for(var key of Object.keys(person)){
    //使用Object.keys()方法获取对象key的数组
    console.log(key+": "+person[key]);//name: coco,age: 22,locate: [object Object]
}

7.数组常用方法?

push():
向末尾添加一个或多个值,将要传的值作为参数传递;
pop():
向末尾移除一个值;
shift():
删除首部的一个值;
unshift():
向首部添加一个或多个值(传参数);
以上方法都是直接修改原数组,
添加的方法返回值为新数组长度,
删除的方法返回值为删除的元素。
concat():
把两个或多个数组拼接,产生一个新的数组;
slice():
不改变原数组,所以要返回 该值。
语法:arrayObject.slice(start,end)
参数描述:
start 必需。规定从何处开始选取。如果是负数,那么它规定从数组尾部开始算起的位置。也就是说,-1 指最后一个元素,-2 指倒数第二个元素,以此类推。
end 可选。规定从何处结束选取。该参数是数组片断结束处的数组下标。如果没有指定该参数,那么切分的数组包含从 start 到数组结束的所有元素。如果这个参数是负数,那么它规定的是从数组尾部开始算起的元素。
一个常用方法:
Array.prototype.slice.call(arguments,1);能将具有length属性的对象转成数组;
join():
传入一个字符串类型的数据,然后依次将数组每个数据连接起来
语法:arrayObject.join(separator)
参数:separator 可选。指定要使用的分隔符。如果省略该参数,则使用逗号作为分隔符。
toString():
主要用于Array、Boolean、Date、Error、Function、Number等对象。
(1)Array.toString():将数组转换成一个字符串,并且返回这个字符串。
描述:当数组用于字符串环境中时,javascript会调用这一方法将数组自动转换成一个字符串。toString()在把数组转换成字符串时,首先要将数组的每个元素都转换成字符串(通过调用这些元素的toString方法)。当每个元素都被转换成字符串时,它就以列表的形式输出这些字符串,字符串之间用逗号分隔。返回值与没有参数的jion()方法返回的字符串相同。
(2)Boolean.toString():将布尔值转换为字符串。
(3)Date.toString():将Date对象转换成一个字符串,采用本地时间。
注意:toLocaleString()是返回采用地方日期使用地方日期格式的规范的字符串。
(4)Number.toString():将数字转换为字符串。用它的参数指定的基数或底数(底数范围为2-36)。如果省略参数,则使用基数10。当参数值为2时,返回二进制数。
reverse():
用于颠倒数组中元素的顺序。
sort():
默认为升序排列,如果想按照其他标准进行排序,sort()方法是可以传入一个函数,函数通过返回一个值来决定;

 //  按照数值大小进行排序-升序
    arr.sort(function(a,b){
    	return a - b;
    })
    //按照数值大小进行排序-降序
    arr.sort(function(a,b){
    	return b - a;
    })
  //  按照数组中对象的某一个属性值进行排序
    var arr = [
        {name:'zopp',age:0},
        {name:'gpp',age:18},
        {name:'yjj',age:8}
    ];
    function compare(property){
        return function(a,b){
            var value1 = a[property];
            var value2 = b[property];
            return value1 - value2;
        }
    }
    console.log(arr.sort(compare('age')))

splice():
splice() 方法向/从数组中添加/删除项目,然后返回被删除的项目。该方法会改变原始数组。
语法:arrayObject.splice(index,howmany,item1,…,itemX)
参数 :
index 必需。整数,规定添加/删除项目的位置,使用负数可从数组结尾处规定位置。
howmany 必需。要删除的项目数量。如果设置为 0,则不会删除项目。
item1, …, itemX 可选。向数组添加的新项目。

8.原生ajax?

ajax----a:async j:javascript a:and x :xml
ajax的主要作用:
实现客户端和服务器端数据的异步交互,实现页面的局部的刷新,早期的浏览器不支持原生的ajax,后来的浏览器提供了对ajax的原生支持。
基本写法:
ajax的原生请求主要通过XMLHttpRequest,ActiveXObject(IE浏览器)对象来实现异步请求。
/步骤:

// 1:创建异步XMLHttpRequest对象   
	var  xhr = null;
	if (XMLHttpRequest) {
		//主流浏览器
		xhr = new XMLHttpRequest();
	} else {
		//兼容ie6  ie7
		xhr = new ActiveXObject("Microsoft.XMLHTTP");
	}

	// 2:打开连接   搭讪
    xhr.open("get","http://localhost:3000",true)

	// 3:发送请求    
	xhr.send(null)

	// 4:获取响应数据
	xhr.onreadystatechange = function(){
		if (xhr.status == 200 && xhr.readyState==4) {
			// var data = xhr.responseText;
			var data = xhr.response;
			console.log(data)
			show(data)
		}
	}

9.jsonp原理?

jsonp(实现原理) :主要是利用动态创建script标签请求后端接口地址,然后传递callback参数,后端接收callback,后端经过数据处理,返回callback函数调用的形式,callback中的参数就是json。

10.跨域及解决方案?

产生原因:
由于浏览器的同源策略,请求不同域名或同域名不同端口的接口数据时,会出现数据无法返回的问题,这个问题就是跨域问题。
是什么:
同源策略是对ajax的一个主要约束,它为通信设置了“相同的域,相同的端口,相同的协议”这一限制。
跨域产生的五种情况:
1不同域名;
2同一域名,不同端口;
3同一域名,不同端口;
4域名和域名对应的ip;
5主域相同,子域不同;
使用场景:
因为目前基于wed技术开发的项目主要是前后端分离的项目,即后端提供数据访问的接口,前端调取接口并实现数据展现和ui交互,这里后端提供的接口通常为线上真实的接口,本地访问通常是会产生可以情况。
跨域解决方案:
1.jsonp(实现原理) :
主要是利用动态创建script标签请求后端接口地址,然后传递callback参数,后端接收callback,后端经过数据处理,返回callback函数调用的形式,callback中的参数就是json。(jsonp和ajax关系)jsonp只是一种解决方案,不是ajax。
2.前端代理方案:
前端代理在vue中主要通过Vue脚手架中的config中的index文件来配置的,其中有个proxyTable来配置跨域的。前端代理核心实现通http-proxy-middleware来实现的,vue2.x和vue3.x脚手架代理跨域实现原理是一样的。
(具体方法代码):在根目录下创建vue.config.js

 module.exports={
		lintOnSave:false,
		runtimeCompiler:true,
		//在devServer里配置跨域请求
		devServer:{
		   proxy:{//在此写跨域配置
			'/api':{ //本地最终缓存路径
			        target:'http://test.365msmk.com/api',//要跨域的那个接口
			        changeOrigin:true,//允许跨域
			        pathRewrite:{ //重写路径
			         '^/api':'',
			       	}
				}
	       	}
		   }
	      }

3.cors方案 :
cors全称是跨域资源共享,主要是后台工程师设置后端代码来
达到前端跨域请求的。(cors的原理):cors定义一种跨域访问的机制,可以让ajax实现可以访问。cors允许一个域上的网络应用向另一个域提交跨域ajax请求,实现此功能非常简单,只需由服务器发送一个响应标头即可。
res.setHeader(“Access-Control-Allow-Origin”,"*");

11.数组去重方法?

1: 利用ES6新增数据类型

<script>
	var array = [1,2,3,4,5,1,2,3,4,5];	
	function uniq1(array) {
	    return [...new Set(array)];
	}
	function uniq2(array) {
		return Array.from(new Set(array));
	}
	console.log(uniq2(array))
</script>

2: 利用 indexOf

<script>
var array = [1,2,3,4,5,1,2,3,4,5];	
function uniq3(arry) {
	    var result = [];
	    for (var i = 0; i < arry.length; i++) {
	        if (result.indexOf(arry[i]) === -1) {
	            result.push(arry[i])
	        }
	    }
	    return result;
	}
	console.log(uniq3(array))
</script>

3: 利用Es7 includes


<script>
var array = [1,2,3,4,5,1,2,3,4,5];	
function uniq4(arry) {
	    var result = [];
	    for (var i = 0; i < arry.length; i++) {
	        if (!result.includes(arry[i])) {
	            result.push(arry[i])
	        }
	    }
	    return result;
	}
	console.log(uniq4(array))
</script>

4:利用 reduce 适合累加 累乘

<script>
var array = [1,2,3,4,5,1,2,3,4,5];	
	function uniq5(arry) {
	    return arry.reduce((prev, cur) => prev.includes(cur) ? prev : [...prev, cur], []);
	}
	console.log(uniq5(array))
</script>

5:利用Map

function uniq6(arry) {
	    let map = new Map();
	    let result = new Array();
	    for (let i = 0; i < arry.length; i++) {
	        if (map.has(arry[i])) {
	            map.set(arry[i], true);
	        } else {
	            map.set(arry[i], false);
	            result.push(arry[i]);
	        }
	    }
	    return result;
	}
	console.log(uniq6(array))

12.函数

是什么:
封装的可重复利用的具有特定功能的代码 ;

作用好处:
安全 ,可重用;

分两大类:
系统函数 eval() isNaN() isArray() Math.random()
parseInt() parseFloat())
自定义函数 return没有具体返回值的时候 返回的就是undefined

写法:
function 函数名(参数列表){
函数体;
return 返回值
}

创建函数的三种方式:
1:使用function关键字申明命名函数

//使用Function对象构造方法创建函数
				var add = new Function(
					"num1",
				 	"num2",
				 	"var sum=num1+num2;return sum;"
				 )
				 console.log(add(2,3))
				匿名函数
				 var add = function(num1,num2){
				 	var sum = num1+num2
				 	return sum;
				 }

2:使用function关键字申明匿名函数

//命名函数
	// function  add(){

	// }

	// console.log(add)//定义函数的字符串  函数名本质是变量名 指向某个function的引用
	// console.log(typeof add)//function  

	//匿名函数
//    var add2 = function(num1,num2){
//    		var sum = num1+num2;
//    		return sum;
//    }
//    console.log(add2(10,20))

3:使用Function对象构造方法创建函数

	//使用Function对象构造方法创建函数
	// var add1 = new Function(//不推荐使用  效率
	// 	"num1",
	// 	"num2",
	// 	"var sum = num1+num2; return sum"
	// )
	// console.log(add1(10,20))

13.构造函数?

构建创造对象时候调用的函数 ,并只能调用一次,作用是给对象做初始化 ,一般函数能调用多次。

14.递归函数?

在函数内部自身调用自身的函数;循环能做的所有事情 递归都能做;循环做不了的事情 ,递归也能做。

方法:
1.找临界值:无须计算,即可得出的值,退出递归的条件 if(i==1){retrun 1}
2.这一次和上一次运算的关系 fn和fn-1的关系
3.假设当前递归函数可以运行,根据上一次调用自身的结果,写出这次运算的结果。f(n)=f(n-1)+n;

实例:
累加求和 1+2+3…+10;(递归入入门)

 function fun(n) {
        if (n == 1) {
            return 1;
        }
        return fun(n - 1) + n;
    }
    console.log(fun(10))

//123*…*100

 function fun(i) {
        if (i == 1) {
            return 1
        }
        return fun(i - 1) * i;
    }
    console.log(fun(100));

传入一个n 打印n个hello world

  function fun(n) {
        if (n == 1) {
            console.log("hello world");
            return 1;
        }
        return fun(n - 1) + console.log("hello world");
    }
    console.log(fun(7))

猴子吃桃问题:
猴子第一天摘下若干个桃子,当即吃了一半,还不过瘾,又多吃了一个,第二天早上又将剩下的桃子吃掉一半,又多吃了一个。以后每天早上都吃前一天剩下的一半零一个。

  //1.for循环
    var num = 1;
    for (var i = 1; i < 10; i++) {
        num = (num + 1) * 2;
    }
    console.log(num);

    // 2.递归函数
    function eat(i) {
        if (i == 10) {
            return 1;
        } //第十天 剩余一个桃子
        return (eat(i + 1) + 1) * 2;
    }
    console.log(eat(1))

兔子繁殖问题:
设有一对新生兔子,从第四个月开始他们每个月月初都生一对兔子,新生的兔子从第四个月月初开始又每个月生一对兔子
按此规律,并假定兔子没有死亡,n(n <= 20)个月月末共有多少对兔子?(斐波那契数列)

 // 规律:月份  1     2    3     4    5    6    7    8    9     10
 // 	 兔子数 1     1    1     2    3    4    6    9    13    19
    // 规律:f(n)= f(n-1)+f(n-3)   
    function rabbit(n) {
        if (n <= 3) {
            return 1;
        }
        return rabbit(n - 1) + rabbit(n - 3)
    }
    console.log(rabbit(10))

给定一个整数数组nums = [2, 7, 11, 15]和目标值target = 9,找出和为目标值的那两个整数的下标

  let arr = [2, 7, 11, 15],
        target = 9;
    function fun(arr, target) {
        arr.sort((v1, v2) => (v1 - v2));
        for (let i = 0, l = arr.length; i < 1; i++) {
            if (arr[i] > target) {
                break;
            } else {
                let index = arr.indexOf(target - arr[i])
                if (index != -1) {
                    return [i, index]
                }
            }
        }
        return []
    }
    console.log(fun(arr, target));

15.原型和原型链?

原型的概念:
JavaScript的所有对象中都包含了一个 [proto] 内部属性,这个属性所对应的就是自身的原型。JavaScript 的函数对象,除了原型 [proto] 之外,还有 prototype 属性,当函数对象作为构造函数创建实例时,该 prototype 属性值将被作为实例对象的原型 [proto]。

原型链的概念:
当一个对象调用自身不存在的属性/方法时,就会去自己 [proto] 关联的前辈 prototype 对象上去找,如果没找到,就会去该 prototype 原型 [proto] 关联的前辈 prototype 去找。依次类推,直到找到属性/方法或 undefined 为止。从而形成了所谓的“原型链”。

16.ES5实现继承?

实现继承的六种方式:
1.原型链继承
原型链继承的基本思想
是利用原型让一个引用类型继承另一个引用类型的属性和方法。
缺点:
1:通过原型来实现继承时,原型会变成另一个类型的实例,原先的实例属性变成了现在的原型属性,
该原型的引用类型属性会被所有的实例共享。
2:在创建子类型的实例时,没有办法在不影响所有对象实例的情况下给超类型的构造函数中传递参数。

2.借用构造函数
基本思想为:
在子类型的构造函数中调用超类型构造函数。
优点:
可以向超类传递参数 解决了原型中包含引用类型值被所有实例共享的问题
缺点:
借用构造函数方法都在构造函数中定义,函数复用无从谈起,另外超类型原型中定义的方法对于子类型而言都是不可见的。

3.组合继承(原型链 + 借用构造函数)
基本思路:
使用原型链实现对原型属性和方法的继承,通过借用构造函数来实现对实例属性的继承,既通过在原型上定义方法来实现了函数复用,又保证了每个实例都有自己的属性。
缺点:
无论什么情况下,都会调用两次超类型构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部。
优点:
可以向超类传递参数
每个实例都有自己的属性
实现了函数复用

4.原型式继承
5.寄生式继承
6.寄生组合式继承

17.ES6继承?

class User{//类  function 
    constructor(name,pass){
        this.name = name;
        this.pass = pass;
    }
    showName(){
        console.log(this.name) 
    }
    showPass(){
        console.log(this.pass) 
    }
}
class VipUser extends User{
    constructor(name,pass,level){
      super(name,pass)//指针 指向父类构造器  区别于this 
      this.level = level;  
    }
    showLevel(){
        console.log(this.level)
    }
}
var u1 = new VipUser("tom","123456",3)
u1.showName();
u1.showPass();
u1.showLevel()
console.log(typeof VipUser) 

18.面向对象和面向过程?

一、编程思想不同
1、面向过程:是一种以过程为中心的编程思想。都是以什么正在发生为主要目标进行编程。

2、面向对象:是一类以对象作为基本程序结构单位的程序设计语言,指用于描述的设计是以对象为核心,而对象是程序运行时刻的基本成分。

二、特点不同
1、面向过程:就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。

2、面向对象语言:识认性,系统中的基本构件可识认为一组可识别的离散对象,对象具有唯一的静态类型和多个可能的动态类型,在基本层次关系的不同类中共享数据和操作。

三、优势不同
1、面向过程:不支持丰富的“面向对象”特性(比如继承、多态),并且不允许混合持久化状态和域逻辑。

2、面向对象语言:在内部被表示为一个指向一组属性的指针。任何对这个对象的操作都会经过这个指针操作对象的属性和方法。

19. jq动画

1、显示 隐藏

  <div class="box"></div>
  <button class="btn">显示</button>
  <button class="btn2">隐藏</button>

      $(function () {
      //1:show和hide的使用 经常使用的到的参数是 1000 就是动画过度时间
      //需求:点击隐藏按钮 隐藏box
      // $(".btn2").on("click", function () {
      //   $(".box").hide()
      // })
      // //需求:点击显示按钮 显示box
      // $(".btn").on("click", function () {
      //   $(".box").show()
      // })
      //需求:只点击显示按钮 可以切换 显示和隐藏
      $(".btn").on("click", function () {
        $(".box").toggle(1000)
      })
    })

2、淡入 淡出

  <div class="box"></div>
  <button class="btn">淡入</button>
  <button class="btn2">淡出</button>

      //2:淡入淡出 fadeIn和fadeOutp的使用 经常使用的到的参数是 1000 就是动画过度时间
      //点击淡入 淡入box
      $(".btn").on("click", function () {
        $(".box").fadeIn(1000)
      })
      //点击淡出 淡出box
      $(".btn2").on("click", function () {
        $(".box").fadeOut(1000)
      })
      //点击淡入 随意切换淡入淡出
      $(".btn").on("click", function () {
        $(".box").fadeToggle(1000)
      })
      //点击淡入 可以修改透明度为.3
      $(".btn").on("click", function () {
        $(".box").fadeTo(1000, .3)
      })

3、滑入 滑出

  <div class="box"></div>
  <button class="btn">滑入</button>
  <button class="btn2">滑出</button>

    $(function () {
      //2:滑入划出 slideDown和slideUp的使用 经常使用的到的参数是 1000 就是动画过度时间
      //点击滑入,box滑入
      $(".btn").on("click", function () {
        $(".box").slideDown(1000)
      })
      //点击划出,box划出
      $(".btn2").on("click", function () {
        $(".box").slideUp(1000)
      })
      //点击滑入,box动态切换滑入划出
      $(".btn").on("click", function () {
        $(".box").slideToggle(1000)
      })
    })

动画

  <button class="btn">开始</button>
  <div class="box"></div>
    $(function () {
      //自定义动画
      //需求:点击开始,让box向左移动200,并且宽度变为200
      // $(".btn").on("click", function () {
      //   $(".box").animate({
      //     left: 200,
      //     width: 200
      //   })
      // })

      //需求:点击开始,让box先向右移动200,然后向下移动200,之后向左移动200,最后向上移动200
      $(".btn").on("click", function () {
        $(".box").stop(true).animate({
            left: 300
          }, 1000)
          .animate({
            bottom: 200
          }, 1000)
          .animate({
            left: 0
          }, 1000)
          .animate({
            top: 50
          }, 1000)
      })
      //.stop() 的作用:参数1:如果设置成true,则清空队列。 参数2::让当前正在执行的动画立即完成,并且重设show和hide的原始样式,
      //.delay()的作用:就是延迟。。。
    })
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值