图说js中的this——深刻理解javascript中this指针

没搞错吧!js写了那么多年,this仍是会搞错!没搞错,javascript就是回搞错!javascript

…………html

文章来源自——周陆军的我的网站:http://zhoulujun.cn/zhoulujun/html/webfront/ECMAScript/jsBase/2016_0329_7729.htmljava

在写java的时候,this用错了,idea都会直接报错!程序员

好比……web

blob.png

可是,js,……idea,心有余而力不足了……面试

在面向对象编程里有两个重要的概念:一个是类,一个是实例化的对象,类是一个抽象的概念,用个形象的比喻表述的话,类就像一个模具,而实例化对象就是经过这个模具制造出来的产品,实例化对象才是咱们须要的实实在在的东西,类和实例化对象有着很密切的关系,可是在使用上类的功能是绝对不能取代实例化对象,就像模具和模具制造的产品的关系,两者的用途是不相同的。编程

  有上面代码咱们能够看到,this指针在java语言里只能在实例化对象里使用,this指针等于这个被实例化好的对象,而this后面加上点操做符,点操做符后面的东西就是this所拥有的东西,例如:姓名,工做,手,脚等等。闭包

 

  其实javascript里的this指针逻辑上的概念也是实例化对象,这一点和java语言里的this指针是一致的,可是javascript里的this指针却比java里的this难以理解的多,究其根本缘由我我的以为有三个缘由:app

 

  缘由一:javascript是一个函数编程语言,怪就怪在它也有this指针,说明这个函数编程语言也是面向对象的语言,说的具体点,javascript里的函数是一个高阶函数,编程语言里的高阶函数是能够做为对象传递的,同时javascript里的函数还有能够做为构造函数,这个构造函数能够建立实例化对象,结果致使方法执行时候this指针的指向会不断发生变化,很难控制。编程语言

 

  缘由二:javascript里的全局做用域对this指针有很大的影响,由上面java的例子咱们看到,this指针只有在使用new操做符后才会生效,可是javascript里的this在没有进行new操做也会生效,这时候this每每会指向全局对象window。

 

  缘由三:javascript里call和apply操做符能够随意改变this指向,这看起来很灵活,可是这种不合常理的作法破坏了咱们理解this指针的本意,同时也让写代码时候很难理解this的真正指向

找你妹 this指针寻找

上面的三个缘由都违反了传统this指针使用的方法,它们都拥有有别于传统this原理的理解思路,而在实际开发里三个缘由又每每会交织在一块儿,so,this,云里雾里了……

javascript高级程序设计

入门书:professionnal Javascript for web devolopers,——高级的说法是这样的:

this老是指向调用该方法的对象!

关键字this指向

 

 var name="zhoulujun";
    function say(){
        console.log(this.name)
    }
    say(); //zhoulujun

blob.png

在script标签里咱们能够直接使用this指针,this指针(指向window对象,结果)就是window对象,即便使用三等号它们也是相等的。全局做用域经常会干扰咱们很好的理解javascript语言的特性,这种干扰的本质就是:

在javascript语言里全局做用域能够理解为window对象,记住window是对象而不是类,也就是说window是被实例化的对象,这个实例化的过程是在页面加载时候由javascript引擎完成的,整个页面里的要素都被浓缩到这个window对象,由于程序员没法经过编程语言来控制和操做这个实例化过程,因此开发时候咱们就没有构建这个this指针的感受,经常会忽视它,这就是干扰咱们在代码里理解this指针指向window的情形。

这里this指向window对象,因此this.name->zhoulujun!

javascript 函数的做用域链


当执行 say函数的时候, JavaScript 会建立一个 Execute context (执行上下文),执行上下文中就包含了 say函数运行期所须要的全部信息。 Execute context 也有本身的 Scope chain, 当函数运行时, JavaScript 引擎会首先从用 say函数的做用域链来初始化执行上下文的做用域链。 

这方面的知识,建议参考:

http://blog.csdn.net/wangxiaohu__/article/details/7260668

http://www.jb51.net/article/30706.htm

这里能够大体记一下:

 

var myObj={
    name:"zhoulujun",
    fn:function(){
        console.log(this.name)
    }

};
 myObj.fn();

blob.png

 

这里的this指向obj,由于fn()运行在obj里面……

而后再来看……

var name="zhoulujun";
function say(){
    console.log(this.name)
    console.log(this)
}
say();

