JavaScript中的this指向问题

394 阅读3分钟

函数的this指向

js中,函数的this指向是在调用函数的时候决定的

函数的调用大致分为四种情况:

1. 作为函数调用

// 例子1
var a = 100;
function foo() {
    var a = 10;
    console.log(this.a);
}

foo(); // 在非严格模式下输出:100
foo(); // 在严格模式下输出:Uncaught TypeError: Cannot read property 'a' of undefined
  • 非严格模式:浏览器环境的全局上下文对象是window,所以这里的this指向的是window对象,this.a是挂载在window上的a属性
  • 严格模式:浏览器环境的全局上下文是undefined,所以执行foo()会报错

ES6语法中用let声明的全局变量不会挂载到window

2.作为对象方法调用

// 例子2
var name = 'xxx';
var obj = {
    name: 'objName',
    sayName: function() {
        console.log(this.name);
    }
}

obj.sayName(); // 'objName',此时调用sayName的是obj,所以this指向的也是obj

var say = obj.sayName;
say(); // 'xxx',将obj.sayName赋值给say,执行say(),此时调用say的是全局对象window,因此结果为 'xxx'

接着看下面这个例子

// 例子3
var name = 'xxx';
function foo() {
    var name = 'foo';
    innerFoo();
    function innerFoo() {
        console.log(this.name);
    }
}

foo(); // 'xxx'

上面代码中,window对象调用了foo函数,在foo函数中又执行了innerFoo函数,本质上仍然是在全局上下文中调用了innerFoo函数,所以this指向仍然是window

3.作为构造函数调用

如果当前this对象本身没有对应的属性,则会从它的原型链上查找

function Foo() {
    this.sayName = function () {
        console.log(this.name);
    }
}

Foo.prototype.name = 1000;

var foo = new Foo();
foo.sayName(); // 1000,由于foo的原型链指向Foo的原型,所以打印 1000

使用new操作符的时候,js内部做了以下几件事情:

1.创建一个全新的对象,称为实例

2.将实例的原型链__proto__链接到构造函数prototype

3.将实例内部的this指向到实例本身

4.如果没有定义构造函数的返回一个对象,则返回创建好的实例

4.箭头函数调用

// 例子4
var name = 'xxx';
let obj = {
    name: 'obj',
    sayName: () => {
        console.log(this.name);
    }
}

obj.sayName(); // 'xxx'

箭头函数与ES5的函数有以下几点区别:

1.箭头函数没有自己的thissuperargumentsnew.target绑定

2.不能使用new来生成实例,即不是构造函数没有constructor

3.没有原型对象

4.无法改变内部的this指向

5.行参名称不能重复

6.箭头函数内部的this指向缓存的是上层的普通函数的this指向,如果没有上层函数,则this指向的是window对象

修改函数内部的this指向

可以通过call,apply,bind来指定普通函数内部的this指向

用法

var name = 'xxx';
var obj = {
    name: 'obj-name',
    sayName: function() {
        console.log(this.name);
    }
}

var say = obj.sayName;
say(); // 'xxx'
say.call(obj); // 'obj-name'
say.apply(obj); // 'obj-name'
say.bind(obj)(); // 'obj-name'

call, apply, bind的差异

相同点:call,apply,bind的第一个参数都是需要绑定的this指向

不同点:

1.bind改变this指向后不会指向函数,apply,call改变this指向后会立即执行函数
2.apply的其余参数是以数组传入,call,bind的其余参数是一个一个传入的