你应该掌握的JavaScript高阶技能(八)

8. Array 数组

朋友们,大家好!本期内容是《你应该掌握的 JavaScript 高阶技能》的第八期,之前写过一篇数组讲解文章,但不够全面,留下了许多坑,所以本文的主要内容:es5数组方法总结(面试常客),望本文能对您有所帮助!☀️

(本文参考❤️)

  • JavaScript 高级程序设计(第四版)
  • JavaScript 忍者秘籍(第二版)

8.1 数组创建

  • 使用数组字面量
//字面量
let arr = [];
  • 使用内置 Array 构造函数
// 构造函数
// 语法:let arr = new Array(2);
// Array() 构造函数的参数为 2 ,表示生成一个两个成员的数组,每个位置都是空值。
// length 属性 --> 数组大小
let arr = new Array(2);
console.log(arr.length);// 2
console.log(arr);// []

// 没有使用 new 关键字
// let arr1 = Array(2);
// 等同于
let arr1 = new Array(2);
let arr2 = ["red","green","blue"];
console.log(arr[3] === undefined);// true
  • 考虑到语义性,以及与其他构造函数用法保持一致,建议总是加上 new

  • 使用 Array() 构造函数,不同的参数个数会导致不一致的行为。

在这里插入图片描述

注意事项

  • 优先使用数组字面量创建数组
  • 由于 JavaScript 的高度动态特性,无法阻止修改内置的 Array 构造函数,也就意味着 new Array() 创建的不一定是数组。因此,推荐坚持使用数组字面量。

在这里插入图片描述

  • JavaScript 在 length 属性上,表现出一种特殊的功能: 可以手动修改 length 属性的值。将 length 值改为比原有值的数,数组会被扩展新扩展出的元素均为 undefined;将 length 值改为比原有值的数,数组会被裁减

在这里插入图片描述

  • 数组空位问题

8.2 检测数组

  • 一个经典的 ECMAScript 问题是判断一个对象是不是数组。通常情况下,我们使用 instanceof 操作符。
let arr = [];
let obj = {};
console.log(arr instanceof Array);// true
console.log(obj instanceof Object);// true
// typeof 运算符无法判断
console.log(typeof arr);// "object"
let arr = [];
console.log(Array.isArray(arr));// true

8.3 实例方法

在这里插入图片描述

8.3.1 转换方法

  • valueOftoString 方法是一个所有对象都拥有的方法。其中,valueOf() 返回的还是数组本身toString() 返回由数组中每个值的等效字符串拼接而成的一个逗号分隔字符串
var arr = [1, 2, 3];
arr.valueOf(); // [1, 2, 3]
arr.toString(); // "1,2,3"
var arr2 = [1, 2, 3, [4, 5, 6]];
arr2.toString(); // "1,2,3,4,5,6"
  • join() 方法以指定参数作为分隔符,将所有数组成员连接为一个字符串返回。如果不提供参数,默认逗号分隔。
var arr = [1,2,3,4];
var str = arr.join(' '); 	// 空格作为分隔符
// '1 2 3 4'
var str1 = arr.join('|'); 	// | 作为分割符
// '1|2|3|4'
var str2 = arr.join(); 		// 逗号作为分隔符 
// '1,2,3,4'
  • 数组成员是 undefinednull 或空位,会被转成空字符串。
[undefined, null].join('#');
// '#'
['a',, 'b'].join('-');
// 'a--b'
  • 通过 call 方法,这个方法也可以用于字符串或类似数组的对象。
Array.prototype.join.call('hello', '-');
// [].join.call('hello','-')
// "h-e-l-l-o"
var obj = {0: 'a',1: 'b',length: 2}; // 类对象 有 length 属性
// [].join.call('obj','-')
Array.prototype.join.call(obj,'-');
// 'a-b'

8.3.2 栈方法

  • 栈(stack)是一种后进先出LIFO,Last-In-First-Out)的数据结构。ECMAScript 数组提供了 push()pop() 方法,以实现类似栈的行为。

  • push :在数组末尾添加添加一个或多个元素,返回添加新元素后的数组长度。注意,该方法会改变原数组

  • pop:从数组末尾删除元素,返回该元素。注意,该方法会改变原数组

在这里插入图片描述

