深刻浅出JavaScript之this

JavaScript中的this比较灵活,根据在不一样环境下,或者同一个函数在不一样方式调用下,this都有多是不一样的。可是有一个总的原则,那就是this指的是,调用函数的那个对象。html

下面是个人学习笔记,把它罗列成8种状况。node

全局的this(浏览器) 

全局做用域的this通常指向全局对象,在浏览器中这对象就是window,在node中这对象就是global。数组

console.log(this.document === document); // true (document === window.document)
console.log(this === window); // true 
this.a = 37;  //至关于建立了一个全局变量a
console.log(window.a); // 37

通常函数的this(浏览器) 

通常的函数声明或者函数表达式,直接调用函数的话,this依然指向全局对象,在浏览器中这对象就是window,在node中这对象就是global。浏览器

function f1(){  
  return this;  
} 
f1() === window; // true, global object

再举一个例子,看完就很是透彻了app

function test(){
 this.x = 1;
  alert(this.x);
}
test(); // 1

为了证实this就是全局对象,对代码作一些改变:函数

var x = 1;
function test(){
 alert(this.x);
}
test(); // 1

运行结果仍是1。再变一下:学习

var x = 1;
function test(){
 this.x = 0;
}
test();
alert(x); //0

可是在严格模式下,通常函数调用的时候this指向undefined,这也是node为何要用严格模式的一个缘由。this

function f2(){  
  "use strict"; // see strict mode  
  return this; 
} 
f2() === undefined; // true

做为对象方法的函数的this 

this做为对象方法来使用是比较常见的。es5

下面这个例子,咱们建立了一个对象字面量o,o里面有个属性f,它的值是一个函数对象,把函数做为对象属性的值这种方式咱们经常叫做对象的方法。做为对象的方法调用的时候,这时候this指向对象ospa

var o = {  
   prop: 37,  
   f: function() {    
     return this.prop;    
  } 
};  

console.log(o.f()); // logs 37

咱们不必定要定义成函数字面量这样子的对象,像下面这种状况,咱们只定义了一个对象o,若是直接调用independent()函数的话,this会指向window,可是咱们经过赋值的方式,临时建立一个属性f,并指向函数对象的时候,咱们仍然拿到了37。

var o = {prop: 37}; 

function independent() {  
   return this.prop; 
} 

o.f = independent;  
console.log(o.f()); // logs 37

 因此并非看函数是怎么建立的,而是只要将函数做为对象的方法去调用,this就会指向这个对象。

对象原型链上的this 

下面这个例子中:咱们先建立了一个对象o,里面有一个属性f,函数做为对象属性的值,咱们经过Object.create(o)建立了一个对象p,p是一个空对象,它的原型会指向o,而后使用p.a = 1; p.b = 4建立对象p上的属性,那么咱们调用原型上的方法时,this.a,this.b依然能取到对象p上的a和b。这里须要注意的是p的原型才是o,咱们调用p.f(),调用的是原型链o上的属性f,原型链上的this能够拿到当前的对象p。

var o = {f:function(){ return this.a + this.b; }};
var p = Object.create(o); 
p.a = 1; 
p.b = 4; 
console.log(p.f()); // 5 

get/set方法与this 

get/set方法中的this通常会指向get/set方法所在对象里面

function modulus(){   
   return Math.sqrt(this.re * this.re + this.im * this.im); 
} 
var o = { 
  re: 1, 
  im: -1, 
  get phase(){      
     return Math.atan2(this.im, this.re);    
  } 
}; 
Object.defineProperty(o, 'modulus', {       //临时动态给o对象建立modules属性
  get: modulus, enumerable:true, configurable:true}); 

console.log(o.phase, o.modulus); // logs -0.78 1.4142

构造函数中的this

用new把MyClass做为构造函数调用的话,this会指向空的对象,而且这个对象的原型会指向MyClass.prototype(能够看这篇文章对原型链的总结),可是调用的时候作了this.a = 37的赋值,因此最后this会做为返回值(没写return语句,或者return的是基本类型的话,会将this做为返回值),第二个例子return语句返回了对象,那么就会将a = 38做为返回值

function MyClass(){    
   this.a = 37; 
} 
var o = new MyClass();  
console.log(o.a); // 37 

function C2(){    
   this.a = 37;   
   return {a : 38};  
} 

o = new C2();  
console.log(o.a); // 38 

call/apply方法与this 

除了不一样的调用方式外,函数对象有些方法能修改函数执行的this,好比call/apply。

call和apply基本上没差异,只不过call传参的方式是扁平的,而apply是把一个数组传进去。以下面这个例子

何时用call和apply呢?好比咱们想调用Object.prototype.toString,可是咱们想指定某个this的时候,那咱们就能够就用Object.prototype.toString.call(this)这样子的方式来调用些没法直接调用的方法。以下面这个例子:

function add(c, d){  
   return this.a + this.b + c + d;  
} 
var o = {a:1, b:3}; 
add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16     //第一个参数接收的是你想做为this的对象
add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34 

function bar() { console.log(Object.prototype.toString.call(this)); } bar.call(7); // "[object Number]"

bind方法与this 

bind方法是es5开始提供的,因此ie9+才支持

function f(){  
   return this.a;  
} 

var g = f.bind({a : "test"});   //想把某个对象做为this的时候,就把它传进去,获得一个新对象g
console.log(g()); // test       //重复调用的时候,this已经指向bind参数。这对于咱们绑定一次须要重复调用依然实现绑定的话,会比apply和call更加高效(看下面这个例子)

var o = {a : 37, f : f, g : g};  
console.log(o.f(), o.g()); // 37, test   //o.f()经过对象的属性调用,this指向对象o;比较特殊的是即便咱们把新绑定的方法做为对象的属性调用,o.g()依然会按以前的绑定去走,因此答案是test不是g

总结

作项目的时候才发现这些基础概念有多么的重要,若是不把它们逐个落实了,真的是一不当心就会掉进坑里。后续我还会对原型链,做用域,继承,链式调用,正则等知识进行总结,欢迎关注 

相关文章
相关标签/搜索