Array.prototype.slice.call()方法的理解

Array.prototype.slice.call()方法的理解

https://blog.csdn.net/huangbaokang/article/details/83411084

在看别人代码时,发现有这么个写法:[].slice.call(arguments, 0),这到底是什么意思呢?

1、基础
1)slice() 方法可从已有的数组中返回选定的元素。

start:必需。规定从何处开始选取。如果是负数,那么它规定从数组尾部开始算起的位置。也就是说,-1 指最后一个元素,-2 指倒数第二个元素,以此类推。

end:可选。规定从何处结束选取。该参数是数组片断结束处的数组下标。如果没有指定该参数,那么切分的数组包含从 start 到数组结束的所有元素。如果这个参数是负数,那么它规定的是从数组尾部开始算起的元素。

返回一个新的数组,包含从 start 到 end (不包括该元素)的 arrayObject 中的元素。该方法并不会修改数组,而是返回一个子数组。

2)call()和 apply()方法都是
** 在特定的作用域中调用函数 **
实际上等于设置函数体内 this 对象的值。apply 和 call 方法的第一个参数都是特定的作用域第二个参数不同,apply 第二个参数可以是 Array 的实例,也可以是 arguments 对象。call 方法需要逐个列出需要传递的参数。

3)arguments 对象指数与数组类似(它并不是 Array 的实例),但是可以使用方括号语法访问每一个元素,使用 length 来确定传递进来多少个参数。

4)Array.prototype.slice.call()可以理解为:改变数组的 slice 方法的作用域,在特定作用域中去调用 slice 方法,call()方法的第二个参数表示传递给 slice 的参数即截取数组的起始位置。

2、原理
Array.prototype.slice.call(arguments)能将具有 length 属性的对象(key 值为数字)转成数组。[]是 Array 的示例,所以可以直接使用[].slice()方法。

//特定的作用域中调用函数,实际上等于设置函数体内this对象的值。
var obj = { 0: "hello", 1: "world", length: 2 };
console.log(Array.prototype.slice.call(obj, 0)); //["hello", "world"]

没有 length 属性的对象

var obj = { 0: "hello", 1: "world" }; //没有length属性
console.log(Array.prototype.slice.call(obj, 0)); //[]

注意点:
1、 使用 apply 时要注意:apply 或 call 只是切换了函数内部 this 的调用,但是执行的方法依然是原始对象上的方法, 即使你在 Array.prototype.slice.call(obj)的 obj 上 覆盖了 slice 方法 ,依然会执行 Array 上的 slice 方法;

2、由于 apply 方法(或者 call 方法)也可以绑定函数执行时所在的对象,但是会立即执行函数,因此不得不把绑定语句写在一个函数体内。建议使用函数改变 this 指向时使用 bind 方法。

3、bind 方法每运行一次,就返回一个新函数,这会产生一些问题。比如,监听事件的时候,不能写成下面这样。

element.addEventListener("click", o.m.bind(o));

上面代码表示,click 事件绑定 bind 方法生成的一个匿名函数。这样会导致无法取消绑定,所以,下面的代码是无效的。

element.removeEventListener("click", o.m.bind(o));

正确的方法是写成下面这样:

var listener = o.m.bind(o);
element.addEventListener("click", listener);
//  ...
element.removeEventListener("click", listener);

第二

Array.prototype.slice.call 方法详解

在很多时候经常看到 Array.prototype.slice.call()方法,比如 Array.prototype.slice.call(arguments),下面讲一下其原理:

1、基本讲解 1.在 JS 里 Array 是一个类 slice 是此类里的一个方法 ,那么使用此方法应该 Array.prototype.slice 这么去用
slice 从字面上的意思很容易理解就是截取(当然你不是英肓的话) 这方法如何使用呢?
arrayObj.slice(start, [end]) 很显然是截取数组的一部分。

2.我们再看 call

call([thisObj [,arg1[arg2[[argN]]]]])

thisObj 是一个对象的方法
arrg1~argN 是参数

那么 Array.prototype.slice.call(arguments,1);这句话的意思就是说把调用方法的参数截取出来。

如:

function test(a,b,c,d)
    {
        var arg = Array.prototype.slice.call(arguments,1);
        alert(arg);
    }
