關於各類This的指向

this 的取值是執行上下文環境的一部分,每次調用函數,都會產生一個新的執行上下文環境。當你在代碼中使用了 this,這個 this 的值就直接從執行的上下文中獲取了,而不會從做用域鏈中搜尋vue

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方法不做爲對象被調用,那麼就指向調用它的那個app

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屬性也就是原型對象oop

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 表明的也是當前對象的值,指向構造函數。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或空就至關因而windowprototype

6:箭頭函數

函數體內的this對象,就是定義時所在的對象,而不是使用時所在的對象code

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實際上是一個構造函數。component

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
        )
      }
    }
  }
}
相關文章
相關標籤/搜索