let arr = [];
arr.push(1); // 1
arr.push('a') // 2
arr.push(true, {}); // 4
console.log(arr); // [1, 'a', true, {}]
console.log(arr.pop());// {}
console.log(arr); // [1, 'a', true]
  • 对空数组使用 pop 方法,不会报错,而是返回 undefined
console.log([].pop());// undefined

8.3.3 队列方法

  • 队列queue)是一种先进先出FIFO,First-In-First-Out)的数据结构。

  • 具体:队列在列表末尾添加数据(使用 push),但从列表开头获取数据并删除(使用 shift),二者结合使用,就构成了“先进先出”的队列结构。

  • shift() 方法用于删除数组的第一个元素,并返回该元素。注意,该方法会改变原数组

  • unshift() 方法用于在数组的第一个位置添加元素,并返回添加新元素数组长度。注意,该方法会改变原数组

在这里插入图片描述

var a = ['a','b','c'];
console.log(a.shift()); // 'a'
console.log(a); // ['b','c']
  • shift()方法可以遍历并清空一个数组。
// shift() 方法
var list = [1, 2, 3, 4];
var item;
// list.shift() 每次返回 list 数组中删除的【第一个】元素
// 缺点:数组元素不能是 0 或任何布尔值等于 false 的元素
while (item = list.shift()) {
  console.log(item);
}
console.log(list); // []

// unshift() 方法 --> 在数组的第一个位置添加元素
var a = ['a','b','c'];
a.unshift('x'); // 返回数组长度为 4
console.log(a); // ['x','a','b','c']
a.unshift('d','e'); // 6
console.log(a) // ['d','e','x','a','b','c']

8.3.4 操作方法

concat 方法
  • concat 方法用于多个数组的合并。它将新数组的成员,添加到原数组成员的后部,然后返回一个新数组,原数组不变
['hello'].concat(['world']);
// ["hello", "world"]

['hello'].concat(['world'], ['!']);
// ["hello", "world", "!"]

[].concat({a: 1}, {b: 2});
// [{ a: 1 }, { b: 2 }]

[2].concat({a: 1});
// [2, {a: 1}]
  • 除了数组作为参数,concat 也接受其他类型的值作为参数,添加到目标数组尾部。
[1, 2, 3].concat(4, 5, 6)
// [1, 2, 3, 4, 5, 6]
  • 如果数组成员包括对象,concat 方法返回当前数组的一个浅拷贝。
  • 浅拷贝指的是新数组拷贝的是对象的引用
var obj = { a: 1 };
var oldArray = [obj, true];

var newArray = oldArray.concat();
// [ { a: 1 }, true ];
obj.a = 2;
oldArray[0].a // 2
newArray[0].a // 2
  • 上面代码中,原数组包含一个对象,concat 方法生成的新数组包含这个对象的引用。改变原对象,新数组也会改变。
slice 方法
  • slice() 用于创建一个包含原数组中一个或多个元素的新数组。接收一个或两个参数:返回元素的开始索引和结束索引,该方法操作不影响原数组。
arr.slice(start,end);
  • 如果只有一个参数,则会返回该索引到数组末尾的所有元素
var arr = [1,2,3,4];
arr.slice(1);// [2,3,4]
  • 如果有两个参数,则返回从开始索引到结束索引对应的所有元素,其中不包含结束索引对应的元素。
var arr = ["red","green","blue","pink","yellow"];
arr.slice(2,4);// ['blue', 'pink']
  • 如果 slice() 方法的参数是负数,那么就以数组长度加上这个负值的结果确定位置。比如:在包含 5 个元素的数组上调用 slice(-2,-1),就相当于调用 slice(5+(-2),5+(-1)) 即 slice(3,4)
var a = ['a', 'b', 'c','d','e'];
a.slice(-2) // ["d", "e"]
a.slice(-2, -1) // ["d"]
  • 如果第一个参数大于等于数组长度,或者结束位置小于开始位置,则返回空数组。
var a = ['a', 'b', 'c'];
a.slice(4) // []
a.slice(2, 1) // []
  • slice()方法的一个重要应用,是将类似数组的对象转为真正的数组
