箭头函数和普通函数

343 阅读3分钟

箭头函数是ES6的新函数类型,与普通函数不同的是,箭头函数本身没有 this 指针的,必须通过查找作用域来决定,如果箭头函数被非箭头函数包含,则 this 绑定的是最近一层非箭头函数的 this

  1. 在非严格模式下,函数中的 this 是指向的是 window
  2. 在严格模式下,函数中的 this 指向的是 undefined,但是全局中的 this 还是指向 window
  3. 使用 let、const 来定义变量并不会绑定到 window 中

一.普通函数

对于普通函数,函数调用时的 this 默认绑定 window,函数可以访问 window 下的全局变量

var c=1
function bar() { 
  var c = 11; 
  console.log(c); 
  console.log(this.c); 
  function inner() { 
    console.log(c); 
    console.log(this.c)
  };  
  inner(); 
}

bar()   // 11, 1, 11, 1
let d = 1
function bak() { 
  let d = 11; 
  console.log(d); 
  console.log(this.d);  
  function innerh() { 
    console.log(d); 
    console.log(this.d) 
  }; 
  innerh();

}

bak()   // 11, undefined, 11, undefined
  1. let、const 来定义变量并不会绑定到 window 中
  2. 函数不管套了几层,只要不是箭头函数,默认绑定的 this 始终是 window
  1. 若有对象调用函数,函数中的 this 就会隐式的绑定到对象上。 this 始终指向函数被调用前的最近的对象,因为可能有嵌套子对象,这时哪个对象最后调用函数,那函数就指向谁。
function foo() {
    console.log(this.name);
}
var obj1 = {
    name: "obj1",
    foo: foo
};
var obj2 = {
    name: "obj2",
    obj1: obj1
};
obj2.obj1.foo();   // obj1

注: 在函数赋值给变量时,会丢失函数原来绑定对象,将函数作为参数传递时也会丢失绑定对象

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

obj.foo();   // 1    这是 foo() 函数中的 this 指针指向的是 obj 这个对象
foo2();      // 2   当把 foo() 函数赋值给另一个变量时,foo绑定的obj对象已丢失,所以默认指向了 window 
  1. 普通函数的 this 指向通过call、apply、bind 等方法来强行更改
function foo () {
    console.log(this.a)
}
var obj = { a: 1 };
var a = 11;

foo();                 // 11     this指向 window
foo.call(obj);         // 1      this强行指向 obj 对象
foo.apply(obj);        // 1
foo.bind(obj)          // 不会执行,只是创建了一个函数,没有调用
foo.bind(obj)();       // 1      这样才会执行
  1. new 绑定会构造一个新对象并把这个新对象绑定到调用函数中的this 
function Person (name) {
    this.name = name;
    this.foo1 = function () {
        console.log(this.name);
    };
    this.foo2 = function () {
        return function () {
            console.log(this.name);
        }
    }
}
var name = 'window';
var person1 = new Person('object');

person1              // Person   { name: "object", foo1: ƒ, foo2: ƒ }
person1.foo1()       // object
person1.foo2()()     // window   指针指向了 window

二. 箭头函数

与普通函数不同的是,箭头函数本身没有 this 指针的,必须通过查找作用域来决定,如果箭头函数被非箭头函数包含,则 this 绑定的是最近一层非箭头函数的 this,否则,this 为 undefined

var name = 'window';

function Person (name) {
  this.name = name
  this.foo1 = function () {
    console.log(this.name);
  }
  this.foo2 = () => {
    console.log(this.name);
  }
}

var person2 = {
  name: 'person2',
  foo2: () => {
    console.log(this.name)
  }
}

var person1 = new Person('person1');


person1.foo1();       //person1
person1.foo2();       //person1   因为new Person后,person1的this指向了Person了
person2.foo2();       //window    没有使用new操作符,因为箭头函数this向外查找,找到window

箭头函数的this是无法通过 bind、call、apply 来直接修改,但是可以通过改变作用域中 this 的指向来间接修改

var name = 'window';
var obj1 = {
  name: 'obj1',
  foo1: function () {
    console.log(this.name);
    return () => {
      console.log(this.name);
    }
  },
  foo2: () => {
    console.log(this.name);
    return function () {
      console.log(this.name);
    }
  }
}
var obj2 = {
  name: 'obj2',
}


obj1.foo1.call(obj2)(); 
//obj2 obj2                   -call改变了foo1的this指向,内部的箭头函数也会跟着改
obj1.foo1().call(obj2); 
//obj1 obj1                   -call无法改变箭头函数的this指向
obj1.foo2.call(obj2)(); 
//window window               -call函数对箭头函数无效
obj1.foo2().call(obj2); 
//window obj2                 -foo2箭头函数内部是一个普通函数,因此能够改变this指向

总结:

  1. 函数里面的this是由外层作用域来决定的,且指向函数定义时的this而非执行时
  2. 创建的对象普通函数,默认作用域是 window,如果里面有箭头函数属性的话,this 指向的是 window
  3. 构造函数创建的对象,作用域是可以理解为是这个构造函数,且这个构造函数的 this 是指向新建的对象的,因此 this 指向这个对象
  4. 箭头函数的 this 是无法通过 bind、call、apply 来直接修改,但是可以通过改变作用域中 this 的指向来间接修改