前端面试整理

1、什么是原型?什么是原型链?如何获取对象的原型值?

        在js中每一个构造函数都有一个prototype属性,该属性是一个对象,包含了所有当前构造函数的属性和方法。当我们通过构造函数创建对象的时候,这个对象中有一个指针,指向这个构造函数的prototype,这个指针就是原型。

        原型链是通过原型组成的一条查找链。

        获取原型方法:

obj.__proto__ 
//或者
Object.getPrototypeOf(obj)

2、什么叫防抖与节流?

        都是为了防止短时间内多次执行事件,消耗浏览器性能。防抖就是多用在模糊查询input事件中,多次更新input时数据会发生抖动的处理,在一定时间内取最后一次输入的value值进行请求。节流是在同一时间段,如果时间没有超过指定的时间间隔,那么就不去执行下一次。

   // 防抖
   function debounce (callback,delay) {
        var t = null;
        return function () {
            clearTimeout(t);
            t = setTimeout(callback,delay);
        }
    }
    window.onscroll = debounce(function(){
        console.log("调用了一次");
    },500)
    
    // 节流;
    function throttle (callback,duration){
        var lastTime = new Date().getTime();
        return function () {
            var now = new Date().getTime();
            if(now - lastTime > 500){
                callback();
                lastTime = now;
            }
        }
    }
    window.onscroll = throttle(function(){
        console.log("调用了一次");
  }

3、闭包

        闭包就是能够读取其他函数内部变量的函数。        

        闭包的特点:1、闭包中的变量不会被浏览器回收,需慎用,同时延长了变量的生命周期,用完一定要手动回收掉。2、闭包中有私有作用域。

4、var,let,const的区别

        var有三个特点,1、声明提升,2、变量会被覆盖,3、没有块级作用域。(其实也有,要在function里)

let没又声明提升,不可被重复声明,有块级作用域。

const声明之后必须赋值否则报错,用来声明常量且不可修改,且拥有let的特性。

5、怎样不创建第三个变量互换a,b两个变量的值?

        es5

//只能互换整数
let a = 1;
let b = 2;
a = a + b;
b = a - b;
a = a - b;

        es6

let a = 1;
let b = 2;
[a, b] = [b, a]

6、如何用ES6快速去重

let arr = [12, 43, 23, 12, 43, 55]
let item = [...new Set(arr)]
console.log(item)

7、vue双向绑定的原理

首先要了解Object中的一个defineProperty方法,

语法:Object.defineProperty(obj,property,descriptor)

参数一:obj
绑定属性的目标对象
参数二:property
绑定的属性名
参数三:descriptor


属性描述(配置),且此参数本身为一个对象

属性值1:value
        设置属性默认值
属性值2:writable
        设置属性是否能够修改
属性值3:enumerable
        设置属性是否可以枚举,即是否允许遍历
属性值4:configurable
        设置属性是否可以删除或编辑
属性值5:get
        获取属性的值
属性值6:set
        设置属性的值

vue中的双向绑定其实就是用了definePropertysetget的方法来实现的。

手写一个双向绑定:

<!--HTML-->
<div>
    <input type="text" id="userName">
    <p id="userText"></p>
</div>
    //js
    let obj = {}
    Object.defineProperty(obj, "userName", {
        get() {
            console.log('get value is:' + userName)
        },
        set(newData) {
            console.log('set newData is:' + newData)
            userName = newData
            document.getElementById("userName").value = newData
            document.getElementById("userText").innerHTML = newData
        }
    })
    document.getElementById("userName").addEventListener("keyup", (e) => {
        console.log(e.target.value)
        obj.userName = e.target.value
    })

8、data为什么是一个函数

        因为data其实就是一个闭包,vue是单页面应用程序,会存在n个组件,就是为了确保各个组件的数据不会互相污染,所以用闭包,做了私有作用域

9、vi-if和v-show的区别

        v-if是多次的渲染,v-show是记载中就渲染好的,false状态下只是隐藏了起来,禁忌用来做权限管理。

10、什么是虚拟DOM,虚拟DOM是怎样提升VUE渲染效率的?

        首先,虚拟DOM的本质是js对象。每一个组件都有一个render函数,这个函数会生成一个虚拟DOM树,在vue渲染视图时,会调用这个render函数,在组件的数据或属性改变时 ,也会调用这个render函数。在初次加载的时候,组件会使用render函数将模板转换成虚拟DOM,再把虚拟DOM转换成真实DOM,渲染到页面上,其实这是vue的一个缺点,首次加载是多消耗了一些性能的;但它的优点是在数据改变时,调用render函数,创建一个新的虚拟DOM树,然后比较新旧虚拟DOM,找到需要修改的虚拟DOM,然后将它改变到真实DOM上,保证对真实DOM做到最小的修改,提升了页面n次渲染的性能。

11、深拷贝和浅拷贝

         基本数据类型的赋值都是深拷贝;对象和数组的赋值是浅拷贝。解构赋值可以对一维数组实现深拷贝,多维数组还是浅拷贝。一般用JSON.parse(JSON.stringify(arr))来实现深拷贝,但无法实现函数的深拷贝。

    function deepClone(data) {
        if (typeof data !== "object") return data
        let newData = Array.isArray(data) ? [] : {}
        for (key in data) {
            if (data.hasOwnProperty(key)) {
                if(data[key] && typeof data[key] === "object"){
                    newData[key] = deepClone(data[key])
                }else{
                    newData[key] = data[key]
                }
            }
        }
        return newData
    }

    var obj = {
        age: 13,
        name: {
            addr: '天边'
        }
    }

    var obj2 = deepClone(obj);
    obj2.age = 14
    obj2.name.addr = '地心'
    console.log(obj, obj2); 

12、Promise怎样使用?Promise的链式调用怎样写?手写一个Promise。

    //Promise的使用
    let flag = true;
    const p = new Promise((resolve, reject) => {
        if (flag) {
            let obj = {
                a: 1,
                b: "aaa"
            }
            resolve(obj)
        }
    })
    //Promise的链式调用
    p.then((res) => {
        return new Promise((resolve, reject) => {
            if (res) {
                let obj = {
                    a: res.a,
                    c: true
                }
                resolve(obj)
            }
        })
    }).then(res => {
        console.log(res)
    })
    //手写Promise
    function myPromise(executor) {
        let self = this;
        self.status = "pending";  //状态初始化
        self.value = null;   //成功之后,返回的数据
        self.reason = null;  //失败的原因

        // 暂存状态
        self.onFulfilledCallbacks = []
        self.onRejectedCallbacks = []

        // 返回成功的结果
        function resolve(value) {
            if (self.status === "pending") {
                self.value = value;   //保存成功结果
                self.status = "fulfilled";
                // 状态改变之后依次取出
                self.onFulfilledCallbacks.forEach(item => item(value))
            }
        }

        // 返回失败的结果
        function reject(reason) {
            if (self.status === "pending") {
                self.reason = reason;   //保存失败结果
                self.status = "rejected";
                // 状态改变之后依次取出
                self.onRejectedCallbacks.forEach(item => item(value))
            }
        }

        try {
            // 它是同步的回调,会立即在主线程上执行,被称为executor函数
            executor(resolve, reject);
        } catch (err) {
            reject(err)
        }

    }

    // 把.then方法放在原型上
    // 不管是成功还是失败
    myPromise.prototype.then = function (onFulfilled, onRejected) {
        let self = this;
        // 判断传进来的是不是一个方法,如果不是就定义一个方法
        onFulfilled = typeof onFulfilled === 'function' ?
            onFulfilled : function (data) { resolve(data) }

        onRejected = typeof onFulfilled === 'function' ?
            onFulfilled : function (err) { throw (err) }

        if (self.status === "pending") {
            self.onFulfilledCallbacks.push(onFulfilled)
            self.onRejectedCallbacks.push(onRejected)
        }
    }

    let demo = new myPromise(function (resolve, reject) {
        console.log(123)
        setTimeout(() => {
            resolve(222)
        }, 500);

    }).then( data => {
        console.log(data)
    })

13、常见算法(冒泡排序,快速排序,去重算法,查找出现最多的字符,diff算法)

    let arr = [9, 56, 5, 7, 12, 35, 54]
    // 冒泡排序
    function arr_sort(arr) {
        // var temp;
        for (var i = 0; i < arr.length - 1; i++) {
            for (var j = 0; j < arr.length - i; j++) {
                if (arr[j] > arr[j + 1]) {
                    var swap = arr[j + 1]
                    arr[j + 1] = arr[j]
                    arr[j] = swap
                }
            }
        }
        return arr
    }
    console.log(arr_sort(arr))
    //快速排序    
    function quickSort(ary) {
        if (ary.length <= 1) {
            return ary
        }
        // 获取数组中间数
        let contentValue = ary.splice(Math.floor(ary.length / 2), 1)[0]
        // 创建左右空数组,大于中间数的放右边,小于中间数的放左边
        let leftArr = []
        let rightArr = []
        for (let i = 0; i < ary.length; i++) {
            let item = ary[i]
            item > contentValue ? rightArr.push(item) : leftArr.push(item);
        }
        return quickSort(leftArr).concat(contentValue, quickSort(rightArr))
    }
    console.log(quickSort(arr))

    // 数组去重
    let arr = [1, 1, 65, 23, 22, 37, 92, 22]
    function unique(ary) {
        let newArr = []
        for (let i = 0, len = ary.length; i < len; i++){
            if(newArr.indexOf(ary[i]) == -1){
                newArr.push(ary[i])
            }
        }
        return newArr
    }
    console.log(unique(arr))
//-------------------------------------------------------------------------
    // 数组去重
    let arr = [1, 1, 65, 23, 22, 37, 92, 22]
    // 速度最快, 占空间最多(空间换时间) 
    function unique(array) {
        var r = [array[0]]
        for (var i = 1; i < array.length; i++) {
            if(array.indexOf(array[i]) == i){
                r.push(array[i])
            }
        }
        return r;
    }
    console.log(unique(arr))
    // 使用数组查找出出现次数最多的字符,确定是如果是多个,只会存最后一个
    var str = "zhaochucichuzuiduodezifu";
    let arr = []
    for (let i = 0, len = str.length; i < len; i++) {
        let index = -1
        let j = 0
        do {
            index = str.indexOf(str[i], index + 1)
            if (index != -1) {
                j++
            }
        } while (index != -1);
        arr[j] = str[i]
    }
    console.log('出现次数最多的是' + arr[arr.length-1])
    console.log('出现次数是' + (arr.length-1))

//-------------------------------------------------------------------------
    // 使用对象,可以查出多个
    var str = "iiiiijjjjj";
    let obj = {}
    for (let i = 0, len = str.length; i < len; i++) {
        if (obj[str[i]]) {
            obj[str[i]]++
        } else {
            obj[str[i]] = 1
        }
    }

    let max = 0;
    for (key in obj) {
        if (obj[key] > max) {
            max = obj[key]
        }
    }

    for (item in obj) {
        if(obj[item] == max){
            console.log('出现次数最多的是:'+ item)
            console.log('出现次数是:'+ obj[item])
        }
    }

14、vue生命周期的理解

        生命周期我理解为整个vue组件实例从创建,到销毁的一个过程,这个过程中包含了8个钩子函数,分别是beforeCreate(创建前),created(创建后),beforeMount(挂载前),mounted(挂载后),beforeUpdate(更新前),updated(更新后),beforeDestroy(销毁前),destroyed(销毁后)。特殊情况下会有另外的两个钩子,是需要组件缓存的时候,使用了keep-alive时延伸出来的,分别是activated(缓存组件激活时)和deactivated(缓存组件停用时),当使用deactivated的时候就不会触发销毁前后的两个钩子。

15、vue组件之间是如何通讯的?

        a)props/$emit

        父组件a通过props的方式向子组件b传递,子组件b通过b组件的$emit来创建自定义事件,父组件a在v-on中方式获取。

        b)$emit/$on

        实现了任何组件直接的通信,建立一个空的Vue,比如let Event = new Vue(),要传递的组件只要创建自定义事件名和要传入的变量放入$emit()方法里,比如Event.$emit("自定义事件名", data),然后要接收的组件用$on()接收,比如Event.$on("自定义事件名",data => {console.log(data)})。缺点是代码可读性变差,如果项目庞大,隔代传递多,很难找到某个状态发生更变的时机。于是我们看c方法

        c)使用vuex

        共享状态管理,vuex和localStorage的区别是,vuex存储的数据是响应式的,但页面关闭vuex就销毁了,localStorage会一直在,且只能纯字符串,vuex储存不需要转换。

        d)$parent/$children与ref

        ref如果在普通的DOM元素上使用,引用指向的就是DOM元素;如果用在子组件上,引用就指向组件实例。这三个方法都可以直接操作组件里的方法,缺点是不能跨级和兄弟间通信。

        e)provide/inject

        vue2.2.0新增的一个API,这对选项需要一起使用,作用是让一个祖先元素向所有的子孙组件注入一个依赖,不管组件层次有多深,都可以生效。一言而蔽之:祖先组件中通过provider来提供变量,然后在子孙组件中通过inject来注入变量。 

