【转】深入浅出JavaScript之this

JavaScript中的this比较灵敏,依据在不一致条件下,或然同二个函数在不一样方式调用下,this皆有相当大希望是分裂的。不过有一个总的原则,这就是this指的是,调用函数的要命指标。

JavaScript中的this相比灵活,依据在分歧条件下,只怕同一个函数在不一致方式调用下,this都有异常的大希望是区别的。可是有二个总的原则,这就是this指的是,调用函数的丰富指标。

无尽目录

  • 深入浅出JavaScript之闭包(Closure)
  • 深入显出JavaScript之this
  • 深入显出JavaScript之原型链和世袭

下边是本身的读书笔记,把它罗列成8种景况。

上边是作者的求学笔记,把它罗列成8种景况。

全局的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。

1
2
3
4
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

再举三个例证,看完就极其深透了

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为啥要用严酷形式的七个缘由。

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

雷同函数的this(浏览器卡塔尔国 

相像的函数注脚或然函数表明式,直接调用函数的话,this依旧指向全局对象,在浏览器中那对象就是window,在node中那对象就是global。

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

再举三个例证,看完就老大通透到底了

1
2
3
4
5
function test(){
 this.x = 1;
  alert(this.x);
}
test(); // 1

为了求证this便是大局对象,对代码做一些变动:

1
2
3
4
5
var x = 1;
function test(){
 alert(this.x);
}
test(); // 1

运作结果照旧1。再变一下:

1
2
3
4
5
6
var x = 1;
function test(){
 this.x = 0;
}
test();
alert(x); //0

只是在严苛格局下,常常函数调用的时候this指向undefined,那也是node为啥要用严俊情势的三个缘由。

1
2
3
4
5
function f2(){ 
  "use strict"; // see strict mode 
  return this;
}
f2() === undefined; // true

作为目的方法的函数的this

this作为目的方法来使用是比较分布的。

下边这些例子,大家成立了三个指标字面量o,o里面有个属性f,它的值是二个函数对象,把函数作为对象属性的值这种措施我们常常叫作对象的措施。作为靶子的艺术调用的时候,这个时候this指向对象o

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 

this作为靶子方法来利用是比较宽泛的。

下边这一个事例,大家创立了一个指标字面量o,o里面有个属性f,它的值是二个函数对象,把函数作为对象属性的值这种方法大家平常叫作对象的措施。作为靶子的艺术调用的时候,当时this指向对象o

1
2
3
4
5
6
7
8
var o = { 
   prop: 37, 
   f: function() {   
     return this.prop;   
  }
}; 
 
console.log(o.f()); // logs 37

大家不分明要定义成函数字面量那样子的对象,像下边这种情况,大家只定义了二个对象o,假诺一向调用independent(卡塔尔国函数的话,this会指向window,不过大家通过赋值的措施,一时创办一个属性f,并照准函数对象的时候,我们依旧得到了37。

1
2
3
4
5
6
7
8
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

指标原型链上的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。

1
2
3
4
5
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

get/set方法与this 

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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

布局函数中的this

用new把MyClass作为构造函数调用的话,this会指向空的目的,况兼这几个目的的原型会指向MyClass.prototype(能够看那篇小说对原型链的下结论卡塔尔国,不过调用的时候做了this.a

37的赋值,所以最后this会作为再次来到值(没写return语句,也许return的是主旨类型的话,会将this作为重临值State of Qatar,第二个例证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

用new把MyClass作为布局函数调用的话,this会指向空的靶子,并且这几个指标的原型会指向MyClass.prototype(能够看那篇作品对原型链的总计卡塔尔国,然而调用的时候做了this.a

37的赋值,所以最终this会作为重返值(没写return语句,只怕return的是主导项指标话,会将this作为重回值State of Qatar,第一个例子return语句重返了指标,那么就能够将a
= 38看成重临值

1
2
3
4
5
6
7
8
9
10
11
12
13
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]"

call/apply方法与this 

除开差异的调用情势外,函数对象有些措施能修正函数实施的this,比方call/apply。

call和apply基本上没差距,只然则call传参的主意是扁平的,而apply是把多个数组传进去。如上面那些事例

何时用call和apply呢?比如我们想调用Object.prototype.toString,但是大家想钦命某些this的时候,那我们就足以就用Object.prototype.toString.call(this卡塔尔(قطر‎那样子的办法来调用些不可能直接调用的点子。如下边这一个事例:

1
2
3
4
5
6
7
8
9
10
11
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

bind方法与this 

bind方法是es5始发提供的,所以ie9+才支撑

1
2
3
4
5
6
7
8
9
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

总结

做项目标时候才察觉那几个根基概念有多么的机要,假如不把它们每一种贯彻了,真的是一一点都不小心就能够掉进坑里。后续笔者还大概会对原型链,作用域,世襲,链式调用,正则等知识实行总计,应接关切

总结

做项目标时候才开采这一个根底概念有多么的首要,假使不把它们各种落到实处了,真的是一一点都不小心就能掉进坑里。后续作者还有大概会对原型链,功效域,世襲,链式调用,正则等学问举行总括,接待关注 

发表评论

电子邮件地址不会被公开。 必填项已用*标注