test(“a”,“b”,“c”,“d”); //b,c,d

2、疑惑解答
先给个例子,这是 jqFloat 插件里的代码:

    if (element.data(‘jDefined’)) {
        if (options && typeof options === ‘object’) {
            methods.update.apply(this, Array.prototype.slice.call(arguments, 1));
        }
    } else {
        methods.init.apply(this, Array.prototype.slice.call(arguments, 1));
    }

多次用到 Array.prototype.slice.call(arguments, 1),不就是等于 arguments.slice(1) 吗?像前者那样写具体的好处是什么?这个很多 js 新手最疑惑的地方。那为什么呢?

因为 arguments 并不是真正的数组对象,只是与数组类似而已,所以它并没有 slice 这个方法,而 Array.prototype.slice.call(arguments, 1)可以理解成是让 arguments 转换成一个数组对象,让 arguments 具有 slice()方法。要是直接写 arguments.slice(1)会报错。

typeof arguments === “Object” //而不是 “Array”

3、真正原理
Array.prototype.slice.call(arguments)能将具有 length 属性的对象转成数组,除了 IE 下的节点集合(因为 ie 下的 dom 对象是以 com 对象的形式实现的,js 对象与 com 对象不能进行转换)
如:

var a={length:2,0:‘first’,1:‘second’};//类数组,有length属性,长度为2,第0个是first,第1个是second
console.log(Array.prototype.slice.call(a,0));// [“first”, “second”],调用数组的slice(0);

var a={length:2,0:‘first’,1:‘second’};
console.log(Array.prototype.slice.call(a,1));//[“second”],调用数组的slice(1);

var a={0:‘first’,1:‘second’};//去掉length属性,返回一个空数组
console.log(Array.prototype.slice.call(a,0));//[]

function test(){
    console.log(Array.prototype.slice.call(arguments,0));//[“a”, “b”, “c”],slice(0)
    console.log(Array.prototype.slice.call(arguments,1));//[“b”, “c”],slice(1)
}

test(“a”,“b”,“c”);

补充:
将函数的实际参数转换成数组的方法

方法一:var args = Array.prototype.slice.call(arguments);

方法二:var args = [].slice.call(arguments, 0);

方法三:

var args = [];
for (var i = 1; i < arguments.length; i++) {
    args.push(arguments[i]);
}
//最后,附个转成数组的通用函数

var toArray = function(s){
                    try{
                        return Array.prototype.slice.call(s);
                    } catch(e){
                        var arr = [];
                        for(var i = 0,len = s.length; i < len; i++){
                            //arr.push(s[i]);
                            arr[i] = s[i]; //据说这样比push快
                        }
                        return arr;
                    }
                }

第三遍 bind()使用方法

bind 方法,顾名思义,就是绑定的意思,到底是怎么绑定然后怎么用呢,下面就来说说我对这个方法的理解。

语法

fun.bind(this,arg1,arg2,…)

bind()方法会创建一个新的函数,称为绑定函数,fun 方法在 this 环境下调用

该方法可传入两个参数,第一个参数作为 this,第二个及以后的参数则作为函数的参数调用

实例

1.创建绑定函数

1 this.a = 1;
 2 var module = {
 3     a : 2,
 4     getA:function() {
 5     return this.a;
 6     }
 7 };
 8 module.getA();//2
 9
10 var getA1 = module.getA;
11 // getA在外部调用,此时的this指向了全局对象
12 getA1();//1
13
14 // 再把getA1方法绑定到module环境上
15 var getA2 = getA1.bind(module);
16 getA2();

从上面的例子可以看出,为什么要创建绑定函数,就是当我们调用某些函数的时候是要在特定环境下才能调用到,所以我们就要把函数放在特定环境下,就是使用 bind 把函数绑定到特定的所需的环境下。

2.让函数拥有预设的参数

使用 bind()方法使函数拥有预设的初始参数,这些参数会排在最前面,传给绑定函数的参数会跟在它们后面

