JS 中的 this 指向问题

JavaScript 中的 this:
1: this指向是在运行函数时确定的,而不是定义函数时候确定的

2: JS 是基于词法作用域的语言,函数在定义它的作用域中执行,而不是在调用它的作用域中执行

3: 匿名函数的执行环境具有全局性,因此其this 对象通常指向window(在通过call()或apply()改变函数执行环境的情况下,this 就会指向其他对象)

4: 类的方法默认是不会绑定 this 的,作为对象的方法:当作为对象的方法被调用时,它们的this是调用该函数的对象,并且this的绑定只受最靠近的成员引用的影响



全局上下文:无论是否在严格模式下,在全局执行上下文中(在任何函数体外)this都指代全局对象

    如:
        1、console.log(this === window); // true
        2、var girl={name:"amy",testThis:this}; console.log(girl.testThis); // window
          (可理解为:var girl=new Object(); girl.name="amy"; girl.testThis=this;(指向window)


函数上下文:在函数内部,this的取值取决于函数被调用的方式

    1、简单调用:
    function f1(){
      return this;
    }
    f1() === window; //在浏览器中,全局对象是window
    非严格模式下,且this的值不是由该调用设置时(具体说就是不由该调用通过 call、apply、bind 这些方法来设置),this的值默认指向全局对象 严格模式下,this将保持它进入执行上下文时的值 所以默认为undefined
    
    2、如果要想把 this 的值从一个上下文传到另一个,就要用 call 或者apply 方法
    
    3、bind方法:
    ECMAScript 5 引入了 Function.prototype.bind。调用f.bind(someObject)会创建一个与f具有相同函数体和作用域的函数,但是在这个新函数中,this将永久地被绑定到了bind的第一个参数,无论这个函数是如何被调用的
    
    4、箭头函数:this与封闭词法上下文的this保持一致(即this被设置为它创建时的上下文)。如果将this的值传给call、bind、apply,它将被忽略,不过仍然可以为为调用添加参数,不过第一个参数应设置为null
    
    5、作为对象的方法:当作为对象的方法被调用时,它们的this是调用该函数的对象,并且此时this的绑定只受最靠近的成员引用的影响
    
    6、原型链中的this:this指向的是调用该方法的对象,就像该方法在对象上一样

    7、getter(与setter)中的this:用作getter(或setter)的函数会把this绑定到获取(或设置)属性的对象上

    8、作为构造函数:this被绑定到正在构造的新对象(虽然构造器返回的默认值是this所指的那个对象,但它仍可以手动返回其他的对象)
    
    9、作为一个DOM事件处理函数:this指向触发事件的元素
    
    10、作为一个内联事件处理函数:this指向监听器所在的DOM元素
        如:
            1、<button onclick="alert(this.tagName.toLowerCase());"> Show this </button> this指向button(注意只有外层代码中的this是这样设置的)
            
            2、<button onclick="alert((function(){return this})());">  Show inner this </button> 这种情况下,没有设置内部函数的this,相当于函数简单调用的情况,所以它指向global/window对象

 

部分代码示例:

例一:
  var age = 18;
  function outside(){
    var age = 20;
    return function(){console.log(age)}
  }
  var fn = outside();
  fn();// 输出20
 (function(){console.log(age)})() //输出 18
  //解析: JS 是基于词法作用域的语言,函数在定义它的作用域中执行,而不是在调用它的作用域中执行

 

例二:
var length = 10;
function fn () {
    console.log(this.length);
}
var obj = {
    length: 5,
    method: function (fn) {
        fn();
        arguments[0]();
    }
};
obj.method(fn, 1); //10 和 2
//浏览器输出的第二个结果是 2
/** 
* 解析: this指向是在运行函数时确定的,而不是定义函数时候确定的
* arguments 是函数中的一个类数组对象,  由 obj.method(...args) 得到此时运行中的
* method 中的this 指向 obj, method 中的 arguments 类数组对象大概如下:
* (当然真实情况中的 arguments 类数组对象不是这样, 但类似这样的结构)
{
  0: function fn () {
         console.log(this.length);
      }, //这个是上面传入的第一个参数, 这个参数是一个函数
  1: 1, //这个是上面传入的第二个参数, 这个参数是一个数字1
  2: length, // 这个是js 函数中arguments 的属性,它的值是函数在调用时实际传入的参数个数
  3: 其他属性,
  4: 其他方法,
  ......
}
* 此时可以看到obj.method(fn,1); 实际传入的参数是两个,因此 arguments.length 值为2
* 所以arguments[0] 就是一个函数
  function fn () {
    console.log(this.length);
  } 
* 而arguments[0]() 就是在执行这个函数, 由于类数组对象调用自身的方法和对象调用自身的方法类似,
* 因此此时这个执行中的函数的this 指向类数组对象自己,
* 也就是this.length 实际上就是arguments.length,这个属性的值是函数在调用时实际传入的参数个数,也就是2 咯

  

  1 <!DOCTYPE html>
  2 <html lang="en">
  3 <head>
  4     <meta charset="UTF-8">
  5     <title>This</title>
  6 </head>
  7 <body>
  8 
  9 <script>
 10     //全局上下文:无论是否在严格模式下,在全局执行上下文中(在任何函数体外)this都指代全局对象 
 11     var girl={
 12         name:"amy",
 13         testThis:this
 14     };
 15     /*
 16         可理解为:
 17         var girl=new Ojbect();
 18         girl.name="amy";
 19         girl.testThis=this;
 20     */
 21     console.log(girl.testThis);//window
 22 </script>
 23 
 24 <script>
 25  26     console.log("类的方法默认是不会绑定 this 的,作为对象的方法:当作为对象的方法被调用时,它们的this是调用该函数的对象,并且this的绑定只受最靠近的成员引用的影响");
 27     console.log("-----------About Amy --------------");
 28     var Amy={
 29             name:"Amy",
 30             sex:"girl",
 31             amyOutter:function(){
 32                 console.log("amyOutter:",this);
 33                 return function(){
 34                     console.log("amyAnonymous:",this);
 35                 };
 36             }
 37         };
 38     console.log("----Amy.func amyOutter -----");
 39     var amy_anony=Amy.amyOutter();
 40     //输出: amyOutter: {name: "Amy", sex: "girl", amyOutter: ƒ}
 41     // 解析:作为对象的方法调用
 42 
 43     console.log("----Amy.func amyOutter quote-----");
 44     var quote=Amy.amyOutter;//获取Amy.amyOutter函数的引用
 45     quote();
 46     //输出:amyOutter: Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
 47     // 解析:这里相当于简单调用
 48     
 49     console.log("----Amy.func amyAnonymous-----");
 50     amy_anony();
 51     //输出: amyAnonymous: Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
 52     // 解析:直接调用返回的匿名函数
 53 
 54     amy_anony.call(Amy);
 55     //输出: amyAnonymous: {name: "Amy", sex: "girl", amyOutter: ƒ}
 56     // 解析:Amy用call调用返回的匿名函数
 57     
 58 </script>
 59 
 60 <script>
 61     console.log("-----------About Mike -----------");
 62     var Mike={
 63         name:"Mike",
 64         sex:"boy",
 65         mikeOutter:function(){
 66             console.log("mikeOutter:",this);
 67             (function(){
 68                 console.log("mikeAnonymous:",this);
 69             })();
 70         }
 71     };
 72 
 73     console.log("----Mike.func mikeOutter---");
 74     Mike.mikeOutter();
 75     //输出: mikeOutter: {name: "Mike", sex: "boy", mikeOutter: ƒ}
 76     // 解析:作为对象的方法调用  
 77     //输出: mikeAnonymous: Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
 78     // 输出:window 解析:匿名函数被调用
 79 
 80     console.log("----Mike.func mikeOutter quote-----");
 81     var quote=Mike.mikeOutter;//获取Mike.mikeOutter函数的引用
 82     quote();
 83     //输出:mikeOutter: Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
 84     // 解析:这里相当于简单调用
 85     //输出: mikeAnonymous: Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
 86     // 解析:匿名函数被调用
 87 
 88     console.log("---Mike func call---");
 89     Mike.mikeOutter.call(Amy);
 90     //输出: mikeOutter: {name: "Amy", sex: "girl", amyOutter: ƒ}
 91     // 解析:Amy 用call调用返回的匿名函数 
 92     //输出:mikeAnonymous: Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
 93     // 解析:匿名函数被调用
 94 
 95 </script>
 96 
 97 <script>
 98     console.log("-----------About Guang -----------");
 99     var Guang={
100         name:"Guang",
101         sex:"boy",
102         GuangOutterOne:function(){
103             console.log("GuangOutterOne:",this);
104             var that=this;
105             // 正确 that 中保留了对 this 的引用
106             this.GuangOutterTwo(function(){
107                 that.GuangOutterThree();                
108             });
109             
110             // 正确 匿名函数直接绑定 this
111             // this.GuangOutterTwo(function(){
112             //     this.GuangOutterThree();                
113             // }.bind(this));
114 
115             // 出错 匿名函数作参数 this 为 window ,window 无 GuangOutterThree 方法
116             // this.GuangOutterTwo(function(){
117             //     this.GuangOutterThree();                
118             // });
119         },
120         GuangOutterTwo:function(cb){
121             console.log("GuangOutterTwo:",this);
122             cb();
123         },
124         GuangOutterThree:function(){
125             console.log("GuangOutterThree:",this);
126         }
127     };
128 
129     console.log("----Guang.func GuangOutterOne---");
130     Guang.GuangOutterOne();
131     //输出:GuangOutterOne: {name: "Guang", sex: "boy", GuangOutterOne: ƒ, GuangOutterTwo: ƒ, GuangOutterThree: ƒ} 
132     //解析:作为对象的方法调用  
133 
134     //输出: GuangOutterTwo: {name: "Guang", sex: "boy", GuangOutterOne: ƒ, GuangOutterTwo: ƒ, GuangOutterThree: ƒ}
135     //解析: GuangOutterOne 中的 this 指向 Guang 调用 Guang 中的 GuangOutterTwo 方法
136 
137     //输出: GuangOutterThree: {name: "Guang", sex: "boy", GuangOutterOne: ƒ, GuangOutterTwo: ƒ, GuangOutterThree: ƒ}
138     //解析: that 保留对 this 的引用 调用 Guang 中的 GuangOutterTwo 方法
139    
140 
141 </script>
142 
143 <script>
144     console.log("JS 是基于词法作用域的语言,函数在定义它的作用域中执行,而不是在调用它的作用域中执行");
145     console.log("箭头函数:this与封闭词法上下文的this保持一致(即this 被设置为他被创建时的上下文)");
146     var foo=(()=>this);
147     /*
148         可以把箭头函数看成是
149         var foo=function(){
150             //other code
151             return this;
152             //此处this所在的函数被创建时是在全局环境中 因为 this被设置为他被创建时的上下文 所以 this就被设置为window
153         };
154     */
155     // 由下可见 无论如何,foo 中的 this 都被设置为他被创建时的上下文(在上面的例子中,就是全局对象)
156     // 注意:对于箭头函数 如果将this传递给call、bind、或者apply,它将被忽略 不过你仍然可以为调用添加参数,不过第一个参数(thisArg)应该设置为null
157     var obj = {foo: foo};
158     console.log("直接调用:",foo()===window,", call调用:",obj.foo() === window,", apply调用:",foo.call(obj) === window,", bind绑定:",foo.bind(obj)() === window); // true true true true
159     
160    //这同样适用于在其他函数内创建的箭头函数:这些箭头函数的this被设置为封闭的词法上下文的
161     console.log("-----------About John ------------------");
162     var John={
163         name:"John",
164         johnOutter:function(){
165             //console.log("johnOutter",this);//johnOutter中的this具体情况可参考上面的Amy和Mike实例
166             var x=(()=>this);
167             //此处this所在的函数被创建时是在johnOutter中  因为 this被设置为他被创建时的上下文 所以 this就被设置为 johnOutter 函数中的this
168             return x;
169         }
170     }
171 
172     var fn=John.johnOutter();//获取返回的函数 若去掉johnOutter中console.log语句的注释 输出:John 解析:解析:作为对象的方法调用 
173     console.log("John.johnOutter():",fn());
174     //输出:John.johnOutter(): Object { name: "John", johnOutter: johnOutter() }
175     //解析:此时johnOutter函数由John调用 所以johnOutter函数中的this为John,而箭头函数的this被设置为johnOutter的this
176 
177     var quote=John.johnOutter;//获取John.johnOutter函数的引用
178     console.log("quote:",quote()());
179     //输出:window 
180     //解析:此时quote获取johnOutter的引用,quote()相当于简单调用,所以johnOutter函数中的this为window,而箭头函数的this被设置为johnOutter的this
181     
182     /**
183      * 对于箭头函数:
184      * 1、确定箭头函数是在哪个执行环境中被创建的
185      * 2、根据执行环境确定this被设置为哪个执行环境
186      * 3、最后对函数的调用参考对象中的方法调用即可
187      * 总结可以简单理解为:
188      * 箭头函数的this在全局执行上下文创建时 它的this就是全局上下文中的this 
189      * 箭头函数的this在非全局执行上下文创建时,它的this就与封闭词法上下文中的this保持一致
190 */ 191 192 </script> 193 194 </body> 195 </html>

 

posted @ 2018-09-19 22:37  ノ→_→(←_←  阅读(258)  评论(0编辑  收藏  举报