一文聊透This

This

在这里插入图片描述

this指向当前属性所在对象。

var A = {
  name: '张三',
  describe: function () {
    return '姓名:'+ this.name;
  }
};

var name = '李四';
var f = A.describe;
f() // "姓名:李四"

JavaScript 语言之中,一切皆对象,运行环境也是对象,所以函数都是在某个对象之中运行,this就是函数运行时所在的对象(环境)。这本来并不会让用户糊涂,但是 JavaScript 支持运行环境动态切换,也就是说,this的指向是动态的,没有办法事先确定到底指向哪个对象,这才是最让初学者感到困惑的地方。

this的指向分类

  1. 全局环境window

    只要是单独在全局环境下执行的函数,其this都是指向全局环境。

  2. 构造函数

    构造函数中的this,指的是实例对象。

    var Obj = function (p) {
      this.p = p;
    };
    

    上面代码定义了一个构造函数Obj。由于this指向实例对象,所以在构造函数内部定义this.p,就相当于定义实例对象有一个p属性。

    var o = new Obj('Hello World!');
    o.p // "Hello World!"
    
  3. 对象的方法

    this是没有链式调用的。只会指向当前运行的对象环境,如果在当前对象环境中找不到某变量,则不能进行比较。 就是内层的函数f2(函数内的函数)this不指向外部,而指向顶层对象。

    为了避免在同一对象内多次调用this其指向不统一的现象,可以通过一个变量固定this。在进行使用this。

    var o = {
      f1: function() {
        console.log(this);
        var that = this;
        var f2 = function() {
          console.log(that);
        }();
      }
    }
    
    o.f1()
    // Object
    // Object
    

    操作数组时,其回调函数中不要使用绑定this。

    var o = {
      v: 'hello',
      p: [ 'a1', 'a2' ],
      f: function f() {
        this.p.forEach(function (item) {
          console.log(this.v + ' ' + item);
        });
      }
    }
    
    o.f()
    // undefined a1
    // undefined a2
    

    原因同上,内层函数this指向顶层对象。

    解决办法:

    1. 使用中间变量

    2. 传递运行环境

      var o = {
        v: 'hello',
        p: [ 'a1', 'a2' ],
        f: function f() {
          this.p.forEach(function (item) {
            console.log(this.v + ' ' + item);
          }, this);
        }
      }
      
      o.f()
      // hello a1
      // hello a2
      

    尽量避免在回调函数中使用this。

改变this指向的方法

函数的四种调用方式
  1. 方法调用,作为对象的方法进行调用
  2. 函数表达式定义,并调用
  3. 构造函数调用
  4. apply、call调用

1. call

function函数特有的call方法可以指定函数内部的this指向。call方法的参数应该是一个对象,如果参数为空、null和undefined,则默认传入全局对象。

call的作用是把函数放入指定作用域执行。

2. apply

与call的区别在与传递参数为数组。

3. bind

bind绑定作用域后返回新的函数。

回调函数使用bind()方法。

手动实现call、apply、bind

  1. 手动实现call
    语法:

fun.call(thisArg, arg1, arg2, …)

其中thisArg取值可能为:

  • 不传,或者传null,undefined, this指向window对象
  • 传递另一个函数的函数名fun2,this指向函数fun2的引用
  • 值为原始值(数字,字符串,布尔值),this会指向该原始值的自动包装对象,如 String、Number、Boolean
  • 传递一个对象,函数中的this指向这个对象
Function.prototype.call = function (context) {
	// 判断context对象类型 如果为undefined或null则进行对象修改为windows
	  // 判断是否是undefined和null
  if (typeof context === 'undefined' || context === null) {
    context = window
  }
  //在context上定义函数fn
  context.fn = this;
  //保存传递进来的参数
  var args = [];
  for(var i = 1; i < arguments.length; i++){
  	args.push('arguments['+ i +']');
  }
  //通过eval函数执行fn方法
  var result = eval('context.fn(' + args.join(',') + ')');
  // 删除fn方法
  delete context.fn;
  //返回结果值
  return result;
}
  1. 手动实现apply
    apply语法:

fun.apply(thisArg, [argsArray])

argsArray: 一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 fun 函数。

Function.prototype.myApply = function (context, arr) {
  // 判断context对象类型 如果为undefined或null则进行对象修改为windows
  // 判断是否是undefined和null
  if (typeof context === 'undefined' || context === null) {
    context = window
  }
  //在context上定义函数fn,指向目标函数
  context.fn = this;
  var result;
  if (!arr) {
      result = context.fn();
  }
  else {
      //保存传递进来的参数
      var args = [];
      for(var i = 1; i < arguments.length; i++){
        args.push('arguments['+ i +']');
      }
      //通过eval函数执行fn方法
      result = eval('context.fn(' + args.join(',') + ')');
  }
  // 删除fn方法
  delete context.fn;
  //返回结果值
  return result;
}

总结:
apply和call都是用来改变函数的this指向的。
区别在于传参形式不同。
其实现方法类似于一次性的。
3. 手动实现bind

bind语法

func.bind(thisArg[, arg1[, arg2[, …]]])

bind功能:
bind() 方法会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this,之后的一序列参数将会在传递的实参前传入作为它的参数。(来自于 MDN )
特点:

  1. 返回一个函数
  2. 可以传入参数
  3. 支持柯里化传参
  4. 当bind绑定后的函数作为构造函数时,绑定的this失效。实例的原型对象指向绑定前的构造函数原型。但是参数会继续传递。
Function.prototype.myBind = function(context) {
	//判定其是否为函数,如果不是函数则抛出异常
	if(typeof this !== "function") {
		throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
	}
	//获取参数
    //这里的this指的是bind前的函数
	var self = this;
	var args = Array.prototype.slice.call(arguments, 1);
	var fNOP = function () {};
	var fBound = function () {
		//这里的 arguments是bind返回的函数传入的参数
		var bindArgs = Array.prototype.slice.call(arguments);
        //判断返回的函数中的this是全局函数调用还是new 构造函数调用
		return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
	}
	fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();
    return fBound;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值