函数在执行时,会在函数体内部自动生成一个this指针。谁直接调用产生这个this指针的函数,this就指向谁。

怎么理解指向呢,我认为指向就是等于。例如直接在js中输入下面的等式:

console.log(this===window);//true

情况不同,this指向的对象也不同。例如:

1.  函数声明的情况

var bj=10;
function add(){
var bj=20;
console.log(this);//window
console.log(this.bj);//
console.log(bj);//
console.log(this.bj+bj);//
}
add();
window.add();

(1) 执行了add()之后,此时的this指向的是window对象,为什么呢?因为这时候add是全局函数,是通过window直接调用的。所以下面我专门写了个window.add()就是为了说明,全局函数的this都是指向的window。

(2) 就像alert()自带的警告弹窗一样,window.alert()执行之后也是一样的效果。所以只要是   window点   这种调用方式都可以省略掉,因此警告弹窗可以直接使用alert()。

2.  函数表达式

var bj=10;
var zjj=function(){
var bj=30;
console.log(this);//window
console.log(this.bj);//
console.log(bj);//
console.log(this.bj+bj);//
}
console.log(typeof zjj);//function
zjj();
window.zjj();

(1) 执行了zjj()之后,函数中的this也是指向window对象。原因和第一个是一样的,都是通过window这个对象直接调用

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);//
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);//
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);//
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);//
}
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);//
}
 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() //
},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)词法分析的时候,这个函数是被怎样处理的?


更新:

以前没搞懂为什么上面内部申明的 get()方法不能在window上访问,其实很简单,只是自己想复杂了而已:

get函数是在say函数里面 创建的,也就是说,不管怎么调用,get函数的作用域都只能是在say函数里面。而get方法是自执行,并没有其它任何对象直接调用,所以this是指向window,但是作用域是say函数里面,却不是window。感觉和立即执行函数没区别。

不要理解成 this 指向window,产生这个this的函数就一定在window作用域上挂载。this指向 和 函数作用域 并不是相互的

由于js是采用的静态作用域(也叫词法作用域),这就意味着函数的执行依赖于函数定义的时候所产生(而不是函数调用的时候产生的)的变量作用域。

所以,函数的作用域是基于函数创建(可以理解为函数定义的时候)的地方,也就是函数在哪里创建,不管是否返回这个函数,或者返回带括号(已执行)的函数,都不用在意,只要知道是在哪里定义即创建的就知道函数的作用域是什么内容了。

在全局作用域中“定义”一个函数的时候,只会创建包含全局作用域的作用域链。只有“执行”该函数的时候,才会复制创建时的作用域,并将当前函数的局部作用域放在作用域链的顶端。

去取变量值的时候,首先看本函数里有没有该值,如果没有再到函数定义的外部去找

