this

196 阅读3分钟

绝大多数情况下,函数的调用方式决定了this的值。this不能在执行期间被赋值,并且在每次函数被调用时this的值也可能会不同。

全局环境

在全局执行环境中(在任何函数体外部)this 都指向全局对象。

console.log(this === window);//true

var a = 30;
console.log(window.a);//30
console.log(this.a);//30

函数(运行内)环境

直接调用

非严格模式下,this绑定全局对象。

function fn1(){
    return this;
}
fn1();//window
console.log(fn1() === window);//true

内部函数

函数嵌套的内部函数的this指向的也是全局对象。

function fn(){
function fn1(){
console.log(this);
}
fn1();
}
fn();//window

setTimeout、setInterval

这两个方法执行的函数this也是指向全局对象。

作为构造函数调用

当一个函数用作构造函数时(使用new关键字),它的this被绑定到正在构造的新对象。

function Person(name){
    this.name = name;
}
Person.prototype.printName = function(){
    console.log(this.name);
}
var p1 = new Person('Jane')
p1.printName();//Jane

作为对象方法调用

当函数作为对象里的方法被调用时,它们的 this 是调用该函数的对象。

var obj = {
    name: 'Jane',
    fn : function(){
        console.log(this);
    }
};

obj.fn();
var fn1 = obj.fn;//fn只是赋值不是执行

fn1(); // window 可看作是window.fn1()

DOM对象绑定事件

在事件处理程序中this代表事件源DOM对象

document.addEventListener('click',function(){
    console.log(this);
});
// #document

方法

call

call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。

语法fun.call(thisArg, arg1, arg2, ...)

function add(c, d) {
  return this.a + this.b + c + d;
}

var o = {a: 1, b: 3};
add.call(o, 5, 7); // 16 o是指定对象
add.apply(o, [10, 20]); // 34

练习

使用 call 方法调用父构造函数

function Person(name,age){
    this.name = name;
    this.age = age;
}
function Student(name,age){
    Person.call(this,name,age);
}
var student = new Student('Jane',18);
console.log(student);

apply

方法调用一个具有给定this值的函数,以及作为一个数组(或类似数组对象)提供的参数。

语法func.apply(thisArg, [argsArray])

arguments作为argsArray参数,它可以被用作被调用对象的所有未指定的参数。

function num(msg){
    console.log(msg);
}
num(1);//1
num(1,2);//1

//
function num(){
    console.log.apply(console,arguments);
}
num(1,2);//1,2

练习

用apply将数组添加到另一个数组

var a = [1,2,3];
var b = [3,4,5];
a.push.apply(a,b);
console.log(a);//[1, 2, 3, 3, 4, 5]

使用apply和内置函数

找出数组中最大/小的数字
var numbers = [1,7,4,8,2,5,3];
var max = Math.max.apply(null,numbers);
var min = Math.min.apply(null,numbers);
console.log(max);//8
console.log(min);//1

缺点:若参数数组过长超出js引擎限制的长度,会抛出异常导致某些参数丢失。

解决方法:将数组切块后循环传入目标。

bind()

Function.prototype.bind这个新函数中,this将永久地被绑定到了bind的第一个参数。

语法function.bind(thisArg[,arg1[,arg2[, ...]]])

var name = 'Jackson'
var fn = {
name: 'Jane',
sayName: function(){
console.log(this);
}
}
var obj = {
name: 'lily'
}
fn.sayName();//Jane
fn.sayName.bind(window)();//Jackson
fn.sayName.bind(obj)();//lily

//
var module= {
  bind: function(){
    var _this = this;
    $btn.on('click', function(){
      console.log(_this); //this指什么
      _this.showMsg();
    })
  },

  showMsg: function(){
    console.log('hello world');
  }
}
//等同于
var module= {
  bind: function(){
    $btn.on('click', this.showMsg.bind(this))
  },

  showMsg: function(){
    console.log('hello world');
  }
}

call、play、bind()区别

  • call、apply、bind作用:改变函数执行时的上下文,即this的指向
  • call、apply参数传递方式不同
    • call把参数按顺序传递进去fun.call(thisArg, arg1, arg2, ...)
    • apply把参数放在数组里fun.apply(thisArg, [argsArray])
  • bind()返回对应函数稍后调用,apply、call是立即调用。

call、apply 、bind函数执行的本质

"use strict"
function fn(a,b){
    console.log(this)
}
fn(1, 2)
//等价于
fn.call(undefined, 1, 2)
fn.apply(undefined, [1, 2])
//在严格模式下, fn 里的 this 就是 call 的第一个参数,也就是 undefined。
//在非严格模式下(不加"use strict"), call 传递的第一个参数如果是 undefined 或者 null, 那 this 会自动替换为 Window 对象
var obj = {
    fn: function(a, b){
        console.log(this)
    },
    child: {
        fn2: function(){
            console.log(this)
        }
    }
}
obj.fn(1, 2)
//等价于
obj.fn.call(obj, 1, 2)         // 所以 this 是 obj
obj.fn.apply(obj, [1, 2])

obj.child.fn2()
//等价于
obj.child.fn2.call(obj.chid)    // 所以 this 是 obj.child

箭头函数

引入箭头函数有两个方面的作用:更简短的函数并且不绑定this。

箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承this