JavaScript——this解析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/only_invarably/article/details/79917947

this全面解析


《你不知道的js 上卷》


this关键字是javascript中最复杂的机制之一,他是一个很特别的关键字,被自动定义在所有函数的作用域中

this提供了一种更优雅的方式来隐式的“传递”一个对象,因此可以将API设计的更加简洁并且易于复用

var a = 'jokey';
function func() {
   return this.a;
}

这个例子就是隐式的将window对象传入到func()函数中,实现对外部属性的调用。通常容易将this理解成指向函数自身,然而并不是这样,对于this的调用,是在运行时绑定的,他的上下文取决于函数调用时的各种条件,this的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。

当一个函数调用时会创建一个记录(有时候也称为执行上下文)。这个记录会包含函数是在哪里被调用,函数的调用方法,传入的参数等信息。this就是记录的其中一个属性,会在函数执行的过程中用到

理解函数在代码中的调用位置

调用位置就是函数在代码中被调用的位置,而不是声明的位置。最重要是分析调用栈(就是为了到达当前执行位置所调用的所有函数)我们所关心的调用位置就在当前正在执行的函数的前一个调用中

function func() {
  //当前的调用栈是 func  作用域是全局作用域
  bar();    // bar 函数的调用位置
}
function bar () {
  // 当前的调用栈是 bar
  foo();   // foo 函数的调用位置
}
function foo () {
  // 当前的调用栈是 foo
  console.log('foo');
}
func();  // func 函数的调用位置

根据调用位置判断this的绑定

this绑定规则

默认绑定

在没有其他规则时,所使用的默认规则

var a = 'jokey';
function func() {
  return this.a;
}
func (); // 'jokey'

分析可知,func函数的调用位置为全局作用域中,当我们调用func函数时,this.a被解析成了全局变量a,这就是函数在调用时应用了默认绑定规则,此时的this指向全局对象

但是如果func函数是在严格模式下(strict mode),那么全局对象将无法使用默认绑定,因此this会绑定到undefined,也就是说,严格模式下this绑定与函数的调用位置无关

func函数在严格模式下:

  function func() {
    "use strict";
    console.log(this.a);
  }
  var a = 2;
  foo();  // TypeError: this is undefined

func函数在非严格模式下:

  function func() {
    console.log(this.a);
  }
  var a = 2;
  (function () {
    "use strict";
    func(); // 2
  })();

隐式绑定

这需要考调用位置是否有上下文对象

function foo() {
  console.log(this.a);
}
var obj = {
  a:2,
  foo:foo
};
obj.foo();

分析:foo函数是在全局环境下定义的,而obj对象中包含了foo函数的引用,因此当通过obj.foo()
调用时,obj对象相当于是下面的样子

 var obj = {
   a : 2,
   foo: function () {
     console.log(this.a);
   }
 }

foo函数的上下文对象为obj,则隐式绑定规则会把函数调用中的this绑定到这个上下文对象,此时this.a和
obj.a是一样的

对象属性引用链中只有最顶层或者说最后一层会影响调用位置。

function foo() {
  console.log(this.a);
}

var obj2 = {
  a:42,
  foo:foo
};

var obj1 = {
  a:2,
  obj2:obj2
};

obj1.obj2.foo(); // 42

分析:foo函数引用在obj2对象中绑定,调用时是通过obj2调用的,因此this指向的就是obj2对象

隐式丢失

一个最常见的this绑定问题就是被隐式绑定的函数会丢失绑定对象,它会应用默认绑定的,将this绑定到全局对象或者undefined上

function func() {
  console.log(this.a);
}
var obj = {
  a:23,
  func:func
}
var bar = obj.func;
var a = "loop";
bar(); // "loop"

分析:当执行var bar = obj.func;时,实际上是将函数func的引用值赋给了bar,则执行bar实际上就是在
执行func函数,因此this就会采用默认绑定,指向的是全局对象(当然是在非严格模式下),严格模式下就是undefined

将回调函数作为参数传递时,也会发生这种状况