Array.prototype.slice.call({ 0: 'a', 1: 'b', length: 2 })
// ['a', 'b']
Array.prototype.slice.call(document.querySelectorAll("div"));
Array.prototype.slice.call(arguments);
splice 方法
  • splice() 方法用于删除原数组的一部分成员,并可以在删除的位置添加新的数组成员,返回值是被删除的元素。该方法会改变原数组
arr.splice(start, count, addElement1, addElement2, ...);
  • splice 的第一个参数是删除的起始位置(从0开始),第二个参数是被删除的元素个数。如果后面还有更多的参数,则表示这些就是要被插入数组的新元素
var arr = ['a','b','c','d','e','f'];
arr.splice(3,2);// 返回删除元素 ["d","e"]
console.log(arr);
// 从下标索引为 3 的位置 删除 2 个数组元素

var arr1 = ['a','b','c','x','y'];
// 删除从下标索引为 3 的位置 删除 2 个数组元素且添加两个元素 'd' 和 'e'
arr1.splice(3,2,'d','e');// ['x', 'y']
console.log(arr1);// ['a', 'b', 'c', 'd', 'f']

// 起始位置如果是负数,就表示从倒数位置开始删除。
var a = ['a', 'b', 'c', 'd', 'e', 'f'];
a.splice(-4, 2); // ["c", "d"]
  • 如果只是单纯地插入元素,splice 方法的第二个参数可以设为 0
var arr = [1, 1, 1];
arr.splice(1, 0, 2) // []
arr // [1, 2, 1, 1]
  • 如果只提供第一个参数,等同于将原数组在指定位置拆分成两个数组。
var arr = [1, 2, 3, 4];
arr.splice(2) // [3, 4]
arr // [1, 2]

8.3.5 排序方法

  • 数组有两个方法可以用来对数组元素重新排序:reverse()sort(),两方法都返回它们数组的引用。

  • reverse()方法将数组元素反向排列。

let values = [1, 2, 3, 4, 5]; 
values.reverse(); 
console.log(values); // 5,4,3,2,1

手写 reverse 方法

function newReverse(arr) {
  for(let i = 0; i < (arr.length)/2; i++) {
      let temp = arr[i];
      arr[i] = arr[arr.length - i - 1];
      arr[arr.length - i - 1] = temp;
  }
  return arr;
}

function newReverse_1(arr) {
  let temp_arr = [];
  for(let i = arr.length - 1; i >= 0; i--) {
    temp_arr.push(arr[i]);
  }
  return temp_arr;
}
// 思考一下?其他实现方式?
  • sort 方法对数组成员进行排序,默认是按照字典顺序排序。排序后,原数组将被改变
['d', 'c', 'b', 'a'].sort();
// ['a', 'b', 'c', 'd']
[4, 3, 2, 1].sort();
// [1, 2, 3, 4]
[11, 101].sort();
// [101, 11]
[10111, 1101, 111].sort();
// [10111, 1101, 111]
  • 上面代码的最后两个例子,需要特殊注意。sort() 方法不是按照大小排序,而是按照字典顺序

  • 也就是说,数值会被先转成字符串,再按照字典顺序进行比较,所以101排在11的前面。

  • 如果想让 sort 方法按照自定义方式排序,可以传入一个函数作为参数。

[5,4,3].sort(function (a, b) {
  return a - b;
});
// [3,4,5]
  • sort参数函数本身接受两个参数,表示进行比较的两个数组成员。如果该函数的返回值大于0,表示第一个成员排在第二个成员后面默认升序);其他情况下,都是第一个元素排在第二个元素前面。
  • 我们可以定义一个比较函数 compare,用于判断哪个值应该排在前面。
// 比较函数 compare
// 比较函数接收两个参数,如果第一个参数应该排在第二个参数前面,就返回负值;如果两个参数相等,就返回 0;如果第一个参数应该排在第二个参数后面,就返回正值
// 下面代码注释的实现为降序
function compare(value1, value2) {
    if(value1 < value2) {
        return -1;
        // return 1;
    } else if(value1 > value2) {
        return 1;
        // return -1;
    } else {
        return 0;
    }
}
var arr = [1,2,3];
arr.sort(compare);
console.log(arr);
  • 应用