// A.vue
export default {
  provide: {
    name: '浪里行舟'
  }
}

// B.vue
export default {
  inject: ['name'],
  mounted () {
    console.log(this.name);  // 浪里行舟
  }
}

16、MVVM和MVC是什么区别?

        首先MVC是,模型、视图、控制器三个单词的缩写,分别对应的是数据,视图和业务逻辑,而MVVM是MVC的发展,在业务逻辑,应用程序越来越复杂的环境下,出现了MV替代了C的框架,以前的C只负责管理自己的生命周期,处理控制器之前的跳转,实现控制器容器。后来应用程序复杂,需要处理数据解析,这部分代码放在了C里,使代码非常臃肿。而MV提供了数据双向绑定,让开发变得更简单。VM算是M和V直接的一个桥梁,它可以视图修改数据,也可以数据修改视图。

17、简述原型,构造函数,实例的理解

        构造函数也可以叫工厂函数,可以理解为对象的模板,可以快速的创建一个有基本属性的对象,一般在用构造函数new一个新对象的时候,就是一个实例,实例有实例成员可以供实例来访问,也就是实例的属性,还有一种成员叫静态成员,静态成员是直接给构造函数本身添加的属性,只能给构造函数本身访问,实例访问不了。

        一般构造函数如果想定制一个方法,提供给每一个实例使用,不会在构造函数里创建,这样每一个实例对象都会创建一个方法,会大量的占用内存。这时就会用到原型,每一个构造函数都有一个属性,叫prototype,prototype是一个对象,包含的当前构造函数所有的属性和方法,我们可以把所有的方法都放在prototype里, 我们在创建实例对象的时候,这个对象会有一个指针,指向就是prototype,这个指针就是原型。原型链说的就是原型组成的查找链。

