CALL和APPLY的具体应用

call和apply是用来改变this指向的,并且把函数立即执行。
需求:需要把类数组转换为数组(call的应用)

类数组:具备和数组类似的结构(索引和length属性以及具备iterator可迭代性),但是并不是数组的实例(不能用数组原型上的方法),我们把这样的结构称为类数组结构。

function func(){
    console.log(arguments);
}
func(10,20,30,40);
/*
Arguments(4) [10, 20, 30, 40, callee: ƒ, Symbol(Symbol.iterator): ƒ]
0: 10
1: 20
2: 30
3: 40
callee: ƒ func()
length: 4
Symbol(Symbol.iterator): ƒ values()
__proto__: Object
*/
dir([])
//Array(0)
/*
length: 0
__proto__: Array(0)
*/

set出来的也是类数组结构

function func() {
    let args = new Set(arguments);
    console.log(args);
}
func(10, 20, 30, 40);
/*
Set(4) {10, 20, 30, 40}
0: 10
1: 20
2: 30
3: 40
size: (...)
__proto__: Set
*/

Tip:类数组的概念(以函数的实参集合为例):结构与数组类似,主要有以下三点数组也有的属性

  1. 以数字作为索引且从零开始;
  2. 有length属性,看元素有多少个。
  3. 有属性Symbol(Symbol.iterator): ƒ values()这个属性的意思是当前的那些索引为数字的属性是可被迭代的。
    之所以是类数组是因为结构与数组相同,但是原型直接指向Object,数组都是Array的实例,原型链直接指向Array,所以不是数组是类数组。
    常见的类数组:arguments、DOM元素集合、DOM节点集合(最常见的三种)
    Document.getElementById();
    Document.getElementByTagName();
    Document.getElementByClassName();等得到的都是类数组
    set出来的也是类数组结构
  • 把类数组转换为数组的常用方法
function func() {
    //1.Array.from();
    //let args = Array.from(arguments);

    //2.基于ES6的展开运算符
    //let args = [...arguments];

    //set默认实现数组去重
    //let args = [...new Set(arguments)];

    //3.手动循环
    let args = [];
    for (let i = 0; i < arguments.length; i++) {
        args.push(arguments[i]);
    }
    console.log(args);
}
func(10, 20, 30, 40);
//[10, 20, 30, 40]

数组的原型上有一个slice方法,此方法不传参数的时候,相当于直接拷贝一份原数组并且返回这个拷贝的新数组。
自己实现slice方法的此功能.数组原型上的方法操作的都是this,当前调用这个方法的数组

Array.prototype.slice = function slice() {
    //this->arr:当前调用这个方法的数组
    let arr = [];
    for (let i = 0; i < this.length; i++) {
        arr.push(this[i]);
    }
    return arr;
}
let arr = [10, 20, 30];
console.log(arr.slice());//[10, 20, 30]

由方法三“手动循环”和自己封装slice实现拷贝的代码对比可知:数组原型的方法操作的是this,方法三操作的是arguments,其它的都是利用循环。ARGUMENTS具备和数组类似的结构,所以操作数组的一些代码(例如:循环)也同样适用于ARGUMENTS;如果我们让ARRAY原型上的内置方法执行,并且让方法中的THIS变为我们要操作的类数组,那么就相当于我们在“借用数组原型上的方法操作类数组”,让类数组也和数组一样可以调用这些方法实现具体的需求。根据这个原理,我们可以找到数组原型上的方法,把方法执行,只是把方法中的this指向arguments就可以了。

function func() {
    // 4.ARGUMENTS具备和数组类似的结构,所以操作数组的一些代码(例如:循环)也同样适用于ARGUMENTS;如果我们让ARRAY原型上的内置方法执行,并且让方法中的THIS变为我们要操作的类数组,那么就相当于我们在“借用数组原型上的方法操作类数组”,让类数组也和数组一样可以调用这些方法实现具体的需求
    //let args = Array.prototype.slice.call(arguments); //[10, 20, 30, 40]

    //slice在数组的原型上,空数组是数组的实例,可以通过原型链__proto__找到数组原型上的方法,所以此处用空数组也可以
    //let args = [].slice.call(arguments);
    //console.log(args); //[10, 20, 30, 40]

    //类数组可以使用数组原型中的很多方法
    [].forEach.call(arguments, (item, index) => {
        console.log(item, index);
    });
}
func(10, 20, 30, 40);
/*[].forEach.call
10 0
20 1
30 2
40 3
*/

数字1在借用Object.prototype上的toString方法,Object.prototype上的toString是用来检测数据类型的,1借用此方法就可以检测出1的数据类型,也是借用机制

console.log(Object.prototype.toString.call(1));//'[object Number]'

我不是某个类的实例,不能直接用它原型上的方法,但是我可以让某个类原型上的方法执行,让方法中的THIS(一般是需要处理的实例)变为我,这样就相当于我在借用这个方法实现具体的功能 这种借用规则,利用的就是call改变this实现的,也是面向对象的一种深层次应用

需求:获取数组中的最大值(apply的应用)
  1. 利用sort排序,a-b升序;b-a降序
let arr = [12, 13, 2, 45, 26, 34];
let max = arr.sort((a, b) =>  b - a )[0];
console.log(max);
  1. 利用Math.max(n1,n2,n3……)数学函数
let arr = [12, 13, 2, 45, 26, 34];
let max = Math.max(...arr);
console.log(max);

apply方法是执行Math.max方法,并且将传递给apply方法的数组参数一个一个传递给执行的方法Math.max。正常Math.max执行其中的this是Math,现在还是Math。第一个参数写Math,并不是将this改变指向,而是想利用apply将数组在apply内部一项一项传递给Math.max方法执行。

let arr = [12, 13, 2, 45, 26, 34];
let max = Math.max.apply(Math,arr);
console.log(max);

3.利用forEach

let arr = [12, 13, 2, 45, 26, 34];
let max = arr[0];
/*
arr.forEach(item => {
    max <= item ? max = item : null;
});
*/
//我们已经获取类数组的第一项,循环的时候可以从第二项开始循环,但是上面的代码是从第一项开始的,所以可以修改下。
//arr.slice(1):从数组的索引为1即第二项开始拷贝至末尾。
arr.slice(1).forEach(item => {
    max < item ? max = item : null;
});
console.log(max);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值