函数在执行时,会在函数体内部自动生成一个this指针。谁直接调用产生这个this指针的函数,this就指向谁。es6
怎么理解指向呢,我认为指向就是等于。例如直接在js中输入下面的等式:数组
console.log(this===window);//true
状况不一样,this指向的对象也不一样。例如:浏览器
1. 函数声明的状况app
var bj=10; function add(){ var bj=20; console.log(this);//window console.log(this.bj);//10 console.log(bj);//20 console.log(this.bj+bj);//30 } add(); window.add();
(1) 执行了add()以后,此时的this指向的是window对象,为何呢?由于这时候add是全局函数,是经过window直接调用的。因此下面我专门写了个window.add()就是为了说明,全局函数的this都是指向的window。函数
(2) 就像alert()自带的警告弹窗同样,window.alert()执行以后也是同样的效果。因此只要是 window点 这种调用方式均可以省略掉,所以警告弹窗能够直接使用alert()。this
2. 函数表达式spa
var bj=10; var zjj=function(){ var bj=30; console.log(this);//window console.log(this.bj);//10 console.log(bj);//30 console.log(this.bj+bj);//40 } console.log(typeof zjj);//function zjj(); window.zjj();
(1) 执行了zjj()以后,函数中的this也是指向window对象。缘由和第一个是同样的,都是经过window这个对象直接调用。prototype
3. 函数做为对象的属性去调用------例一线程
var bj=10; var obj={ name:"八戒", age:"500", say:function(){ var bj=40; console.log(this);//就是obj这个对象 console.log(this.bj);//undefined console.log(this.name);//八戒 } } obj.say(); window.obj.say();
(1) 当obj.say()被执行的时候,此时的this指向的是 obj 这个对象,为何呢?由于say函数是经过obj这个对象直接调用的。指针
(2) 那有人可能会问了,obj对象实际上也是经过window对象调用的,为何this不指向window呢?我认为是由于say这个函数是经过 obj 对象直接调用的,而没有经过 window 对象直接调用,所以this不会指向window。看下面的例子就明白了。
3.1 函数做为对象的属性去调用------例二
var bj=10; var obj={ name:"八戒", age:500, say:function(){ console.log(this);//是obj这个对象 console.log(this.bj);//undefined console.log(this.name)//八戒 }, action:{ name:"悟空", age:1000, say:function(){ console.log(this);//是action这个对象 console.log(this.bj);//undefined console.log(this.name)//悟空 } } } obj.say(); obj.action.say(); window.obj.action.say();
(1) obj.say()执行以后,此时这个函数里的this指向的是obj对象,缘由是由于say函数是经过obj直接调用的。
(2) obj.action.say()执行以后,此时这个函数里的this指向的是action对象,缘由是由于say函数是经过action对象直接调用的。并无经过obj直接调用。也没有经过 window 直接调用,因此此时action对象中say函数里的的this指向并不会是obj或者window。
3.2 函数做为对象的属性去调用------例三
var bj=10; var obj={ name:"八戒", age:500, say:function(){ console.log(this);//就是obj这个对象 console.log(this.bj);//undefined console.log(this.name)//八戒 function wk(){ console.log(this);//window console.log(this.bj);//10 console.log(this.name);//这里显示的是为空 } wk(); }, } obj.say();
(1) 这种状况下,say函数里的this指针仍是指向的obj,缘由是由于say函数是经过obj直接调用。
(2) 可是这时候wk函数中的this就是指向的是window了。为何呢?由于 wk()函数在 say()函数中,是属于普通函数调用,可是并无经过say或者obj直接调用,只是自执行,这个时候,wk就是一个全局函数,所以该函数的this指向的就是window。
(3) 那为何this.name是显示的为空呢?由于 window 对象中自己就有一个 name 值,并非某处添加的,若是把name换成age,获得的就是undefined了。
(4) 那怎样让wk()函数中的this指向obj呢。一种方式就是在say函数中把say()函数的this用变量保存起来,即 var that=this; 而后wk()函数使用that就能达到指向obj的目的了。另外的方式是经过apply或者call来改变。
(5) 那wk()在这里能不能写成window.wk()呢?这样是不行的,会报错,window.wk is not a function。为何不行呢,this不是指向window吗,为何widow对象里灭有wk()这个函数。。这个嘛,我也不知道,先留个坑,后面再来填 ×××
3.3 函数做为对象的属性去调用------例四
var bj=10; var obj={ name:"八戒", age:"500", say:function(){ var bj=40; console.log(this);//window console.log(this.bj);//10 console.log(this.name);//这里没有输出内容 } } var elseObj=obj.say; elseObj();
(1) 执行了elseObj()函数以后,为何say函数中的this却指向了window呢?首先要理解这句话:谁直接调用产生这个this指针的函数,this就指向谁。当obj.say赋值给elseObj的时候,elseObj只是一个函数,而并无执行,所以this指针的指向并不明确,这个时候执行到 var elseObj=obj.say的 时候,整程序至关于:
var bj=10; var elseObj=function(){ var bj=40; console.log(this); console.log(this.bj); console.log(this.name); } elseObj();
这就和 第2种 函数表达式的状况同样了。因此,当执行elseObj()的时候,this就指向window,this.obj为10,由于这时候elseObj()是经过 window 直接调用的
(2) this.name为空是由于 window 对象中自己就有一个 name 值,并非某处添加的,若是把name换成其它的好比age,获得的就是undefined了,由于全局并无age属性。
3.4 函数做为对象的属性去调用------例五
var bj=10; var obj={ name:"八戒", age:500, say:function(){ return function(){ console.log(this);//window console.log(this.bj);//10 console.log(this.age);//undefined } } } obj.say()(); // var elseObj=obj.say(); // elseObj();
(1) obj.say()()为何会有两个括号?由于obj.say()执行以后返回的是一个函数,并无执行,再加一个括号就是执行返回的那个匿名函数。
(2) 若是不习惯也可使用上面注释的那种方式,是同样的效果。
(3) 执行了函数以后,为何返回的函数中this是指向window的呢?那是由于执行obj.say()的时候,只是一个函数,至关于就是注释里的第一行代码,这时候返回的函数并未被执行。当再加一个括号的时候,就是执行了返回的那个函数,这个时候返回的函数就至关因而一个全局函数,是经过window直接调用,所以this就是指向的是window。
4. 工厂模式中this的指向------例一
var bj=10; function fun(a,b){
console.log(this);//window对象 var bj=20; var sun=new Object(); sun.one=a; sun.two=b; sun.say=function(){ console.log(this);//是sun对象,{one: 2, two: 3, say: ƒ()} console.log(this.bj);//undefined console.log(this.one);//2 } return sun; } var wk=fun(2,3); wk.say();
(1) 话说为何叫工厂模式,我搞不太清楚,不过这个不重要,重要的是经过这个模式,在每次调用函数的时候,虽然每次都返回的是sun这个对象,可是每一个对象都是不类似的,即便内容同样,好比 var sf=fun(2,3); console.log(sf===wk);//false 。
(2) 那为何say()函数执行以后,this是指向返回的那个对象呢?这个很明显嘛,say()是经过wk这个对象直接调用的,而wk是fun函数返回sun对象。因此这里的this就指向的是返回的对象。因此this.bj为undefined,由于返回的对象中没有bj属性。
(3) 我认为这种模式最重要的仍是 renturn sun这个返回语句,这个是必不可少的。
(4) fun(a,b)这个函数中的this指向的是window,缘由是执行 var wk=fun(2,3); 的时候,fun函数已经被执行了,而且直接调用它的就是window,因此这时的this是指向的window。
4.1 工厂模式中this的指向------例二
var bj=10; function fun(a,b){
console.log(this);//window对象 var bj=20; var sun=new Object(); sun.one=a; sun.two=b; sun.say=function(){ console.log(this);//是sun对象,{one: 2, two: 3, say: ƒ()} return function(){ console.log(this);//是window对象 } } return sun; } var wk=fun(2,3); var ss=wk.say(); ss();
(1) 为何say函数中return 的函数中this是指向的window对象呢?首先,执行到 var wk=fun(2,3); 的时候,wk是一个对象。继续执行下一句代码,ss这时候是一个函数,就是经过say函数返回以后赋值的。这时候返回的函数还未执行,this指向并不明确。当执行到最后一句代码,ss()函数执行了。这时候,ss函数就是一个全局函数,是经过window直接调用的。因此这时的this指向的是window。
(2) 若是say中返回的是一个对象,对象中又有个函数,像下面同样:
sun.say=function(){ console.log(this);//是sun对象,{one: 2, two: 3, say: ƒ()} return { wk:"1", say:function(){ console.log(this); } } }
这时候执行到ss.say()的时候,this指向的就是ss这个对象,即经过say函数返回的那个对象。缘由仍是同样,say函数是经过ss直接调用的,而ss对象是wk.say()返回的对象。
5. 构造函数中this的指向
var bj=10; function Add(){ var bj=20; this.bj=30; this.say=function(){ console.log(this);//Add {bj: 30, say: ƒ()} console.log(this.bj);//30 } console.log(this) ;//Add {bj: 30, say: ƒ()} } var obj=new Add(); console.log(typeof obj);//object obj.say();
(1) 要明白构造函数的this指向,咱们须要明白调用构造函数经历的步骤:
a。建立一个新对象。
b。将构造函数的做用域赋给新对象(所以this就指向了这个新对象)。
c。执行构造函数中的代码(为这个新对象添加属性)。
d。返回新对象。
摘至js高程 6.2.2节。
(2) 构造函数与工厂模式相比:(原谅照搬js高程的话)。
a。没有显示的建立对象。
b。没有return语句。
c。直接将属性和方法赋值给 this 对象。
摘至js高程 6.2.2节。
(3) 首先,obj.say()执行以后,say函数中this的指向是obj对象,这个很明显,再也不赘述。在不用new操做符的时候,Add()函数里的this指向的就是window;可是使用了new操做符以后,Add()函数中 console.log(this) 这个this为何是obj对象,而不是window呢?
(4) 这个缘由我认为在js权威指南4.6节对象建立表达式和8.2.3构造函数使用中,有所说明。使用new操做符的时候,js先建立一个新的空对象,而后,js传入指定的参数并将这个新对象当作this的值来调用一个指定的函数。这个函数可使用this来初始化这个新建立对象的属性。因此当使用new操做符以后,函数中的this指向的是新建立的对象。因此构造函数中的this就是指向new出来的那个对象。
(5) 若是构造函数中有return语句,那么此时 var obj=new Add(); obj就是return出来的内容,可是Add函数中的this仍是指向的建立的新对象Add;
6. 原型对象中this的指向
var bj=10; function Add(){
console.log(this);//Add{}
}; Add.prototype.bj=10; Add.prototype.say=function(){ console.log(this);//Add{} return function(){ console.log(this);//window } } var obj=new Add;//没传参数能够省略括号 obj.say()();
(1) obj.say()()执行的时候,this指向的是window,这个仍是由于obj.say()执行时返回的是一个函数,而后再加一个括号,就执行返回的这个函数,此时这个函数属于全局函数,因此,this会指向window
(2) Add()这个构造函数中的this指向的是Add{},缘由和上面构造函数中this的指向同样。
(3) Add.prototype.say=function(){ console.log(this) } 这里面的this 也是指向的是Add{},至于缘由,我认为是由于say()这个函数是经过obj直接调用的,因此this指向的是obj,因此是Add{}。
总结:
要想判断函数中this的指向,只要知道谁直接调用产生this指针的函数,this就指向谁了。只是要注意使用了new 操做符以后,构造函数内部的this指向的是新对象,通俗点讲就是new出来的新实例。
更新:
再来一个例子说明一下,通俗的理解一下this指针的指向:谁直接调用产生 this 指针的函数,这函数里的 this 指针就指向谁。
var factory = function(){ this.a = 'first-A'; this.b = 'first-B'; this.c = { a:'second-A', b:function(){ console.log(this.a) return this.a } } } new factory().c.b(); // second-A
(1) 这个代码首先考的是运算符的优先级。MDN运算符优先级 能够先查看优先级再思考。
(2) new的优先级和 点运算符等级同样,从左至右执行,因此先执行 new factory() 而后在执行 点运算符。
(3) 执行了 new 操做以后。而后发现函数调用的优先级和成员访问运算符的优先级同样,而后遵循从左到有的执行方式。所以就先执行 成员访问运算符 .c
(4) 这时 .c 就是一个对象,而后再取 b 属性,是个函数。这个时候 this 指针已经产生, 而产生这个this指针的是b函数,并且是经过 c 调用的。所以此时 this 的指向就是 c 对象。因此最后打印出second-A
(5) 若是想要 c 里面的 b函数中 this指向的是 factory 实例。要么使用 bind.apply,call等方法来强行改变。 要么就把 b 函数写成 es6箭头函数的方式。这样 b 函数就没有this指针,而 b 函数里面的this,就是上一级的 this。
而后再来讲一下回调函数,当即执行函数(IIFE),点击事件 的 this 的指向。
弄清楚这个以前,咱们要知道,函数传参是按值传递,若是是基本数据类型,则是直接复制数据的值传过去。若是是引用类型,好比对象,函数这种,传递的就是该数据 在堆中存放的地址。
举个例子来证实:
var obj = { a: 10, getA: function(){ console.log(this.a); } } active(obj.getA); function active(fn) { console.log(fn === obj.getA ) // true fn(); }
(1) 形参 fn 与 obj.getA 全等,就说明了 传的是存放的地址,而不是内容。由于对象就算内容相同,存放地址不一样的话,也不会相等。
那么,回调函数就是传的 函数在堆中的地址,也就是说,回调函数中 this 的指向,决定于执行回调函数 时的执行上下文环境。
首先是 setTimeout,setInterval 这种类型的回调函数。
setTimeout的回调 --- 例一
setTimeout(function(){ console.log(this) })
(1) 这是最最经常使用的常见的定时器用法,回调函数里的this指向的是window。
(2) 由setTimeout()
调用的代码运行在与所在函数彻底分离的执行环境上。这会致使,这些代码中包含的 this
关键字在非严格模式会指向 window
(或全局)对象,严格模式下为 undefined,这和所指望的this
的值是不同的。在严格模式下,setTimeout( )的回调函数里面的this仍然默认指向window对象, 并非undefined。 这几句话是 MDN上,setTimeout中 关于 this 的问题 里对 this 指向的解释。
(3) 个人理解是:因为setTimeout属于宏任务,它的回调在延时以后才进入到主线程执行,而函数执行的时候才明确 this 的指向。执行的时候,因为没有设置内部this的指向。至关因而普通函数调用。因此会默认指向window
setTimeout的回调 --- 例二
var obj = { age:10, getage:function(){ console.log(this.age) } } setTimeout(obj.getage,1000) // undefined setTimeout(function(){ obj.getage() // 10 },1000)
(1) 第一个setTimeout,执行obj.getage 以后,至关于setTimeout的回调是一个匿名函数,执行的时候,函数内部未设置this的指向。至关因而普通函数调用。因此this默认指向window,因此结果是undefined。
(2) 第二个setTimeout,传给setTimeout的也是一个匿名回调函数,执行匿名函数,执行到 obj.getage() 的时候,getage函数里的this,指向的就是obj了,因此能打印出10。仍是遵循 谁调用产生 this指针的函数,this就指向谁的规则
对于 数组的遍历方法:
foreach,map,filter,some,每次 callback
函数被调用的时候,this
都会指向 最后一个参数 thisArg
的这个对象。若是省略了 thisArg
参数,
或者赋值为 null
或 undefined
,则 this 指向全局对象 。在严格模式下则是undefined(未传值的时候)。若是用箭头函数的写法,就要看当前上一层的 this 指向的是哪里了
reduce 累加器的参数中并无 thisArg 对象能够传,可是在回调函数中,this指向的是window。若是用箭头函数的写法,就要看当前上一层的 this 指向的是哪里了
对于 点击,移入移出等相似事件的回调函数 的 this 指向。
<button type="button" id="btn">点我啊</button> function getDom(){ console.dir(this) } // 第一种调用方法 document.getElementById('btn').addEventListener('click',function(){ getDom(); }) // 第二种调用方法 document.getElementById('btn').onclick = getDom() // 第三种调用方法 document.getElementById('btn').addEventListener('click',getDom) // 第四种调用方法 <button type="button" id="btn" onclick="console.log(this)">点我啊</button>
(1) 第一种调用方法,this指向的是window。虽然在function(){} 回调函数里的 this 指向的是button这个DOM对象,可是getDom是在这里面调用的,和普通函数调用没什么区别。因此也指向window
(2) 第二种都不用点击,直接触发,this指向window。由于直接当作普通函数调用了。
(3) 第三种方法,this指向 button这个DOM对象。回调函数传入的是函数执行的地址,执行的时候至关因而在window环境下执行,因此getDom的this指向的是window
(4) 第四种方式,this指向 button 这个DOM对象。
当函数被用做事件处理函数时,它的this
指向触发事件的元素(一些浏览器在使用非addEventListener
的函数动态添加监听函数时不遵照这个约定)。 --- MDN
对于 当即执行函数 IIFE 中 this的指向,指向的是window
(function(){ console.log(this) // window })()
到这里,我仍是没搞懂下面这种状况:
var obj={ age:10, say:function(){ function get(){ console.log(this) // window } get(); } } obj.say();
get函数里的this指向的是window,由于get函数 独立调用,并无为内部 this 明确指向。因此会指向 window 。若是是严格模式,则指向undefined。
不明白的是:
(1)既然 this 指向的是window,为何get函数在window上不能访问?
(2)这种在函数内部定义并执行的方式,和当即执行函数有没有区别?
(3)词法分析的时候,这个函数是被怎样处理的?