1 function list(){
 2     // 让类数组arguments拥有数组的方法slice,这个函数实现了简单把类数组转换成数组
 3     return Array.prototype.slice.call(arguments);
 4 }
 5
 6 list(1,2,3);//[1,2,3]
 7
 8 //给list绑定一个预设参数4
 9 var list1 = list.bind(undefined,4);
10
11 list1();//[4]
12
13 list1(1,2,3);//[4,1,2,3]

3.setTimeout 的使用

正常情况下,调用 setTimeout 的时候 this 会指向全局对象,但是使用类的方法时我们需要指向类的实例,所以要把 this,绑定要回调函数方便继续使用实例

function Fun1() {
  this.name = 1;
}
Fun1.prototype.fun2 = function () {
  window.setTimeout(this.fun3.bind(this), 1000);
};
Fun1.prototype.fun3 = function () {
  console.log("name:" + this.name); //name:1
};
var fun = new Fun1();
fun.fun2();

4.快捷方法–把类数组转换成数组

第一种方法是使用 apply 方法

1 function fun1() {
2     var slice = Array.prototype.slice;
3     return slice.apply(arguments);
4 }
5
6 fun1(1,2,3);//[1,2,3]

第二种方法是使用 call 方法和 bind 方法一起使用

function fun2() {
  var unboundSlice = Array.prototype.slice;
  // 把函数的call方法绑定在数组slice方法上,之后再给call方法传递参数
  var slice = Function.prototype.call.bind(unboundSlice);
  return slice(arguments);
}

fun2(1, 2, 3); //[1,2,3]

第四遍 javaScript bind() 的用法

bind() 怎么用?码的过程中用了无数次,$(‘button’).bind(‘click’, function() {…} );,结果要给室友解释时,竟无从开口,果然,行不行,溜一溜就知道了。赶紧把自己关进小黑屋,再磨磨剑。

MDN 官方文档对 bind() 的定义

The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.

室友读完后第一反应,能不能说人话 ??简单说,bind() 是用来控制调用函数的范围(全局、某个类等等),在是 bind(arg1) 这个函数被调用时,arg1 是调用 bind() 函数里面的 this,不管这个函数被调用多少次,这个函数里的 this 一直是这个 arg1。貌似,有点像人话,但你 TM 在说什么?

只好用 MDN Function.prototype.bind() 官方文档两个栗子来解释 bind() 的用法。

this.x = 9;
var module = {
  x: 81,
  getX: function () {
    return this.x;
  },
};

module.getX(); // 81

var retrieveX = module.getX;
retrieveX(); // 9

var boundGetX = retrieveX.bind(module);
boundGetX(); // 81

module.getX(); 的结果是 81,因为 getX 里的 this 是 module, 所以 this.x 是 module 里的 x = 81。
retrieveX(); 的结果是 9, 因为这时相当于 var retrieveX = function() { return this.x; };, retrieveX(); 相当于在全局跑了遍函数里的内容,this.x 是 全局的 this.x = 9。
关键来了:var boundGetX = retrieveX.bind(module);,bind(module) 的作用是每当调用 retrieveX() 这个函数时,这个函数里的 this 都是 module,替换掉了全局的 this,在 boundGetX() 调用时,返回的是 module.x,所以是 81。

再看第二个栗子:

function LateBloomer() {
  this.petalCount = 1;
}

LateBloomer.prototype.bloom = function () {
  console.log(this.declare()); //undefined
  console.log(this.declare.bind(this)); //declare 这个函数
  window.setTimeout(this.declare.bind(this), 1000);
};

LateBloomer.prototype.declare = function () {
  console.log("I am a beautiful flower with " + this.petalCount + " petals!");
};

var flower = new LateBloomer();
flower.bloom();
// 执行结果:1s 后,打印出 'I am a beautiful flower with 1 petals!'

这个栗子的核心是 window.setTimeout(this.declare.bind(this), 1000); 首先这里的两个 this 都指的是 LateBloomer。这个栗子一开始看来显得有点智障,为了要打印出那句话,直接用 window.setTimeout(this.declare(), 1000); 不就好了,干嘛还要 bind()?通常,像我这种质疑这种成千上万的人看过没有问题的文档的才是智障那个。