let arr = [
    { name: "张三", age: 30 },
    { name: "李四", age: 25 },
    { name: "王五", age: 46 },
    { name: "柳六", age: 45 },
    { name: "琪七", age: 20 },
];
arr.sort(function compare(o1,o2){
    return o1.age - o2.age;
});
console.log(arr);
/*[
	{ name: "琪七", age: 20 },
	{ name: "李四", age: 25 },
    { name: "张三", age: 30 },
    { name: "柳六", age: 45 },
    { name: "王五", age: 46 },
];*/

8.3.6 搜索和位置方法(一)

  • ECMAScript 提供两类搜索数组的方法:按严格相等搜索和按断言函数搜索(es6)

  • 严格相等搜索indexOf() lastIndexOf()

  • indexOf 方法返回给定元素在数组中第一次出现的位置,如果没有出现则返回 -1

var a = ['a', 'b', 'c'];
a.indexOf('b') // 1
a.indexOf('y') // -1
  • indexOf 方法还可以接受第二个参数,表示搜索的开始位置
['a', 'b', 'c'].indexOf('a', 1) // -1

indexOf 应用

例1:数组去重

核心算法:遍历原数组,使用原数组的元素查询新数组,如果该元素在新数组里面没有出现过,就添加该元素,否则不添加。

let fn = function(arr) {
    let newArr = [];
    for(let i = 0; i < arr.length; i++) {
        if(newArr.indexOf(arr[i]) === -1){
            newArr.push(arr[i]);
        }
    }
    return newArr;
}
var arr = ['a','b','x','a','c','b'];
var newArr = fn(arr);
console.log(newArr); // ['a','b','x','c']

例2:查找字符串中某个字符出现次数

利用 indexOf 的第二个参数

var str = 'abcoefoxyozzopp';
var index = str.indexOf('o');
var count = 0;
while(index !== -1) {
    console.log(index);
    count++;
    index = str.indexOf('o',index+1);
}
console.log('o出现的次数是' + count);
console.log('-------------------');
// 变式:如果是统计某个字符串在字符串数组中出现的次数
var str1 = ['red', 'blue', 'red', 'green', 'pink', 'red'];
var count1 = 0;
// var index1 = str1.indexOf('blue'); // 1
for(var index1 = str1.indexOf('red'); index1 !== -1; index1 = str1.indexOf('red',index1 + 1)) {
  console.log(index1);
  count1++;
}

// 封装一个函数 ==> 统计某个字符或字符串在字符串或字符串数组中出现的次数
function newIndexOf(str,ch) {
  // str 字符串或字符串数组 ch 查找字符或字符串
  var index = str.indexOf(ch); // 第一次出现位置
  var count = 0;
  while(index !== -1) {
      // 继续查找
      console.log(index);
      count++;
      index = str.indexOf(ch,index + 1);
  }
  return count;
  /*
  其他做法:while()部分改变
  for(var index = str.indexOf(ch); index !== -1; index = str.indexOf(ch,index+1)) {
     console.log(index);
     count++;
  }
  */
}
console.log('-------------------');
newIndexOf(str1,'red');
console.log('-------------------');
newIndexOf(str,'o');

在这里插入图片描述

例3:统计字符串中出现最多的字符和其次数

核心算法:利用 charAt() 遍历字符串 str,把每个字符都存储给对象 obj,如果对象没有该属性就设置值为1 ,如果存在就原值加1,然后遍历对象,得到最大值和该字符。

let fn1 = function(str) {
    let obj = {};
    let chars;
    for(let i = 0; i < str.length; i++) {
        chars = str.charAt(i);
        if(obj[chars]) {
          obj[chars]++;
        } else {
          obj[chars] = 1;
        }  
    }
    return obj;
}
let max = 0;
let ch = '';
for (let k in obj) {
  if(obj[k] > max) {
    max = obj[k];
    ch = k;
  }
}
console.log("字符串 " + ch + " 出现次数最多且为 " + max);

在这里插入图片描述

  • lastIndexOf 方法返回给定元素在数组中最后一次出现的位置,如果没有出现则返回-1
var a = [2, 5, 9, 2];
a.lastIndexOf(2) // 3
a.lastIndexOf(7) // -1
  • 注意,这两个方法不能用来搜索 NaN 的位置,即它们无法确定数组成员是否包含NaN