18、前端怎样解决跨域问题?

        jsonp

19、清除浮动的方式有哪些?

        a)使用clear:both,给每个有浮动元素的父元素,增加一个有clear:both的子元素。优点是兼容好,代码少,缺点是会多很多无意义的代码。

        b)使用overflow,给每一个浮动的元素加上overflow:hidden。优点是不会多很多无意义代码,缺点是有时要用到这个属性。

        c)给需要清楚浮动的元素加一个伪元素,伪元素来做clear:both。最优雅的解决方法,缺点是不兼容老IE。

20、什么是重绘和回流?

        在HTML中,每个元素都可以理解成一个盒子,在浏览器解析过程中,会涉及到回流与重绘。回流:布局引擎会根据各种样式计算每个盒子在页面上的大小与位置。当改变大小,显示隐藏,更换不同尺寸的图片等影响盒子布局的属性时,就会回流。

重绘:当计算好盒模型的位置、大小及其他属性后,浏览器根据每个盒子特性进行绘制。当修改颜色,透明度,阴影等,会触发重绘。一般回流必定会导致重绘,但重绘不一定会导致回流。

减少回流的几种方式:

        a)避免使用table布局,table中的每个元素的大小和内容都会影响整个table的重新布局;

        b)对于复杂动画对其设置定位,使它脱离文档流,就可以减少他对其他元素的影响;

        c)减少一条一条的修改DOM样式,最好预定好class,然后更换class。

