176 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			176 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| ---
 | ||
| title: this reference
 | ||
| localeTitle: 这个参考
 | ||
| ---
 | ||
| ## `this`参考
 | ||
| 
 | ||
| 在JavaScript中,每个函数都有`this`引用在声明它自动创建。该参考颇为相似, `this`在其他基于类的语言如Java或C#参考(JavaScript是一种基于原型的语言,并没有“阶级”的概念): _它指向哪个对象有时调用的函数_ (该对象称为_上下文_ )。但是,在JavaScript中, _函数内部的`this`引用可以绑定到不同的对象,具体取决于调用函数的位置_ 。以下是JavaScript中`this`绑定的5个基本规则:
 | ||
| 
 | ||
| ### 规则1
 | ||
| 
 | ||
| 在全局范围中调用函数时, `this`引用默认绑定到**全局对象** (浏览器中的`window`或Node.js中的`global` )。例如:
 | ||
| 
 | ||
| ```javascript
 | ||
| function foo() { 
 | ||
|   this.a = 2; 
 | ||
|  } 
 | ||
|  
 | ||
|  foo(); 
 | ||
|  console.log(a); // 2 
 | ||
| ```
 | ||
| 
 | ||
| 注意:如果在严格模式下声明上面的`foo()`函数,那么在全局范围内调用此函数, `this`将是`undefined`并且赋值`this.a = 2`将抛出`Uncaught TypeError`异常。
 | ||
| 
 | ||
| ### 规则2
 | ||
| 
 | ||
| 我们来看下面的例子:
 | ||
| 
 | ||
| ```javascript
 | ||
| function foo() { 
 | ||
|   this.a = 2; 
 | ||
|  } 
 | ||
|  
 | ||
|  var obj = { 
 | ||
|   foo: foo 
 | ||
|  }; 
 | ||
|  
 | ||
|  obj.foo(); 
 | ||
|  console.log(obj.a); // 2 
 | ||
| ```
 | ||
| 
 | ||
| 显然,在上面的代码片段中, `foo()`函数被调用, _上下文_是`obj`对象,现在`this`引用绑定到`obj` 。因此,当使用上下文对象调用函数时, `this`引用将绑定到此对象。
 | ||
| 
 | ||
| ### 规则3
 | ||
| 
 | ||
| `.call` , `.apply`和`.bind`都可以在调用点使用显式绑定`this` 。使用`.bind(this)`是你可以在很多React组件中看到的东西。
 | ||
| 
 | ||
| ```javascript
 | ||
| var foo = function() { 
 | ||
|   console.log(this.bar) 
 | ||
|  } 
 | ||
|  
 | ||
|  foo.call({ bar: 1 }) // 1 
 | ||
| ```
 | ||
| 
 | ||
| 这是一个快速的例子,说明如何使用每个绑定`this` :
 | ||
| 
 | ||
| *   `fn.call(thisObj, fnParam1, fnParam2)` `.call()` : `fn.call(thisObj, fnParam1, fnParam2)`
 | ||
| *   `.apply()` : `fn.apply(thisObj, [fnParam1, fnParam2])`
 | ||
| *   `.bind()` : `const newFn = fn.bind(thisObj, fnParam1, fnParam2)`
 | ||
| 
 | ||
| ### 规则4
 | ||
| 
 | ||
| ```javascript
 | ||
| function Point2D(x, y) { 
 | ||
|   this.x = x; 
 | ||
|   this.y = y; 
 | ||
|  } 
 | ||
|  
 | ||
|  var p1 = new Point2D(1, 2); 
 | ||
|  console.log(p1.x); // 1 
 | ||
|  console.log(p1.y); // 2 
 | ||
| ```
 | ||
| 
 | ||
| 您必须注意的是使用`new`关键字调用的`Point2D`函数,并且`this`引用绑定到`p1`对象。因此,当使用`new`关键字调用函数时,它将创建一个新对象,并且`this`引用将绑定到此对象。
 | ||
| 
 | ||
| 注意:当您使用`new`关键字调用函数时,我们也将其称为_构造函数_ 。
 | ||
| 
 | ||
| ### 规则5
 | ||
| 
 | ||
| 的JavaScript确定的值`this`在运行时,基于所述当前上下文。所以`this`有时会指向你期望的东西。
 | ||
| 
 | ||
| 考虑这个带有一个名为`makeSound()`的方法的Cat类示例,遵循规则4(上面)中的模式,带有构造函数和`new`关键字。
 | ||
| 
 | ||
