JavaScript 中最大的一个安全问题,也是最使人困惑的一个问题,就是在某些状况下this
的值是如何肯定的。有js基础的同窗面对这个问题基本能够想到:this
的指向和函数调用的方式相关。这固然是正确的,然而,这几种方式有什么联系吗?这是我接下来要说明的问题。javascript
this
从哪里来this
是js的一个关键字,和arguments
相似,它是函数运行时,在函数体内部自动生成的一个对象,只能在函数体内部使用。这句话彷佛与认知不一样,咱们在函数体外部即全局做用域下也能使用this
。html
// 直接在全局做用域下输出this console.log(this); // 输出window
可是不要忘记,即使是全局做用域,依旧是运行在window
下的,咱们写的代码都在window
的某个函数中。而这也催生了一种理解this
指向的方法:this
永远指向调用者(非箭头函数中)。java
函数做为普通函数直接调用(也称为自执行函数)的时候,不管函数在全局仍是在另外一个函数中,this
都是指向window
。数组
function fn() { this.author = 'Wango'; } fn(); console.log(author); // Wango
这很好理解,但又不是很好理解,由于在代码中省略了window
,补全后就好理解了:this
指向的是调用者。安全
function fn() { this.author = 'Wango'; } window.fn(); console.log(window.author); // Wango
而在内部函数中,自执行函数中的this
依旧指向全局做用域,咱们没法经过window.foo()
调用函数,但并不妨碍咱们先这样理解(具体参见本文最后一部分this
的强制转型)。app
function fn() { function foo() { console.log(this); } foo(); // Window window.foo(); // TypeError } fn();
在构造函数中,this
指向new
生成的新对象,即构造函数是经过new
调用的,构造函数内部的this
固然就应该指向new
出来的对象。函数
function Person(name, age) { this.name = name; this.age = age; console.log(this); // Person { name: 'Wango', age: 24 } } new Person('Wango', 24);
构造函数中的this
与构造函数的返回值类型无关,下列代码中p
指向了构造函数返回的对象,而不是new
出来的对象。固然,这是构造函数的特性,与本主题关系不大。this
function Person(name, age) { console.log(this); // Person {} this.name = name; this.age = age; console.log(this); // Person { name: 'Wango', age: 24 } return { name: 'Lily', age: 25 } } Person.prototype.sayName = function() { return this.name + ' ' + this.age } const p = new Person('Wango', 24); console.log(p.sayName()); // TypeError: p.sayName is not a function
经过对象方法调用时,this
指向应该是最明晰的了。与其余面向对象语言的this
行为相同,指向该方法的调用者。spa
function Person(name, age) { this.name = name; this.age = age; } Person.prototype.sayName = fn; function fn() { return this.name + ' ' + this.age } const p = new Person('Wango', 24); console.log(p); // Person { name: 'Wango', age: 24 } console.log(p.sayName()); // Wango 24
[]
调用对象方法一般,咱们对于对象方法是经过.
语法调用,但经过[]
也能够调用对象方法,在这种状况下的this
指向经常会被咱们混淆、忽略。prototype
function fn() { console.log(this); } const arr = [fn, 1]; arr[0](); // [Function: fn, 1] function fn2() { arguments[0](); } fn2(fn, 1); // [Arguments] { '0': [Function: fn], '1': 1 }
在上例中,不管是数组仍是伪数组,其本质上都是对象,在经过[]
获取函数元素并调用的时候,会改变函数中的this
指向,this
指向这个数组或伪数组,与对象调用函数的行为一致。
function fn() { console.log(this.name); } const author = { name: 'Wango' } fn.call(author); // Wango
这彷佛与this
永远指向调用者相违背,但一旦咱们明白了call函数的实现机制就会明白,这不只不是违背,反而是佐证。对call
、apply
、bind
实现机制不熟悉的同窗能够参考我另外一篇文章,下面截取call
简要说明。
// 保存一个全局变量做为默认值 const root = this; Function.prototype.myCall = function(context, ...args) { if (typeof context === 'object') { // 若是参数是null,使用全局变量 context = context || root; } else { // 参数不是对象的建立一个空对象 context = Object.create(null); } // 使用Symbol建立惟一值做为函数名 let fn = Symbol(); context[fn] = this; context[fn](...args); delete context[fn]; }
call
函数最核心的实如今于context[fn] = this;
和context[fn](...args);
这两行。实际上就是将没有函数调用者的普通函数挂载到指定的对象上,这时this
指向与对象调用方法的一致。而delete context[fn];
是在调用后当即解除对象与函数之间的关联。
this
强制转型使用函数的apply()
或call()
方法时,在非严格模式下null
或undefined
值会被强制转型为全局对象。在严格模式下,则始终以指定值做为函数this
的值,不管指定的是什么值。这也是为什么在严格模式下,自执行函数的this
再也不指向window
,而是指向undefined
的根本缘由。
// 定义一个全局变量 color = "red"; function displayColor() { console.log(this.color); } // 在非严格模式下使用call修改this指向,并指定null,或undefined, displayColor.call(null); displayColor.call(); // red // 修改指向无效,传入null或undefined被转换为了window
实际上,咱们也能够将自执行函数,如fn()
,看做是fn.call()
的语法糖,在普通模式下,第一个参数默认为undefined
,但被强制转换为window
。这也就解释了为什么全部自执行函数中this
都指向window
但没法经过window
调用的问题(函数在call
函数中挂载到window
对象上,执行后被当即删除,因此没法再次经过window
访问)。
apply()
或call()
方法在严格模式下传入简单数据类型做为第一个参数时,该简单数据类型会被转换为相应的包装类,而非严格模式不会如此转换。
function foo() { console.log(this); } foo.call(); // Window {} foo.call(2); // Number {2} function foo() { console.log(this); } foo.call(); // undefined foo.call(2); // 2
this
指向在箭头函数中, this
引用的是定义箭头函数的上下文。即箭头函数中的this
不会随着函数调用方式的改变而改变。
function Person(name) { this.name = name; this.getName = () => console.log(this.name); } const p = new Person('Wango'); p.getName(); // Wango const getName = p.getName; getName(); // Wango getName.call({name: 'Lily'}); // Wango
参考资料:
Javascript 的 this 用法 Javascript高级程序设计(第四版)