21、从输入url到页面展示到底发生了什么?

        1)输入地址

        在我们开始输入url时,浏览器会从历史记录或者书签中找可以对应的url,然后给出智能提示,可以补全地址。

        2)浏览器查找域名的 IP 地址

        3)浏览器向web服务器发送一个http请求。

        4)服务器的永久重定向相应。

        5)服务器处理请求。

        6)服务器返回请求

        7)浏览器显示html

        8)加载资源

22、web前端的优化策略

        1)减少数据请求,比如css雪碧图,合并压缩样式表,js脚本。利用缓存等;

        2)减少首次加载的数据,比如延迟加载(懒加载)

        3)样式表和js文件书写的顺序。比如css放在头部,js文件放在尾部的</body>上面,先让用户看到页面样式。

23、项目中遇到的问题

        

24、项目中package.json文件有什么作用

        首先,它记录了当前项目的基本信息,比如项目名称,版本,作者,GitHub地址等,其次它记录了项目引用了哪些第三方模块,这样在下载项目的时候不必下载整个项目的依赖包,直接初始化init项目就可以了。

25、webpack的使用有哪些优化建议?

        

26、vue常用的指令有哪些,怎样封装指令?

        常用的指令有v-if,v-show,v-else,v-bind之类,封装的话有两种方法,一种是全局封装,一种是局部组件封装。

