关于this的几种情况以及call、apply的区别使用

关于THIS的几种情况 :

  • 给当前元素的某个事件行为绑定方法,方法中的THIS是当前元素本身「排除:DOM2在IE6~8中基于attachEvent进行事件绑定,这样处理方法中的this->window」
  • 方法执行,看方法前面是否有“点”,有“点”,“点”前面是谁THIS就是谁,没有“点”,THIS是window「严格模式下是undefiend」,特殊例外:

自执行函数中的THIS一般是window/undefined
回调函数中的THIS一般是window/undefined「当然某些方法中会做一些特殊的处理」
括号表达式有特殊性
........

  • 构造函数执行,构造函数体中的THIS是当前类的实例
  • 箭头函数中没有THIS「类似的还有块级上下文」,所以无论怎样去执行,怎样去修改,都没有用,如果函数中出现THIS,一定是其所在的上级上下文中的THIS
  • 在Function.prototype提供了三个方法:call/apply/bind,这三个方法都是用来强制改变函数中this指向的「每一个函数都是Function的实例,所以都可以调取这三个方法执行」

代码:

"use strict";
let obj = {
    name: 'zhufeng',
    fn() {
        /!* // this -> obj
        let self = this;
        setTimeout(function () {
            // console.log(this);
            // this -> window
            console.log(self);
        }, 1000); *!/

        setTimeout(() => {
            console.log(this);
            // this是上级上下文中的,也就是obj
        }, 1000);
    }
}
obj.fn();

call/apply/bind 三者的区别:

需求:把fn函数执行,并且让方法中的this是obj,再并且传递10/20分别给x和y形参

"use strict";
window.name = 'window';
const fn = function fn(x, y) {
    console.log(this, x + y);
};
let obj = {
    name: 'obj'
};
// fn(); //this->undefined  Uncaught TypeError: Cannot read property 'name' of undefined
// obj.fn(); //Uncaught TypeError: obj.fn is not a function  obj中不存在fn属性,之间没有关联


fn.call(obj, 10, 20);

fn.call(); //fn中的this->window/undefined  x=undefined  y=undefined
fn.call(null/undefined); //this->window & null/undefined  x=undefined  y=undefined
fn.call(10, 20); //this->10  x=20  y=undefined


fn.apply(obj, [10, 20]); 

fn首先作为Function的实例,基于proto找到Function.prototype.call方法,并且把找到的call方法执行.


apply和call只有一个区别:

call方法在设定给函数传递的实参信息的时候,是要求一个个传递实参值;但是apply是要求用户把所有需要传递的实参信息以数组/类数组进行管理的; 虽然要求这样写,但是内部最后处理的时候,和call一样,也是一项项的传递给函数的;

场景一:求一个数组中的最大值或者最小值

let arr = [10, 14, 23, 34, 20, 13];

// 1.排序处理 时间复杂度稍微高一些「sort内部 N^2」
console.log(arr.sort((a, b) => b - a)[0]); 
// 2.假设法  N
let max = arr[0],
    i = 1,
    len = arr.length,
    item;
for (; i < len; i++) {
    item = arr[i];
    if (item > max) {
        max = item;
    }
}
console.log(max); 
//3.Math.max获取最大值  推荐

console.log(Math.max(10, 14, 23, 34, 20, 13)); //=>34
console.log(Math.max(arr)); //=>NaN  方法本身是获取一堆数字中的最大值,需要把比较的数字一项项的传递给max方法才可以

// 想把数组中的每一项分别传递给max方法
let max = Math.max(...arr);
console.log(max); //=>34

let max = Math.max.apply(null, arr); //利用apply的机制「虽然传递给apply的是一个数组,但是apply内部会把数组中的每一项分别传递给对应的函数」;而且Math.max中用不到this,所以this改成谁无所谓,就是占个位而已;
console.log(max); //=>34

场景二:任意数求和「不确定实参的个数,所以无法基于设定形参接受」

  + 剩余运算符 ES6
  + arguments ES3
const sum = function sum(...params) {
    if (params.length === 0) return 0;
    // params -> 数组
    return params.reduce((total, item) => total + item, 0);
}; 
const sum = function sum() {
    let params = arguments; // params -> 类数组「不能直接使用数组的办法」
    if (params.length === 0) return 0;
    // 把类数组转换为数组
    //   + params = Array.from(params);  ES6+
    //   + params = [...params]; ES6+
    //   + ...
    let i = 0,
        len = params.length,
        arr = [];
    for (; i < len; i++) {
        arr[arr.length] = params[i]; //<==> arr.push(params[i]);
    }
    return arr.reduce((total, item) => total + item, 0);
}; 
Array.prototype.slice = function slice() {
    // 模拟的方法
    // this -> ary
    let i = 0,
        len = this.length,
        arr = [];
    for (; i < len; i++) {
        arr[arr.length] = this[i];
    }
    return arr;
};
// ary.slice()
// ary.slice(0)
// // 数组的克隆,把原始数组中的每一项都克隆一份给返回的新数组「浅克隆」

const sum = function sum() {
    let params = arguments;
    if (params.length === 0) return 0;
    params = [].slice.call(params);
    return params.reduce((total, item) => total + item, 0);
};

把类数组转换为数组:如果我们能把slice执行,并且让slice中的this是arguments,这样就相当于在迭代arguments中的每一项,把每一项赋值给新的数组集合 -> 也就是把类数组转换为数组
+ 如何让slice执行 Array.prototype.slice() / [].slice() .....
+ 如何改变slice中的this call/apply...

原理:“因为类数组的结果和数组非常的相似”,所以大部分操作数组的代码,也同样适用于类数组,在这个前提下,我们只需要把实现好的数组方法执行,让方法中的this变为类似组,就相当于类数组在操作这写代码,实现了类数组借用数组方法的目的,我们这种操作叫做“鸭子类型”


 const sum = function sum() {
    let params = arguments;
    if (params.length === 0) return 0;
    // 不转换了,直接借用即可
    return [].reduce.call(params, (total, item) => total + item, 0);
}


const sum = function sum() {
    let params = arguments;
    // params.__proto__ = Array.prototype; //修改原型链的指向  arguments可以直接使用数组原型上的任何方法
    params.reduce = Array.prototype.reduce;
    if (params.length === 0) return 0;
    return params.reduce((total, item) => total + item, 0);
};

console.log(sum()); //->0
console.log(sum(10)); //->10
console.log(sum(10, 20)); //->30
console.log(sum(10, 20, 30)); //->60

你可能感兴趣的:(关于this的几种情况以及call、apply的区别使用)