This的指向

this的取值是执行上下文环境的一部分,每次调用函数,都会产生一个新的执行上下文环境。当你在代码中使用了 this,这个 this的值就直接从执行的上下文中获取了,而不会从作用域链中搜寻。

1.全局的this指向window

  • 在全局情况下this永远指向window;

    1
    console.log(this===window)//true
  • 普通函数调用的时候this也是指向window(注意严格模式下为undefined)

    1
    2
    3
    4
    5
    6
    var x = 10; //window.x
    function foo(){
    console.log(this); //window
    console.log(this.x); //10
    }
    foo(); //foo.call(window),window.foo()

2.对象方法

  • 如果函数作为对象的方法来调用的时候,this指向调用它的该对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    注意这里声明用的不是let,所以obj没有自己的块级作用域;
    var obj = {
    x: 10,
    foo: function () {
    console.log(this); //{x: 10, foo: ƒ}
    console.log(this.x); //10
    }
    };
    obj.foo();
  • 如果在对象方法中定义函数,那么也就是闭包,this是会指向window

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    var obj = {
    x: 10,
    foo: function () {
    console.log(this) //{x: 10, foo: ƒ}
    function f(){
    console.log(this); //Window
    console.log(this.x); //10
    console.log(obj.x===window.x) //true,obj===window.obj
    }f();
    }
    }
    obj.foo();

函数虽然是在obj.foo中定义的,但它仍然只是个普通函数。作用域的特性,自己内部没有就会向父函数里找,父函数没有,就会向更上级找,直到最终找到或找不到为止。

  • 如果foo方法不作为对象被调用,那么就指向调用它的那个
    1
    2
    3
    4
    5
    6
    7
    8
    9
    var obj = {
    x: 10,
    foo: function () {
    console.log(this); //Window
    console.log(this.x); //10
    }
    };
    var fn = obj.foo; //window.fn
    fn();

3.构造函数

构造函数就是由一个函数 new 出来的对象,一般构造函数的函数名首字母大写,例如像 Object,Function,Array这些都属于构造函数。

  • 如果函数作为构造函数使用,那么其中的 this 就代表它即将 new 出来的对象。

    1
    2
    3
    4
    5
    6
    function Foo(){
    this.x = 10;
    console.log(this); //Foo {x:10}
    }
    var foo = new Foo();
    console.log(foo.x); //10
  • 但是如果直接调用 Foo 函数,而不是 new Foo(),这时候 Foo() 就变成普通函数。

    1
    2
    3
    4
    5
    6
    function Foo(){
    this.x = 10;
    console.log(this); //Window
    }
    var foo = Foo();
    console.log(foo.x);//Uncaught TypeError: Cannot read property 'x' of undefined

4.构造函数的prototype属性

构造函数的prototype属性也就是原型对象。

1
2
3
4
5
6
7
8
9
10
11
12
function Foo(){
this.x = 10;
this.xx=function(){
console.log(this) //Foo {x: 10, xx: ƒ}
}
}
Foo.prototype.getX = function () {
console.log(this); //Foo {x: 10, xx: ƒ}
console.log(this.x); //10
}
var foo = new Foo();
foo.getX();

在整个原型链中this代表的也是当前对象的值。

5.函数用call,bind,apply调用

1
2
3
4
5
6
7
8
9
10
11
12
13
var obj = {
x: 10
}
function foo(){
console.log(this); //{x: 10}
console.log(this.x); //10
}
//foo.call(obj)=obj.foo;f.foo.call(obj)=obj.foo。
//call和apply改变了函数的this上下文后便执行该函数, 而bind则是返回改变了上下文的一个函数
foo.call(obj); //call(obj,arg1,arg2)
foo.apply(obj); //apply(obj,[arg1,arg2])
foo.bind(obj)(); //还要再调用一下,多用于绑定回调函数

call,bind,apply可以改变this的指向,this的值就取传入的对象的值。第一个参数如果为undefined,null或空就相当于是window。

6.箭头函数

函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var obj = {
x: 10,
foo: function() {
var fn = () => {
return () => {
return () => {
console.log(this); //Object {x: 10, foo: ƒ}
console.log(this.x); //10
}
}
}
fn()()();
}
}
obj.foo();

7.vue的this

注意,不应该使用箭头函数来定义 method 函数 (例如 plus: () => this.a++)。理由是箭头函数绑定了父级作用域的上下文,所以 this将不会按照期望指向 Vue实例,this.a将是 undefined。

为了弄清楚这个,我们先来弄懂这些:

  • 我们在使用vue的时候总会先new vue({}),那么就是说vue其实是一个构造函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function A(){
this.a=3;
this.aa={
aaa:function(){
console.log(this,'this') //指向aaa它自己这个函数
}
}
}
function A(){
this.a=3;
this.aa={
aaa:()=>{
console.log(this,'this')//箭头函数指向A这个函数,非箭头函数指向aaa这个函数
}
}
}
var b=new A()
b.aa.aaa()//箭头函数一开始就被定义指向了A,所以即使b是A的实例,this也不会指向b,而还是指向一开始生成被定义的A
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function add(){ //正常不是箭头函数的
console.log(this===a) // true
}
var a = {};
a['add'] = function(args){
add.apply(a,[args]); //a.add();add的this就指向调用者a
};
a.add()
//有箭头函数的,用`bind,call,apply`无效
var bind=function(fn,vm){
return function(){
return fn.apply(vm,arguments)
}
}
var a={'_a':3};
var add=()=>{
console.log(this===a)//false
}
a['add']=bind(add,a)
a.add()

那么下面我们来看它源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
export function bind (fn: Function, ctx: Object): Function { //兼容重写bind方法
function boundFn (a) {
const l: number = arguments.length
return l
? l > 1
? fn.apply(ctx, arguments)
: fn.call(ctx, a)
: fn.call(ctx)
}
boundFn._length = fn.length
return boundFn
}
function initMethods (vm: Component) {
const methods = vm.$options.methods
if (methods) {
for (const key in methods) {
vm[key] = methods[key] == null ? noop : bind(methods[key], vm) //如果是箭头函数此处用不了bind,改不了this指向,(fn,vm)/vm.fn,this就指向vm
if (process.env.NODE_ENV !== 'production' && methods[key] == null) {
warn(
`method "${key}" has an undefined value in the component definition. ` +
`Did you reference the function correctly?`,
vm
)
}
}
}
}