[NaN].indexOf(NaN) // -1
[NaN].lastIndexOf(NaN) // -1
  • 这是因为这两个方法内部,使用严格相等运算符(===)进行比较,NaN 是唯一一个不等于自身的值。

8.3.7 迭代方法

  • ECMAScript数组定义了 5 个迭代方法。
  • every some map forEach filter
  • 每个方法接收两个参数:以每一项为参数运行的函数以及可选的作为函数运行上下文的作用域对象(影响函数中 this 的值)
  • 传给每个方法的函数接收 3 个参数:数组元素、元素索引和数组本身。因具体方法而异,这个函数的执行结果可能会也可能不会影响方法的返回值。

挨个学习!<参数于下述不多赘述>

  • every():对数组每一项都运行传入的函数,如果对每一项函数都返回 true,则这个方法返回 true
// Array.prototype.every(Function,this);
let arr = [1,2,3,4,5,4,3,2,1];
let everyResult = arr.every((item,index,array) => item > 2); // false

在这里插入图片描述

在这里插入图片描述

  • some():跟 every 方法类似,对数组每一项都运行传入的函数,只要有一项函数返回 true,则这个方法返回 true
var arr = [1, 2, 3, 4, 5];
arr.some(function (elem, index, arr) {
      return elem >= 3;
});
// true
  • 注意:some 方法从数组的第 1 项开始执行回调函数,直到回调函数返回 true,意味着如果其中一项回调函数返回 true后续元素不再继续检查

在这里插入图片描述

在这里插入图片描述

  • 注意,对于空数组,some 方法返回 falseevery 方法返回true,回调函数都不会执行。
function isEven(x) { return x % 2 === 0 }
[].some(isEven); // false
[].every(isEven); // true
  • map 方法 :将数组的所有成员依次传入参数函数,然后把每一次的执行结果组成一个新数组返回
var arr = [1, 2, 3];
var arr1 = arr.map(function (n) {
  return n + 1;
});
console.log(arr,arr1);
var arr2 = arr.map(function(elem, index, arr) {
    console.log(elem,index,arr);
    return elem * index;
});
console.log(arr2);
// [0,2,6]
const ninjas = [ 
     {name: "Yagyu", weapon: "shuriken"}, 
     {name: "Yoshi", weapon: "katana"}, 
     {name: "Kuma", weapon: "wakizashi"} 
];

在这里插入图片描述

  • 下述示例通过 map() 方法的第二个参数,将回调函数内部的this 对象,指向 arr 数组。
var arr = ['a', 'b', 'c'];
[1, 2].map(function (item) {
  return this[item];
}, arr);
// ['b','c']
  • 如果数组有空位,map() 方法的回调函数在这个位置不会执行,会跳过数组的空位。
var f = function (n) { return 'a' };
[1, undefined, 2].map(f) // ["a", "a", "a"]
[1, null, 2].map(f) // ["a", "a", "a"]
[1, , 2].map(f) // ["a", , "a"]
// map 方法 不会跳过 undefined 和 null,但会跳过空位
  • forEach 方法:跟 map 方法类似。对数组每一项都运行传入的函数,没有返回值

  • 所以,如果数组遍历的目的是为了得到返回值,那么使用 map() 方法,否则使用 forEach() 方法。

function log(element, index, array) {
  console.log('[' + index + '] = ' + element);
}
[2, 5, 9].forEach(log);
// [0] = 2
// [1] = 5
// [2] = 9
  • 下述代码中,空数组 outforEach() 方法的第二个参数,意味着回调函数内部的 this 就指向 out
var out = [];

[1, 2, 3].forEach(function(elem) {
  this.push(elem * elem);
}, out);

out // [1, 4, 9]
  • 注意,forEach() 方法使用 break无法中断执行,总是会将所有成员遍历完。如果想中断执行,可以用 try/catchthrow new Error 来停止。

在这里插入图片描述

  • 如果希望符合某种条件时,就中断遍历,请要使用 for 循环。
  • forEach()方法也会跳过数组的空位。
var log = function (n) {
  console.log(n + 1);
};

[1, undefined, 2].forEach(log);
// 2
// NaN
// 3

[1, null, 2].forEach(log);
// 2
// 1
// 3