function foo() {
  console.log(this.a);
}
function func(fn) {
  fn();
}
var obj = {
  a:3,
  foo:foo
};
var a = "oops, global";
func(obj.foo); //  "oops, global"

分析:obj对象中保存的是对函数foo的引用,因此执行func(obj.foo)时,实际上传入的就是函数foo,
可以这样理解,func(obj.foo)执行时,函数func内部是这样的

function func(obj.foo) {
  console.log(this.a);   // 执行函数foo
}

所以,结果是this指向的是全局对象window(非严格模式下)

显示绑定

显示绑定的规则,我们需要使用JavaScript中的call()方法和apply()方法
这两个方法的具体实现可如下所示:

  Function.prototype.newApply = function (obj, arr) {
    var obj = obj || window;
    obj.fn = this;
    if(!arr) {
      var result = obj.fn();
      delete obj.fn;
      return result;
    }else{
      var args = [],
        len = arr.length;
      for(var i = 0; i < len; i++){
        args.push('arr[' + i + ']');
      }
      var result = eval('obj.fn(' + args.join(',') + ')');
      delete obj.fn;
      return result;
    }
  }
  Function.prototype.newCall = function () {
    var obj = arguments[0] || window;
    obj.fn = this;
    obj.fn();
    var args = [],
      len = arguments.length;
    for(var i = 1; i < len; i++){
      args.push('arguments[' + i + ']');
    }
    var result = eval('obj.fn(' + args.join(',') + ')');
    delete obj.fn;
    return result;
  }

这两个方法的第一个参数都是一个对象,它们会将这个对象绑定到this,接着在调用函数时指定这个this

它们的第二个参数有所不同,call方法从第二个参数开始接收的是任意个数的变量,而apply方法接收的是
一个数组

如果我们在方法的第一个参数传入了一个原始值来作为this的绑定对象,这个原始值会被转换为它的对象形式

function foo() {
  console.log(this.a);
}
var obj = {
  a:2,
};
foo.call(obj); // 2

这就是通过call将foo的this强制绑定到obj上

硬绑定

通过显示绑定的一个变种———硬绑定,我们可以解决之前的丢失绑定问题

function foo() {
  console.log(this.a);
}
var obj = {
  a:2
};
var bar = function () {
  foo.call(obj);
};
bar(); // 2
bar.call(window);  // 2

分析:在函数bar内部进行函数foo的this绑定,将this强制绑定到了obj,无论之后如何调用bar,foo函数中的this都不会变

第三方库的许多函数,以及JavaScript语言和宿主环境中许多新的内置函数,都提供了一个可选的参数
通常称为上下文,其作用和bind()一样,确保你的回掉函数使用指定的this,如:

function foo(el) {
  console.log(el, this.id);
}
var obj = {
  id:'awesome'
};
[1, 2, 3].forEach(foo, obj);  // 调用foo时把this绑定到obj上

本质上也是通过call()或者apply()实现了显示绑定

new绑定

在JavaScript中new的机制和面向类的语言完全不同,JavaScript中的构造函数只是一些使用new操作时被调用的函数

当使用new来调用函数时,会执行下面的操作:
1、创建一个全新的对象
2、这个对象会被执行[[原型]]连接
3、这个新对象会绑定到函数调用的this
4、如果这个函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象

function func(a) {
  this.a = a;
}
var aaa = new foo(2);
console.log(aaa.a); // 2

使用new来调用func函数时,我们会构造一个新对象并把它绑定到foo()函数调用的this上

this判断

根据下面的判断应用的this绑定规则:

1、函数是否在new中调用(new绑定)?如果是的话this绑定的是新创建的对象。
2、函数是否通过call、apply(显示绑定)或者硬绑定调用?如果是的话,this绑定的是指定的对象。
3、函数是否在某个上下文对象中调用(隐式绑定)?如果是的话,this绑定的是那个上下文对象。
4、如果上面的都不是,使用的是默认绑定。如果是在严格模式下,就绑定到undefined,否则绑定到全局对象。

阅读更多
换一批

没有更多推荐了,返回首页