关于各种This的指向

1:全局的this指向window

  • 在全局情况下this永远指向window;
console.log(this===window)//true
  • 普通函数调用的时候this也是指向window(注意严格模式下为undefined)
var x = 10;  //window.x
function foo(){
    console.log(this);   //window  
    console.log(this.x);  //10 
}
foo();  //foo.call(window),window.foo()

2:对象方法

  • 如果函数作为对象的方法来调用的时候,this指向调用它的该对象
    注意这里声明用的不是let,所以obj没有自己的块级作用域;
    var obj = {
        x: 10,
        foo: function () {
            console.log(this);        //{x: 10, foo: ƒ}
            console.log(this.x);      //10
        }
    };
    obj.foo();  

- 如果在对象方法中定义函数,那么也就是闭包,this是会指向window

    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方法不作为对象被调用,那么就指向调用它的那个

var obj = {
    x: 10,
    foo: function () {
        console.log(this);       //Window
        console.log(this.x);     //10
    }
};
var fn = obj.foo;  //window.fn
fn();

3:构造函数

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

  • 如果函数作为构造函数使用,那么其中的 this 就代表它即将 new 出来的对象。
function Foo(){
    this.x = 10;
    console.log(this);    //Foo {x:10}
}
var foo = new Foo();
console.log(foo.x); //10
  • 但是如果直接调用 Foo 函数,而不是 new Foo(),这时候 Foo() 就变成普通函数。
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属性也就是原型对象

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调用

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对象,就是定义时所在的对象,而不是使用时所在的对象

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:vuethis

注意,不应该使用箭头函数来定义 method 函数 (例如 plus: () => this.a++)。理由是箭头函数绑定了父级作用域的上下文,所以 this 将不会按照期望指向 Vue 实例,this.a 将是 undefined
为了弄清楚这个,我们先来弄懂这些:
我们在使用vue的时候总会先new vue({}),那么就是说vue其实是一个构造函数。

    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
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()

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

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
        )
      }
    }
  }
}
我的前端博客http://www.gaoyang521.top
posted @ 2017-11-21 11:48  前端の战士  阅读(900)  评论(0编辑  收藏  举报