深入理解JS函数中this指针的指向的更多相关文章

  1. 在JS函数中执行C#中的函数、字段

    1.调用字段 cs文件的代码: ; protected void Page_Load(object sender, EventArgs e) { id = ; } js页面的代码: function ...

  2. js函数中获得当前被点击元素

    问题描述:在html页面中点击<a>或者’按钮‘,进入js中的函数,在js函数中获得被点击那个<a>或‘按钮’元素 解决方法:方法一: html中: <a>标签:& ...

  3. JS高级---函数中的this的指向,函数的不同调用方式

    函数中的this的指向 普通函数中的this是谁?-----window 对象.方法中的this是谁?----当前的实例对象 定时器方法中的this是谁?----window 构造函数中的this是谁 ...

  4. js函数中参数的传递

    数据类型 在 javascript 中数据类型可以分为两类: 基本类型值 primitive type,比如Undefined,Null,Boolean,Number,String. 引用类型值,也就 ...

  5. js对象系列【二】深入理解js函数,详解作用域与作用域链。

    这次说一下对象具体的一个实例:函数,以及其对应的作用域与作用域链.简单的东西大家查下API就行了,这里我更多的是分享自己的理解与技巧.对于作用域和作用域链,相信绝大多数朋友看了我的分享都能基本理解,少 ...

  6. 深入理解C语言中的指针与数组之指针篇

    转载于http://blog.csdn.net/hinyunsin/article/details/6662851     前言 其实很早就想要写一篇关于指针和数组的文章,毕竟可以认为这是C语言的根本 ...

  7. 深入理解C语言中的指针与数组之指针篇(转载)

    前言 其实很早就想要写一篇关于指针和数组的文章,毕竟可以认为这是C语言的根本所在.相信,任意一家公司如果想要考察一个人对C语言的理解,指针和数组绝对是必考的一部分. 但是之前一方面之前一直在忙各种事情 ...

  8. js函数中的this关键字

    关于这个this关键字,也是很多项目中常常被用到的,那么,有人也许会问,干嘛要用this呢,在函数被调用时,直接指明是什么对象在调用不就行了?还整那么个模模糊糊的概念出来干嘛?不过嘛,存在即真理,既然 ...

  9. js函数中this的不同含义

    1.js函数调用过程中,js线程会进入新的执行环境并创建该环境的变量对象,并添加两个变量:this和arguments,因此可以在函数中使用这两个变量.需要注意的是,this变量不能重新赋值,而arg ...

随机推荐

  1. How to enable usb on vbox

    Device-->Install Guest Addition Shared Folders Settings-->Advanced-->Shared Clipboard--> ...

  2. Modification Default Identity Table

    Step 1. Open IdentityModel.cs to add following code. protected override void OnModelCreating(DbModel ...

  3. kubernetes增加污点,达到pod是否能在做节点运行

    master node参与工作负载 (只在主节点执行)使用kubeadm初始化的集群,出于安全考虑Pod不会被调度到Master Node上,也就是说Master Node不参与工作负载. 这里搭建的 ...

  4. puppet一个完整的实例

    一个具体实例来简单说明puppet的具体结构 创建第一个配置 puppet的组成清单这主要包含这几个部分 资源,文件,模板,节点,类,定义 puppet中有个模块的定义,这个比较重要,基本是puppe ...

  5. Vue——轻松实现vue底部点击加载更多

    前言 需求总是不断改变的,好吧,今天就把vue如何实现逐步加载更多和分布加载更多说下,默认你知道如何去请求数据的哈 一次请求 页面 使用slice来进行限制展现从0,a的数据 <div v-fo ...

  6. Longest Ordered Subsequence POJ - 2533 最长上升子序列dp

    题意:最长上升子序列nlogn写法 #include<iostream> #include<cstdio> #include<cstring> #include&l ...

  7. 洛谷P3690 Link Cut Tree (模板)

    Link Cut Tree 刚开始写了个指针版..调了一天然后放弃了.. 最后还是学了黄学长的板子!! #include <bits/stdc++.h> #define INF 0x3f3 ...

  8. bzoj 1103 : [POI2007]大都市meg (树链剖分+线段树)

    Description 在经济全球化浪潮的影响下,习惯于漫步在清晨的乡间小路的邮递员Blue Mary也开始骑着摩托车传递邮件了.不过,她经常回忆起以前在乡间漫步的情景.昔日,乡下有依次编号为1..n ...

  9. 解决 phpstorm 运行卡,自动关闭等问题

    解决 phpstorm 自动关闭问题: 使用文件搜索工具(可在本博客搜索“管理工具”,或查找安装目录) 找到phpstorm.vmoptions文件,使用记事本打开. 添加以下两行代码: -Dawt. ...

  10. [luogu3878][TJOI2010]分金币【模拟退火】

    题目描述 现在有n枚金币,它们可能会有不同的价值,现在要把它们分成两部分,要求这两部分金币数目之差不超过1,问这样分成的两部分金币的价值之差最小是多少? 分析 根据模拟退火的基本套路,先随机分两堆金币 ...