//全局注册
Vue.directive('focus', {
  // 当被绑定的元素插入到 DOM 中时……
  inserted: function (el) {
    // 聚焦元素
    el.focus()
  }
})

//局部注册
directives: {
  focus: {
    // 指令的定义
    inserted: function (el) {
      el.focus()
    }
  }
}

27、Vue中的key的作用,用到哪些地方

        key的作用是标记item的唯一性,在更新数据的时候,vue会用新老虚拟DOM来对比,对比就是用key属性来对比的,在遍历的时候,下标就可以用来当key,但最好还是用唯一值id一类。可以对比的更快更准确。

        如果key重复会报错。

28、Vuex的原理

29、路由的几种模式,history的原理

        两种,一种是hash,一种是history,hash是#号后面的/index一类,他并不会以为#号后的修改而再次请求,history用的是浏览器提供的 pushState() 和 replaceState()两个方法,实现跳转不刷新页面,两种模式都非常适合SPA单页面应用程序。

        hash     优点:纯前端路由,不需要后端干涉,兼容性好。

                     缺点:url带#号不规范,不美观

        history  优点:url规范

                     缺点:刷新404,兼容性差一点,用了浏览器的pushState() 和 replaceState() 方法。

30、Vue的计算属性和侦听属性

        计算属性是用已有的值得到一个新值,当值有更新时,就会触发,包含了get和set方法,用对象的形式写,如果只是获取没有修改,直接像方法一样简写,具有缓存特性,如果变量没有更新,在词使用可以直接获取,不会在重新计算,使用很快捷。

<div id="demo">{{ fullName }}</div>
var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'kobe',
    lastName: 'bryant',
  },
  computed: {
	  fullName: {
	    // getter
	    get: function () {
	      return this.firstName + ' ' + this.lastName
	    },
	    // setter
	    set: function (newValue) {
	      var names = newValue.split(' ')
	      this.firstName = names[0]
	      this.lastName = names[names.length - 1]
	    }
	 }
 }

侦听属性是用来监听一个指定数据的改变,进而调用对应的逻辑处理数据

<div id="app">
    <input type="number" name="" v-model="num">
    {{price}}
    {{total}}
</div>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
<script >
  let vm = new Vue({
    el: '#app',
    data: {
      price:"55",
      num:"",
      total:""
    },
    watch:{
      num( newVal ){
        this.total = this.price * newVal
      }
    }
  })
</script>

        总结:计算属性不能执行异步调用,必须同步执行,而watch可以异步使用。

31、Href和src区别

        href是引用资源,当他下载的时候不会停止对其他资源的加载,src在下载资源的时候会停止其他资源的加载,等到当前这个资源加载好在继续。

32、图片怎么判断是否显示成功

        图片可以加一个onload事件,当图片渲染完成后,就会触发事件。


33、Promise函数的作用, async awite函数手写


34、Flex布局实现瀑布流,实现骰子布局