[1, , 2].forEach(log);
// 2
// 3
[1, 2, 3, 4, 5].filter(function (elem) {
  return (elem > 3);
})
// [4, 5]
// 将大于3的数组元素作为新数组返回
var arr = [0,1,'a',true,undefined,NaN,false,null];
var arr1 = arr.filter(Boolean);
console.log(arr1);
// [1,'a',true]

// 返回偶数位置的成员组成的新数组
[1, 2, 3, 4, 5].filter(function (elem, index, arr) {
  return index % 2 === 0;
});
// [1, 3, 5]

在这里插入图片描述

  • 下面代码中,过滤器 myFilter() 内部有 this 变量,它可以被 filter() 方法的第二个参数 obj 绑定,返回大于3的成员。
var obj = { MAX: 3 };
var myFilter = function (item) {
  if (item > this.MAX) return true;
};
var arr = [2, 8, 3, 4, 1, 3, 2, 9];
arr.filter(myFilter, obj) // [8, 4, 9]

8.3.8 归并方法

  • ECMAScript 为数组提供了两个归并方法:reduce()reduceRight() 。这两个方法都会迭代数组的所有项,并在此基础上构建一个最终返回值。
  • reduce() 方法从数组第一项开始遍历到最后一项。 而 reduceRight() 从最后一项开始遍历至第一项。
  • 这两个方法都接收两个参数:对每一项都会运行的归并函数,以及可选的以之为归并起点的初始值
  • 传给 reduce()reduceRight() 的函数接收 4 个参数:上一个归并值、当前项、当前项的索引和数组本身
    • 注意
      • 上一个归并值:第一次执行时,默认为数组的第一个成员;以后每次执行时,都是上一轮的返回值
      • 当前项:第一次执行时,默认为数组的第二个成员;以后每次执行时,都是下一个成员。
// 语法:
[/*...*/].reduce(function (
  a,   // 上一个归并值,必须
  b,   // 当前项,必须
  i,   // 当前索引,可选
  arr  // 原数组,可选
) {
  // ... ...

来点栗子 !

[1, 2, 3, 4, 5].reduce(function (a, b) {
  console.log(a, b);
  return a + b;
})
// 1 2
// 3 3
// 6 4
// 10 5
//最后结果:15
/*
执行流程:
	第一次执行:a 是数组的第一个成员且值为 1,b 是数组的第二个成员且值为 2。
	第二次执行:a 为上一轮的返回值 3,b为第三个成员且值为 3。
	第三次执行:a 为上一轮的返回值 6,b为第四个成员且值为 4。
	第四次执行:a 为上一轮返回值 10,b 为第五个成员且值为 5。至此所有成员遍历完成,整个方法的返回值就是最后一轮的返回值 15。
*/
  • 如果要对累积变量指定初值,可以把它放在 reduce() 方法和 reduceRight() 方法的第二个参数。
[1, 2, 3, 4, 5].reduce(function (a, b) {
  return a + b;
}, 10);
// 25
  • 建议总是加上第二个参数,这样比较符合直觉,每个数组成员都会依次执行 reduce() 方法的参数函数。另外,第二个参数可以防止空数组报错。
function add(prev, cur) {
  return prev + cur;
}

[].reduce(add);
// TypeError: Reduce of empty array with no initial value
[].reduce(add, 1);
// 1
  • 上面代码中,由于空数组取不到累积变量的初始值,reduce() 方法会报错。这时,加上第二个参数,就能保证总是会返回一个值。

在这里插入图片描述

  • reduceRight() 方法与 reduce 区别只是方向相反。
function subtract(prev, cur) {
  return prev - cur;
}

[3, 2, 1].reduce(subtract); // 0
[3, 2, 1].reduceRight(subtract); // -4

使用 reduce 还是 reduceRight 取决于遍历数组元素的方向。

应用:找出字符长度最长的数组成员

function findLongest(strArr) {
  return strArr.reduce(function (longest, item) {
    return item.length > longest.length ? item : longest;
  }, '');
}

var strArr = ['aaa','bb','cccc','ddd'];
console.log(findLongest(strArr)); // 'cccc'

第八期学习内容就这么多啦,如果您觉得内容不错的话,望您能关注🤞点赞👍收藏❤️一键三连!

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值