Javascript中this的指向
来源:互联网 发布:云计算的部署方式包括 编辑:程序博客网 时间:2024/06/05 03:57
在箭头函数出现之前,每个新定义的函数都有它自己的 this值(在构造函数的情况下是一个新对象,在严格模式的函数调用中为 undefined,如果该函数被称为“对象方法”则为基础对象等)。 ES6 引入了支持this词法解析的箭头函数(它在闭合的执行上下文内设置this的值)。
在具体说this的4种应用场景前,先从函数调用开始说起,以方便我们理解之后的代码。当我们执行一个函数,以下几种调用方式都是等价的 :
"use strict"function fn(a,b){ console.log(this)}fn(1, 2)//等价于fn.call(undefined, 1, 2)fn.apply(undefined, [1, 2])
- 在严格模式下, fn 里的 this 就是 call 的第一个参数,也就是 undefined。
- 在非严格模式下(不加”use strict”), call 传递的第一个参数如果是 undefined 或者 null, 那 this 会自动替换为 Window 对象。
var obj = { fn: function(a, b){ console.log(this) }, child: { fn2: function(){ console.log(this) } }}obj.fn(1, 2)//等价于obj.fn.call(obj, 1, 2) // 所以 this 是 objobj.fn.apply(obj, [1, 2])obj.child.fn2()//等价于obj.child.fn2.call(obj.chid) // 所以 this 是 obj.child
除去不常用的with和eval等情况,具体到实际应用中,this指向大致可以分为以下4种。
1. 作为对象的方法调用
2. 作为普通函数调用
3. 构造函数调用
4. 箭头函数
作为对象的方法调用
当函数作为对象的方法被调用时,this指向该对象。
var obj = { a:1, getA: function(){ console.log(this === obj); //true console.log(this.a); //1 } }obj.getA();
作为普通函数调用
当函数不能作为对象的属性被调用时,也就是我们常说的普通函数调用方式,此时的this总是指向全局对象(window对象), Es5的严格模式下会指向undefined。
window.name = 'globalName';var getName = function(){ return this.name;}console.log(getName()) //globalName//或者window.name = 'globalName';var myObject = { name: 'sven', getName: function(){ return this.name; }}var getName = myObject.getName;console.log(getName()); //globalName
构造函数调用
当用new运算符调用函数时,该函数总会返回一个对象,通常情况下,构造器里的this就指向返回的这个对象。代码如下:
var MyClass = function(){ this.name = 'sven';}var obj = new MyClass();console.log(obj.name);//或者console.log(new MyClass().name)
如果构造函数不显示的返回任何数据,或者是返回一个非对象类型的数据,此时this指向没有问题。代码如下:
var MyClass = function(){ this.name = 'sven'; return 'anne'; //返回string类型}var obj = new MyClass();console.log(obj.name); //输出anne
需要注意的是,用new调用构造函数时,如果构造函数显示地返回了一个object类型的对象,那么此次运算结果最终会返回这个对象,而不是之前期待的this。
var MyClass = function(){ this.name = 'sven'; return { //显示地返回一个对象 name: 'anne' }}var obj = new MyClass();console.log(obj.name); //输出anne
为了更好的理解new的过程,可以参考
- new操作符里到底发生了什么
- new创建对象的过程发生了什么
箭头函数
箭头功能不会创建自己的this;它使用封闭执行上下文的this值。
在全局代码中,它将被设置为全局对象。
var window = this;var foo = (() => this);console.log(foo() === window); //true
当在其他函数中创建的箭头函数:这些箭头函数的this被设置为外层执行上下文。
let app = { fn1: function(a){ console.log(this) //app }, fn2(a) { console.log(this) //app }, fn3: (a)=>{ console.log(this) //window }}app.fn1();app.fn2();app.fn3();
粗略一看,fn1、fn2、fn3 貌似都一样,实际上 fn1和 fn2完全等价,但 fn3是有区别的
app.fn2相当于app.fn2.call(app)
app.fn3相当于app.fn3.call( 它的上一级的 this)。
因为箭头函数不会绑定this,所以会找它的上一级,找到window。
我们可能会写出如下代码:
function Person(){ this.age = 0; setInterval(function growUp(){ this.age++; console.log(p.age); // 每隔一秒打印age },1000) } var p = new Person();
上面代码相当于
function Person(){ this.age = 0; function growUp(){ this.age++; console.log(p.age); // 每隔一秒打印age } // 过1秒后执行 growUp(); } var p = new Person();
普通函数执行,this为window,而不是Person;在ECMAScript 3/5中,通过将this值分配给封闭的变量,可以解决this问题。代码如下:
function Person(){ this.age = 0; var that = this; setInterval(function growUp(){ that.age++; console.log(p.age); //每隔一秒打印age },1000)}var p = new Person();
有了箭头函数之后,我们可以写出下面的代码:
function Person(){ this.age = 0; setInterval(() => { this.age++; console.log(p.age); //每隔一秒打印age },1000)}var p = new Person();
因为箭头函数不会绑定this,所以会找它的上一级,找到Person对象。
再来个稍微复杂点的例子:
var app = { init() { var menu = { init: ()=>{ console.log(this) }, bind() { console.log(this) } } menu.init() /*相当于 menu.init.call(menu 所在的环境下的 this) , 所以 init 里面的 this 也就是 app。 (假设 app.init 也是箭头函数,想想 menu.init 里面的 this 是什么?) */ menu.bind() /*相当于 menu.bind.call(menu),也就是 menu,所以 bind 里面的 this 就是 menu */ } }app.init()
最后一个例子:
var app = { fn1() { setTimeout(function(){ console.log(this) }, 10) }, fn2() { setTimeout(()=>{ console.log(this) },20) }, fn3() { setTimeout((function(){ console.log(this) }).bind(this), 30) }, fn4: ()=> { setTimeout(()=>{ console.log(this) },40) }}app.fn1() //windowapp.fn2() //appapp.fn3() //appapp.fn4() //window
上面的代码可以转化为:
var app = { fn1() { function fn(){ console.log(this) } //过10ms 后执行 //fn.call(undefined) ,所以输出 Window }, fn2() { //过20ms 执行箭头函数 //箭头函数里面没资格有 自己的 this,借用 setTimeout 外面的 this,也就是 app }, fn3() { // 可以转化为 var fn = function(){ console.log(this); } var fn2 = fn.bind(this); setTimeout(fn2,30); // 创建了一个新函数,这个新函数里面绑定了 外面的this,也就是 app // 20 ms 后执行新函数,输出 this,也就是刚刚绑定的 app }, fn4: ()=> { //过40ms 执行箭头函数 //箭头函数里面没资格有 this,用 setTimeout 外面的 this //setTimeout 所在的 fn4也是箭头函数,没资格拥有自己的 this,借用外面的 this ,也就是 Window }}
注: 因为箭头函数并不绑定this,因此使用箭头函数后的对象尝试使用call,apply,bind设定this是无效的。
参考阅读:
1. JavaScript设计模式与开发实践–曾探著
2. this
3. this-course
4. this 的值到底是什么?一次说清楚
5. What’s this?
- javascript 中 this 的指向
- JavaScript中 this 的指向
- JavaScript中this的指向
- javascript中this的指向
- javaScript中this的指向
- Javascript中this的指向
- JavaScript中this指向
- javascript中this的指向问题
- javascript中this指向问题的综合
- JavaScript中this的指向问题
- JavaScript中this的指向问题
- javascript中this对象的指向问题
- JavaScript中this的指向问题
- 详解JavaScript中this的指向
- javaScript中关于this的指向
- javascript中this的指向详解
- JavaScript-this的指向
- javascript的this指向
- Excel无法输入中文
- No adapter attached; skipping layout 原因、解决办法
- 金蝶软件公司风投案例
- C语言插入排序方法
- static的深入理解
- Javascript中this的指向
- Eclipse 找不到 Servlet 包
- 计算机视觉与深度学习(7)
- 【Django】MVC/MTV介绍
- 聊聊设计师的六大瓶颈期(下)
- Mockito在Maven junit测试中的应用
- Capstone日志2017-12-21
- MediaPlayer本地播放流程解析(2)
- 大数据技术概览