《JavaScript----this》

116 阅读4分钟

JavaScript函数的this指向

前言

在常见的编程语言中,几乎都有this这个关键字(Objective-C中使用的是self),但是JavaScript中的this和常见面向对象语言中的this不太一样

  • 常见面向对象的编程语言中,比如Java、C++、Swift、Dart等等一系列语言中,this通常只会出现在类的方法中。
  • 也就是你需要有一个类,类中的方法(特别是实例方法)中,this代表的是当前调用对象。
  • 但是JavaScript中的this更加灵活,无论是它出现的位置还是它代表的含义 我们来看一下编写一个obj对象,有this和没有this的区别
//不使用this
var obj = {
    name:'aky',
    running:function(){
        console.log(obj.name +  "running")
    },
    studying:function(){
        console.log(obj.name +  "studying")
    }
}

//使用this
var obj = {
    name:'aky',
    running:function(){
        console.log(this.name +  "running")
    },
    studying:function(){
        console.log(this.name +  "studying")
    }
}

this的指向什么呢?

  • 我们先说一个最简单的,this在全局作用域下指向什么?

    • 这个问题非常容易回答,在浏览器中测试就是指向window
  • 但是,在开发中很少直接在全局作用域下使用this,通常都是在函数中使用 * 所有的函数在被调用时,都会创建一个执行上下文 * 这个而上下文记录这函数的调用栈、AO对象、作用域链等 * this也是其中的一条

  • 我们先看一个令人困惑的问题:

    • 定义一个函数,我们采用三种不同的方式进行调用,他产生了三种不同的结果
      //定义一个函数
      function foo(){
          console.log(this)
       }
       //1、调用方式一:直接调用
       foo()    // window
       // 2、调用方式二:将foo放到一个对象obj中,在调用
       var obj = {
           name:'aky',
           foo:foo
        }   
        obj.foo()   // obj对象
        // 3、 调用方式三:通过call/apply来调用
        foo.call("abc") //  String abc 对象
      
  • 从上面案例中我们可以看出

    • 在函数调用的时候,JavaScript会默认给this绑定一个值
    • this的绑定和定义的位置没有关系
    • this的绑定和调用方式以及调用的位置有关系
    • this在运行的时候被绑定的 下面我们就来看看this的绑定规则

绑定规则一:默认绑定

独立函数调用会默认绑定this 注:独立的函数调用我们可以理解函数没有绑定到某个对象上进行调用

//案例一:
function foo(){
    console.log(this)
}
 foo()    //widow
 
//案例二:
function foo1(){
    console.log(this)
}
function foo2() {
    console.log(this)
    foo1()
}

function foo3() {
    console.log(this)
    foo2()
}
foo3()  // window  window window

//案例三:
var obj = {
   name: "why",
   foo: function() {
        console.log(this)
   }
}

var bar = obj.foo
bar() // window

绑定规则二:隐式绑定

隐式绑定:也是就它的调用位置中,是通过某个对象发起的函数调用

//案例一
var obj = {
  name: "why",
  foo: foo
}

obj.foo() // obj对象

//案例二
var obj1 = {
  name: "obj1",
  foo: function() {
    console.log(this)
  }
}

var obj2 = {
  name: "obj2",
  obj1 :obj1
}

obj2.obj1.foo()  //obj1对象

隐式绑定有一个前提:

  • 必须在调用的对象内部有一个对函数的引用(比如一个属性)
  • 如果没有这样的引用,在进行调用时,会报找不到该函数的错误
  • 正式通过这个引用,间接的将this绑定到了这个对象上

绑定规则三:显示绑定

我们不希望在对象内部包含这个函数的引用,同时又希望在这个对象上进行强制调用,怎么做呢?

  • JavaScript为所有函数提供了callapply方法
  • 这两个函数的第一个参数就时给this准备的
  • 调用函数的时候,会将this绑定到传入的对象身上

call、apply、bind

通过call或者apply绑定this对象 显示绑定后,this就会明确的指向绑定的对象

function foo() {
 console.log("函数被调用了", this)
}

var obj = {
 name: "obj"
}

foo.call(obj)   //obj对象
foo.apply(obj)  // obj对象
foo.apply("aaaa")  //String对象,存放aaaa

var newFoo = foo.bind(obj)
newFoo()   // obj

绑定规则四:new绑定

JavaScript中的函数可以当作一个类的构造函数来使用,也就是说使用new关键字。 使用new关键字来调用函数时,会执行如下操作:

  • 创建一个空对象
  • 这个空对象的[[proto]]与构造函数的protptype链接
  • 这个新对象会绑定到函数调用的this上(this的绑定在这步骤完成)
  • 如果函数没有返回值,返回新的对象
function Person(name){
    
    this.name =name
 }
 Person.prototype.foo = function(){
        console.log(this)
 }
 var p = new Person('aky')
 p.foo()   // p对象

规则绑定优先级

  1. 默认规则的优先级最低
  2. 显示绑定优先级高于隐式绑定
  3. new绑定的优先级高于隐式绑定
  4. new绑定优先级高于bind
    • new绑定和call、apply是不允许同时使用,所以不存在优先级
    • new绑定可以和bind一起使用,new优先级更高

this规则之外-ES6箭头函数

ES6中新增一个非常好用的函数类型:箭头函数,箭头函数不是同this的四种绑定规则(也是就不绑定this),而是根据外层作用域来决定this

image.png

image.png