这个栗子主要是为了讲 bind() 的返回值。window.setTimeout(function, milliseconds) 这是这个函数的用法,function 是延迟的函数,milliseconds 是延迟的时间。如果 console.log(this.declare()); 一下就会发现, 结果是 undefined, console.log(this.declare.bind(this)); 的结果则是 declare 这个函数,在使用 bind() 了之后,会创建一个新的函数。

这也就是为什么要用 window.setTimeout(this.declare.bind(this), 1000);,只有这样,才能看到 1 秒延迟,不信你删掉 bind()试试看。

综上,重新看下那句非人话。在 bind(arg1, arg2) 被调用时,会创建一个新函数,这个新函数的 this,都是 arg1,也就是第一个参数。

「干货」细说 call、apply 以及 bind 的区别和用法

https://segmentfault.com/a/1190000018017796

前言

上一篇文章 《「前端面试题系列 4」this 的原理以及用法》 中,提到了 call 和 apply。

它们最主要的作用,是改变 this 的指向。在平时的工作中,除了在写一些基础类,或者公用库方法的时候会用到它们,其他时候 call 和 apply 的应用场景并不多。

不过,突然遇到的时候,需要想一下才能转过弯来。所以今天,就让我们好好地探究一下,这两个方法的区别以及一些妙用。最后,还会介绍与之用法相似的 bind 的方法。

call 和 apply 的共同点
它们的共同点是,都能够改变函数执行时的上下文,将一个对象的方法交给另一个对象来执行,并且是立即执行的。

为何要改变执行上下文?举一个生活中的小例子:平时没时间做饭的我,周末想给孩子炖个腌笃鲜尝尝。但是没有适合的锅,而我又不想出去买。所以就问邻居借了一个锅来用,这样既达到了目的,又节省了开支,一举两得。

改变执行上下文也是一样的,A 对象有一个方法,而 B 对象因为某种原因,也需要用到同样的方法,那么这时候我们是单独为 B 对象扩展一个方法呢,还是借用一下 A 对象的方法呢?当然是借用 A 对象的啦,既完成了需求,又减少了内存的占用。

另外,它们的写法也很类似,调用 call 和 apply 的对象,必须是一个函数 Function。接下来,就会说到具体的写法,那也是它们区别的主要体现。

call 和 apply 的区别
它们的区别,主要体现在参数的写法上。先来看一下它们各自的具体写法。

call 的写法

Function.call(obj,[param1[,param2[,…[,paramN]]]])
需要注意以下几点:

调用 call 的对象,必须是个函数 Function。

call 的第一个参数,是一个对象。 Function 的调用者,将会指向这个对象。如果不传,则默认为全局对象 window。

第二个参数开始,可以接收任意个参数。每个参数会映射到相应位置的 Function 的参数上。但是如果将所有的参数作为数组传入,它们会作为一个整体映射到 Function 对应的第一个参数上,之后参数都为空。

function func(a, b, c) {}

func.call(obj, 1, 2, 3);
// func 接收到的参数实际上是 1,2,3

func.call(obj, [1, 2, 3]);
// func 接收到的参数实际上是 [1,2,3],undefined,undefined

apply 的写法

Function.apply(obj[,argArray])
需要注意的是:

它的调用者必须是函数 Function,并且只接收两个参数,第一个参数的规则与 call 一致。

第二个参数,必须是数组或者类数组,它们会被转换成类数组,传入 Function 中,并且会被映射到 Function 对应的参数上。这也是 call 和 apply 之间,很重要的一个区别。

func.apply(obj, [1, 2, 3]);
// func 接收到的参数实际上是 1,2,3

func.apply(obj, {
  0: 1,
  1: 2,
  2: 3,
  length: 3,
});
// func 接收到的参数实际上是 1,2,3

什么是类数组?
先说数组,这我们都熟悉。它的特征有:可以通过角标调用,如 array[0];具有长度属性 length;可以通过 for 循环或 forEach 方法,进行遍历。

那么,类数组是什么呢?顾名思义,就是具备与数组特征类似的对象。比如,下面的这个对象,就是一个类数组。

let arrayLike = {
  0: 1,
  1: 2,
  2: 3,
  length: 3,
};

类数组 arrayLike 可以通过角标进行调用,具有 length 属性,同时也可以通过 for 循环进行遍历。

