js(七)this

1、this的五种情况

this执行主体,谁把它执行的「和在哪创建&在哪执行都没有必然的关系」

  • 函数执行,看方法前面是否有“点”,没有“点”,this是window「严格模式下是undefined」,有“点”,“点”前面是谁this就是谁
  • 给当前元素的某个事件行为绑定方法,当事件行为触发,方法中的this是当前元素本身「排除attachEvent」
  • 构造函数体中的this是当前类的实例
  • 箭头函数中没有执行主体,所用到的this都是其所处上下文中的this
  • 可以基于Function.prototype上的call/apply/bind去改变this指向

2、call、apply、bind

callapply的唯一区别是参数,第一个参数都是要绑定的上下文
call可以有多个参数,剩余参数被认为是传递给方法的参数
apply第二个参数为一个数据,是传递给方法参数的集合

func.call(obj, 10, 20);
func.apply(obj, [10, 20]);

call

原理:利用**'点’定THIS**机制,context.xxx=self “obj.xxx=func” => obj.xxx()
注意:原始值比如n=10;n.xxx=100不报错,但是n.xxx拿不到,所以判断如果传入的this上下文不是对象,转为对象

  • 把func中的this改为obj
  • 并且把params接收的值当做实参传递给func函数
  • 并且让func函数立即执行
Function.prototype.call = function call(context, ...params) {
    // this/self->func  context->obj  params->[10,20]
    let self = this,
        key = Symbol('KEY'), //加的属性不能影响原始obj中的属性
        result;
    context == null ? context = window : null;
    !/^(object|function)$/i.test(typeof context) ? context = Object(context) : null; //原始值加属性不报错,但是拿不到值 判断是否为对象 不是则改为对象
    context[key] = self;
    result = context[key](...params);
    delete context[key];
    return result;
};

bind

  • 和call/apply的区别:并没有把func立即执行
  • 把传递进来的obj/10/20等信息存储起来「闭包」
  • 执行bind返回一个新的函数 例如:proxy,把proxy绑定给元素的事件,当事件触发执行的是返回的proxy,在proxy内部,再去把func执行,把this和值都改变为之前存储的那些内容

Function.prototype.bind = function bind(context, ...params) {
    // this/self->func  context->obj  params->[10,20]
    let self = this;

    return function proxy(...args) {
        // 把func执行并且改变this即可  args->是执行proxy的时候可能传递的值
        self.apply(context, params.concat(args));
    };
};

普通函数的调用 + 柯里化的实现
如果是正常的普通函数的调用方法来调用bind返回的函数,那么直接返回像调用call函数那样返回,第一个参数值就是传入bind的asThis值。注意要将前后传入的参数(两次…arg收集到的)同时收集到call函数的参数中,从而实现柯里化。

通过new操作符调用 + 柯里化的实现
如果通过new来调用bind生成的函数(绑定函数)情况又有所不同了

Function.prototype.bind2 = function(asThis,...args) {
    //这里的this就是调用bind2的函数(普通函数this指向调用者)
    if (typeof this !== "function") {
        throw new TypeError("Not a Function");
    }
    //  这里保存调用bind的函数以及传给bind后面的参数。
    var origin = this;
    let  args0 = args;
 
    function bound(...args) {
        if(this instanceof bound) 
            //如果是new的形式来使用绑定函数的
            return new origin( ...args0,...args)
        else 
            //如果是普通函数的形式来使用绑定函数的(基本上与call、apply无区别,实现柯里化即可)
            return origin.call(asThis, ...args0,...args);
      }
      bound.prototype = origin.prototype;
      return bound;
  }

3、鸭子方法

类数组像数组「结构、一些操作…」,我们让其用数组上的方法「不能直接用」

/* Array.prototype.slice = function slice() {  不传参数是浅拷贝,克隆arr返回一个新数组
    let result = [];
    for (let i = 0; i < this.length; i++) {
        result.push(this[i]);
    }
    return result;
}; */
// 克隆arr返回一个新数组
// arr.slice();

// 掌握this的好玩应用:鸭子类型
//   像鸭子,我们就说他是鸭子 -> 类数组像数组「结构、一些操作...」,我们让其用数组上的方法「不能直接用」
function func() {
    console.log(arguments); //js把传入到这个函数的全部参数存储在一个叫做arguments的东西里面
    // 把arguments变为数组,这样就可以用数组的办法了:Array.from/[...arguments]/...
    let result = [];
    for (let i = 0; i < arguments.length; i++) {
        result.push(arguments[i]);
    } 
    //  Array.prototype.slice -> [].slice  
    // let result = Array.prototype.slice.call(arguments);
    // console.log(result);

    [].forEach.call(arguments, item => {
        console.log(item);
    });
}
func(10, 20, 30); 

4、绑定规则

new绑定》显示绑定》隐式绑定》默认绑定

默认绑定:独立函数调用,可以把默认绑定看作是无法应用其他规则时的默认规则,this指向全局对象。

var name = 'Jenny';
function person() {
    return this.name;
}
console.log(person());  //Jenny

隐式绑定
当函数引用有上下文对象时,隐式绑定规则会把函数中的this绑定到这个上下文对象。对象属性引用链中只有上一层或者说最后一层在调用中起作用。

函数还可以作为某个对象的方法调用,这时this就指这个上级对象

function test() {
  console.log(this.x);
}
 
var obj = {};
obj.x = 1;
obj.m = test;
 
obj.m(); // 1

这个函数中包含多个对象,尽管这个函数是被最外层的对象所调用,this指向的也只是它上一级的对象

你可能感兴趣的:(javaScript,javascript,前端,开发语言)