function say2(){
    var site="zhoulujun.cn";
    console.log(this.site);
}
say2();

blob.png

 

myObj2={
    site:"zhoulujun.cn",
    fn:function(){
        console.log(this.site)
    }
}

 

blob.png

这里的this指向的是对象myObj2,由于你调用这个fn是经过myObj2.fn()执行的,那天然指向就是对象myObj2,这里再次强调一点,this的指向在函数建立的时候是决定不了的,在调用的时候才能决定,谁调用的就指向谁,必定要搞清楚这个

 

而后,咱们更深刻(受不了 …………

myObj3={
    site:"zhoulujun.cn",
    andy:{
        site:"www.zhoulujun.cn",
        fn:function(){
            console.log(this.site)
        }
    }
};
myObj3.andy.fn();

blob.png

这里一样也是对象Object点出来的,可是一样this并无执行它,那你确定会说我一开始说的那些不就都是错误的吗?其实也不是,只是一开始说的不许确,接下来我将补充一句话,我相信你就能够完全的理解this的指向的问题。

若是,你实在理解不了,就这么样背下来吧!

 

状况1:若是一个函数中有this,可是它没有被上一级的对象所调用,那么this指向的就是window,这里须要说明的是在js的严格版中this指向的不是window,可是咱们这里不探讨严格版的问题,你想了解能够自行上网查找。

 

状况2:若是一个函数中有this,这个函数有被上一级的对象所调用,那么this指向的就是上一级的对象。

 

状况3:若是一个函数中有this,这个函数中包含多个对象,尽管这个函数是被最外层的对象所调用,this指向的也只是它上一级的对象,若是不相信,那么接下来咱们继续看几个例子。

这样既对了吗??深刻点(就受不了了……讨厌……

    myObj3={
        site:"zhoulujun.cn",
        andy:{
            site:"www.zhoulujun.cn",
            fn:function(){
                console.log(this)
                console.log(this.site)
            }
        }
    };
//    myObj3.andy.fn();
    var fn=myObj3.andy.fn;
    fn();

blob.png

其实,这里的 fn等价于

 fn:function(age){

            console.log(this.name+age);

        }

下面咱们来聊聊函数的定义方式:声明函数和函数表达式

咱们最上面第一个案例定义函数的方式在javascript语言称做声明函数,第二种定义函数的方式叫作函数表达式,这两种方式咱们一般认为是等价的,可是它们实际上是有区别的,而这个区别经常会让咱们混淆this指针的使用,咱们再看看下面的代码:

blob.png

为何say能够执行,say3不能够?那是由于:

blob.png

为何say3打印结果是undefined,我在前文里讲到了undefined是在内存的栈区已经有了变量的名称,可是没有栈区的变量值,同时堆区是没有具体的对象,这是javascript引擎在预加载扫描变量定义所致,可是ftn01的打印结果很使人意外,既然打印出完成的函数定义了,并且代码并无按顺序执行,这只能说明一个问题:

 

在javascript语言经过声明函数方式定义函数,javascript引擎在预处理过程里就把函数定义和赋值操做都完成了,在这里我补充下javascript里预处理的特性,其实预处理是和执行环境相关,在上篇文章里我讲到执行环境有两大类:全局执行环境和局部执行环境,执行环境是经过上下文变量体现的,其实这个过程都是在函数执行前完成,预处理就是构造执行环境的另外一个说法,总而言之预处理和构造执行环境的主要目的就是明确变量定义,分清变量的边界,可是在全局做用域构造或者说全局变量预处理时候对于声明函数有些不一样,声明函数会将变量定义和赋值操做同时完成,所以咱们看到上面代码的运行结果。因为声明函数都会在全局做用域构造时候完成,所以声明函数都是window对象的属性,这就说明为何咱们无论在哪里声明函数,声明函数最终都是属于window对象的缘由了。

这里推荐看下——java一个类的执行顺序:

http://www.zhoulujun.cn/zhoulujun/html/java/javaBase/7704.html

blob.png

其实在javascript语言里任何匿名函数都是属于window对象,它们也都是在全局做用域构造时候完成定义和赋值,可是匿名函数是没有名字的函数变量,可是在定义匿名函数时候它会返回本身的内存地址,若是此时有个变量接收了这个内存地址,那么匿名函数就能在程序里被使用了,由于匿名函数也是在全局执行环境构造时候定义和赋值,因此匿名函数的this指向也是window对象,因此上面代码执行时候fn的this都是指向window,由于javascript变量名称无论在那个做用域有效,堆区的存储的函数都是在全局执行环境时候就被固定下来了,变量的名字只是一个指代而已。

 

相似的状况(面试题喜欢这么考!)……好比:

blob.png

this都是指向实例化对象,前面讲到那么多状况this都指向window,就是由于这些时候只作了一次实例化操做,而这个实例化都是在实例化window对象,因此this都是指向window。咱们要把this从window变成别的对象,就得要让function被实例化,那如何让javascript的function实例化呢?答案就是使用new操做符。

再来看 构造函数:

function  User(){
    this.name="zhoulujun";
    console.log(this);
}
var andy=new User();
console.log(andy.name)

blob.png

 

why andy 的name  是 zhoulujun,那是:由于:

new关键字能够改变this的指向,将这个this指向对象andy,

那andy何时又成了思密达,oh,no,is Object?

由于用了new关键字就是建立一个对象实例(重要的事情默读三遍)

咱们这里用变量andy建立了一个User用户实例(至关于复制了一份User到对象andy里面),此时仅仅只是建立,并无执行,而调用这个函数User的是对象andy,那么this指向的天然是对象andy,那么为何对象User中会有name,由于你已经复制了一份User函数到对象andy中,用了new关键字就等同于复制了一份。

java 程序猿: Class user=new User();似曾相识木有……

blob.png

function既是函数又能够表示对象,function是函数时候还能当作构造函数,javascript的构造函数我常认为是把类和构造函数合二为一,固然在javascript语言规范里是没有类的概念,可是我这种理解能够做为构造函数和普通函数的一个区别,这样理解起来会更加容易些

 

下面我贴出在《javascript高级编程》里对new操做符的解释:

new操做符会让构造函数产生以下变化:

1.       建立一个新对象;

2.       将构造函数的做用域赋给新对象(所以this就指向了这个新对象);

3.       执行构造函数中的代码(为这个新对象添加属性);

4.       返回新对象



 

……

妈的:读的那么拗口,不明觉厉…………看图……还不

不明白……

var myObj5={
    name:"andy"
};
var myObj6=new Object();
myObj6.name="andy";

 function  say5(name){
     console.log(name)
 }
 var say6=new Function("name","console.log(name)");
console.log(myObj5)
console.log(myObj6)
 say5("andy");
 say6("andy");

Unnamed QQ Screenshot20160330175322.jpg

 

还不明白,就请奶奶买块豆腐,撞死算了……

第四点也要着重讲下,记住构造函数被new操做,要让new正常做用最好不能在构造函数里写return,没有return的构造函数都是按上面四点执行,有了return状况就复杂了

return这王八蛋……

blob.png

那么我这样呢……

blob.png

 

 

does it have to be like this?Tell me why(why),is there something I have missed?

Tell me why(why),cos I don't understand…………

tell me why 歌手

那是由于……because of u?no  return……blob.png

 

因此:若是返回的是基本类型,就会丢掉…只能返回Object类型……typeof  xx ===“object”

看到called 没有?什么鬼!!

其实new关键字会建立一个空的对象,而后会自动调用一个函数apply方法,将this指向这个空对象,这样的话函数内部的this就会被这个空的对象替代。

 var a={
     name:"andy",
     site:"zhoulujun.cn",
     fn:function(age){
         console.log(this.name+age);
     }
 };
var b={
    name:"zhoulujun",
    site:"www.zhoulujun.cn",
    fn:function(age){
        console.log(this.name+age);
    }
};
a.fn(2); //andy2
a.fn.call(b,2) //zhoulujun2
a.fn.apply(b,[2])//zhoulujun2

固然,还有bind……

…………

写到这里,都看不下去,逻辑有点混乱,有的是从前辈哪里引用的。

改天有时间整理下,而后,在去讲下闭包(……closer

参考文章:

http://blog.jobbole.com/81018/

http://blog.jobbole.com/74110/

http://www.cnblogs.com/aaronjs/archive/2011/09/02/2164009.html#commentform

http://www.codeceo.com/article/javascript-this-pointer.html

 文章来源自——周陆军的我的网站:http://zhoulujun.cn/zhoulujun/html/webfront/ECMAScript/jsBase/2016_0329_7729.html

相关文章
相关标签/搜索