类数组,还是比较常用的,只是我们平时可能没注意到。比如,我们获取 DOM 节点的方法,返回的就是一个类数组。再比如,在一个方法中使用 arguments 获取到的所有参数,也是一个类数组。

但是需要注意的是:类数组无法使用 forEach、splice、push 等数组原型链上的方法,毕竟它不是真正的数组。

call 和 apply 的用途

下面会分别列举 call 和 apply 的一些使用场景。声明:例子中没有哪个场景是必须用 call 或者必须用 apply 的,只是个人习惯这么用而已。

call 的使用场景

1、对象的继承。如下面这个例子:
function superClass() {
  this.a = 1;
  this.print = function () {
    console.log(this.a);
  };
}

function subClass() {
  superClass.call(this);
  this.print();
}

subClass();
// 1

subClass 通过 call 方法,继承了 superClass 的 print 方法和 a 变量。此外,subClass 还可以扩展自己的其他方法。

2、借用方法。还记得刚才的类数组么?如果它想使用 Array 原型链上的方法,可以这样:

let domNodes = Array.prototype.slice.call(document.getElementsByTagName(“*”));
这样,domNodes 就可以应用 Array 下的所有方法了。

apply 的一些妙用

1、Math.max。用它来获取数组中最大的一项。

let max = Math.max.apply(null, array);
同理,要获取数组中最小的一项,可以这样:

let min = Math.min.apply(null, array);
2、实现两个数组合并。在 ES6 的扩展运算符出现之前,我们可以用 Array.prototype.push 来实现。

let arr1 = [1, 2, 3];
let arr2 = [4, 5, 6];

Array.prototype.push.apply(arr1, arr2);
console.log(arr1); // [1, 2, 3, 4, 5, 6]

bind 的使用
最后来说说 bind。在 MDN 上的解释是:bind() 方法创建一个新的函数,在调用时设置 this 关键字为提供的值。并在调用新函数时,将给定参数列表作为原函数的参数序列的前若干项。

它的语法如下:

Function.bind(thisArg[, arg1[, arg2[, …]]])
bind 方法 与 apply 和 call 比较类似,也能改变函数体内的 this 指向。不同的是,bind 方法的返回值是函数,并且需要稍后调用,才会执行。而 apply 和 call 则是立即调用。

来看下面这个例子:

function add(a, b) {
  return a + b;
}

function sub(a, b) {
  return a - b;
}

add.bind(sub, 5, 3); // 这时,并不会返回 8
add.bind(sub, 5, 3)(); // 调用后,返回 8

如果 bind 的第一个参数是 null 或者 undefined,this 就指向全局对象 window。

总结
call 和 apply 的主要作用,是改变对象的执行上下文,并且是立即执行的。它们在参数上的写法略有区别。

bind 也能改变对象的执行上下文,它与 call 和 apply 不同的是,返回值是一个函数,并且需要稍后再调用一下,才会执行。

Function.prototype.bind()

bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。

 //bind 基本使用
      var x = 10;
      function show() {
        console.log(this.x);
      }

      var DuyiO = {
        x: 20,
      };
      //返回一个新的函数  功能与show一样,this不一样; this等于传入的参数DuyiO
      var newShow = show.bind(DuyiO);

      newShow();
    ```
绑定指在哪个作用域下执行
const module = {
  x: 42,
  getX: function () {
    return this.x;
  },
};

const unboundGetX = module.getX;
console.log(unboundGetX()); // The function gets invoked at the global scope
// expected output: undefined

//绑定指在哪个作用域下执行
const boundGetX = unboundGetX.bind(module);
console.log(boundGetX());
// expected output: 42
单对象编程
<button id="btn">duyi</button>;
var list = {
  init: function () {
    this.ms = "duyi";

    this.dom = document.getElementById("btn");
    this.bindEvent();
  },
  bindEvent: function () {
    //this = list
    //改变this
    this.dom.onclick = this.showMessage.bind(this, "hello", "world"); //参数会放到实例参数的前面
  },
  showMessage: function (info1, info2, e) {
    //this = 调用这个函数的this.dom
    console.log(this.ms);
  },
};

list.init();

总结

function A() {}
var o = {};

var x = 1;
var y = 2;
var z = 3;
var B = A.bind(o, x, y);
B("c");

// 1.函数A调用bind方法时,需要传递的参数 o, x, y, z....
// 2.返回新的函数B
// 3.函数B在执行的时候,具体的功能实际上还是使用的A, 只不过this指向变成o   window
// 4. 函数B在执行的时候,传递的参数会拼接到 x, y, z 后面一并在内部
// 5. new B() 构造函数依旧是A, 而且o不会起到任何作用
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0, user-scalable=no"
    />
    <title>bind</title>
  </head>
  <body>
    <button id="btn">click</button>

    <script>
      function LateBloomer() {
        this.petalCount = 1;
      }

      LateBloomer.prototype.bloom = function () {
        console.log(this.declare()); //undefined
        console.log(this.declare.bind(this)); //declare 这个函数
        window.setTimeout(this.declare.bind(this), 1000);
      };

      LateBloomer.prototype.declare = function () {
        console.log(
          "I am a beautiful flower with " + this.petalCount + " petals!"
        );
      };

      var flower = new LateBloomer();
      flower.bloom();
      // 执行结果:1s 后,打印出 'I am a beautiful flower with 1 petals!'
    </script>
  </body>
</html>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: array.prototype.slice.call()是一种将类数组对象转换为数组的方法。它通过调用Array原型上的slice方法,并将类数组对象作为其上下文来实现。这个方法可以用于将一些类似数组的对象(比如arguments对象)转换为真正的数组,以便于进行数组操作。 ### 回答2: array.prototype.slice.call()是JavaScript中的一个方法,用于将类数组对象(如arguments对象)转换为真正的数组。 array.prototype.slice是数组的一个内置方法,用于从已有的数组中返回选定的元素。这个方法通常用于截取数组中的一部分元素创建一个新的数组,不会对原数组产生影响。 而调用call()方法slice方法中的this指向传入的对象。由于类数组对象并不具有slice方法,因此需要通过这种方式将slice方法应用在类数组对象上。 使用array.prototype.slice.call()方法,可以将类数组对象转换为真正的数组。它的工作原理是通过在调用slice方法时将类数组对象作为this参数传入,使得slice方法可以在类数组对象上正确地运行并返回一个新的数组。 使用array.prototype.slice.call()方法的一个常见场景是将类数组对象转换为真正的数组以方便进行数组操作,例如使用数组的forEach、map等方法进行遍历或处理。另外,使用该方法还可以将字符串转换为数组,因为字符串也是类数组对象。 总结来说,array.prototype.slice.call()是将类数组对象转换为真正的数组的一种常用方法,它通过在调用slice方法时将类数组对象作为this参数传入,使得slice方法可以正确地运行并返回一个新的数组。 ### 回答3: array.prototype.slice.call() 是一种将类数组对象(array-like object)转化为真正的数组的方法。类数组对象是指具有与数组类似的结构,但却不是真正的数组,比如 arguments 对象,DOM 元素集合等。 array.prototype.slice.call() 的作用是将类数组对象转化为数组,并返回一个新的数组。该方法利用了 Array.prototype.slice() 方法,将其绑定到类数组对象上,然后调用该方法进行数组转化。由于 slice() 方法可以接收参数来指定切片的起始和结束位置,所以可以将整个类数组对象拷贝到新的数组中。 具体来说,使用 array.prototype.slice.call() 的步骤如下: 1.在类数组对象上调用 call() 方法,并将 Array.prototype.slice() 方法作为该方法的参数; 2.通过该调用,将类数组对象绑定到 Array.prototype.slice() 方法上,使其成为该方法的执行环境; 3.执行 Array.prototype.slice() 方法,将类数组对象转化为数组,并返回新的数组。 例如,我们可以使用 array.prototype.slice.call(arguments) 将函数参数对象 arguments 转化为数组。 需要注意的是,array.prototype.slice.call() 方法只能将具有索引属性(index property)的类数组对象转化为数组,而不能转化不具有索引属性的对象。 总之,array.prototype.slice.call() 是一种将类数组对象转化为数组的常用方法,可以方便地处理一些本应使用数组操作的对象。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值