35、vue路由传参的几种方式

        a)params来传值(会显示在url上)

// 声明式
// 父组件
<router-link to = "/跳转路径/传入的参数"></router-link>

// 子组件使用
this.num = this.$route.params.num

// 路由配置
{path: '/a/:num', name: A, component: A}

地址栏中的显示: http://localhost:8080/#/a/123
// 编程式  
<button @click="deliverParams(123)">push传参</button>

  methods: {
    deliverParams (id){
      $route.push({
        path: `/a/${id}`
      })
    }
  }

// 子组件使用
this.id= this.$route.params.id

// 路由配置
{path: '/a/:id', name: A, component: A}

地址栏中的显示: http://localhost:8080/#/a/123

        b)使用name匹配存放在params(不会显示在url上,刷新则消失)

// 声明式
<router-link :to="{name:'Child',params:{id:123}}">进入Child路由</router-link>
//编程式 
this.$router.push

//子路由配置
{
  path: '/child,
  name: 'Child',
  component: Child
}
//父路由编程式传参(一般通过事件触发)
this.$router.push({
    name:'Child',
    params:{
    	id:123
    }
})

//子组件获取
this.$route.params.id

        c)使用name匹配存放在query(会显示在url上)

        操作与b一样,区别在于会显示在url上

36、vue数据更新视图不更新的原因

        如果用下标是修改数组或对象,就会出现这种情况

// 错误案例
new Vue({
	el: "#app",
	data: {
		arr: ["Javascript", 'Vue'],
		objInfo: {
			'item-1': "123"
		}
	},
	methods: {
		upDateData() {
			// 借助 $set函数 对数据进行操作
			this.$set(this.arr, 0, "CSS")
		}
	}
})

// 处理方法
new Vue({
	el: "#app",
	data: {
		arr: ["Javascript", 'Vue'],
		objInfo: {
			'item-1': "123"
		}
	},
	methods: {
		upDateData() {
			// 借助 splice 函数 对数据进行操作
			this.arr.splice(0, 1, "CSS")
		}
	}
})

37、v-for和v-if哪个优先级高

        v-for优先级高。

        如何避免v-for带来的性能损耗:

//方法一:将v-if置于外层元素
<ul v-if="data.length > 0">
    <li v-for="item in data">
        {{item.name}}
    </li>
</ul>

//方法二:将遍历的list通过computed过滤一遍
<ul>
    <li v-for="item in newData">
        {{item.name}}
    </li>
</ul>

//js
computed: {
	newData: function() {
		return this.data.filter((item) => {
			return item.isActive
		})
	}
}

38、https和http有什么区别

        a)https的端口是443,http的端口是80,连接方式不一样;

        b)https有加密传输,http是明文的,https是有着更高的安全性;

        c)https需要申请证书,http不用

39、vue的优化策略

一、代码优化

        1、不要随意的在data里加变量,每个变量都会增加get和set的监听。

        2、页面合理的使用keep-alive标签来做缓存

        3、v-if有阻断性,可以使用v-if来减少渲染;且v-show也有缓存的特性,所以if和show的使用尽量合理。

        4、key的使用,保证了diff算法的快捷。

        5、提高组件的复用性,因为虚拟dom是挂载到window上的。

        6、对于只是展示,不会修改的对象,可以使用Object.ferrze()方法,冻结的对象不会被增加getter和setter。

        7、数据持久化使用防抖和节流的策略。

二、加载优化

        1、第三方插件按需加载。

        2、懒加载

40、路由守卫

        三种类型,分别是全局守卫、路由独享守卫、组件内路由守卫。独享守卫和组件内路由守卫没使用过。

        常用的是全局守卫,有前置守卫,解析守卫和后置守卫。场景一般是前置(校验token,加载进度条),后置(关闭进度条)。

       独享守卫的使用场景是进入该路由要做什么事,在这里处理。

        组件内路由守卫配合keep-alive使用。

41、slice和splice的区别

        slice是截取起始下标到结束下标,并且不会修改元数据;splice是起始下标和截取个数,并且会修改元数组。

42、vue重写了数组方法,为什么没有影响Array原型

43、map和forEach的区别

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值