| ```javascript
 | ||
| var Cat = function(name, sound) { 
 | ||
|     this.name = name; 
 | ||
|     this.sound = sound; 
 | ||
|     this.makeSound = function() { 
 | ||
|         console.log( this.name + ' says: ' + this.sound ); 
 | ||
|     }; 
 | ||
|  } 
 | ||
|  var kitty = new Cat('Fat Daddy', 'Mrrooowww'); 
 | ||
|  kitty.makeSound(); // Fat Daddy says: Mrrooowww 
 | ||
| ```
 | ||
| 
 | ||
| 现在让我们试着给猫一个方法来`annoy()`人们通过重复他的声音100次,每半秒一次。
 | ||
| 
 | ||
| ```javascript
 | ||
| var Cat = function(name, sound) { 
 | ||
|     this.name = name; 
 | ||
|     this.sound = sound; 
 | ||
|     this.makeSound = function() { 
 | ||
|         console.log( this.name + ' says: ' + this.sound ); 
 | ||
|     }; 
 | ||
|     this.annoy = function() { 
 | ||
|         var count = 0, max = 100; 
 | ||
|         var t = setInterval(function() { 
 | ||
|             this.makeSound(); // <-- this line fails with `this.makeSound is not a function` 
 | ||
|             count++; 
 | ||
|             if (count === max) { 
 | ||
|                 clearTimeout(t); 
 | ||
|             } 
 | ||
|         }, 500); 
 | ||
|     }; 
 | ||
|  } 
 | ||
|  var kitty = new Cat('Fat Daddy', 'Mrrooowww'); 
 | ||
|  kitty.annoy(); 
 | ||
| ```
 | ||
| 
 | ||
| 这不起作用,因为在`setInterval`回调中我们创建了一个具有全局范围的新上下文,所以`this`不再指向我们的kitty实例。在Web浏览器中, `this`将指向Window对象,该对象没有`makeSound()`方法。
 | ||
| 
 | ||
| 有几种方法可以使它工作:
 | ||
| 
 | ||
| 1)创建新的上下文之前,分配`this`名为局部变量`me` ,或`self` ,或任何你想将它命名,并使用回调内部的变量。
 | ||
| 
 | ||
| ```javascript
 | ||
| var Cat = function(name, sound) { 
 | ||
|     this.name = name; 
 | ||
|     this.sound = sound; 
 | ||
|     this.makeSound = function() { 
 | ||
|         console.log( this.name + ' says: ' + this.sound ); 
 | ||
|     }; 
 | ||
|     this.annoy = function() { 
 | ||
|         var count = 0, max = 100; 
 | ||
|         var self = this; 
 | ||
|         var t = setInterval(function() { 
 | ||
|             self.makeSound(); 
 | ||
|             count++; 
 | ||
|             if (count === max) { 
 | ||
|                 clearTimeout(t); 
 | ||
|             } 
 | ||
|         }, 500); 
 | ||
|     }; 
 | ||
|  } 
 | ||
|  var kitty = new Cat('Fat Daddy', 'Mrrooowww'); 
 | ||
|  kitty.annoy(); 
 | ||
| ```
 | ||
| 
 | ||
| 2)同ES6就可以避免分配`this`通过使用箭头功能,其结合本地变量`this`到它的限定的周围的代码的情况下。
 | ||
| 
 | ||
| ```javascript
 | ||
| var Cat = function(name, sound) { 
 | ||
|     this.name = name; 
 | ||
|     this.sound = sound; 
 | ||
|     this.makeSound = function() { 
 | ||
|         console.log( this.name + ' says: ' + this.sound ); 
 | ||
|     }; 
 | ||
|     this.annoy = function() { 
 | ||
|         var count = 0, max = 100; 
 | ||
|         var t = setInterval(() => { 
 | ||
|             this.makeSound(); 
 | ||
|             count++; 
 | ||
|             if (count === max) { 
 | ||
|                 clearTimeout(t); 
 | ||
|             } 
 | ||
|         }, 500); 
 | ||
|     }; 
 | ||
|  } 
 | ||
|  var kitty = new Cat('Fat Daddy', 'Mrrooowww'); 
 | ||
|  kitty.annoy(); 
 | ||
| ```
 | ||
| 
 | ||
| ### 其他资源
 | ||
| 
 | ||
| *   [javascriptissexy.com](http://javascriptissexy.com/understand-javascripts-this-with-clarity-and-master-it/)
 | ||
| *   [你不懂JS](https://github.com/getify/You-Dont-Know-JS/blob/master/this%20%26%20object%20prototypes/ch2.md) |