关于javascript中的this指向

javascript 当中的 this是一个用于指向当前上下文对象的关键字。在面向对象编程及平常开发当中咱们常常与其打交道,初学javscript的朋友很是容易误入歧途从而理解错误。javascript

上下文对象概念

在个人深刻贯彻闭包思想一章中其实已经讲了不少关于环境栈(即上下文对象)的相关内容,但内容过于冗长,不便阅读,时间充足的同窗能够去看下,但这篇中,咱们仍是独立开来,下面我总结了几条有关上下文的知识,但愿能帮助你们。java

首先须要声明的是这些术语的问题,其实咱们常说的环境栈调用栈上下文栈 等等不少民间自创术语,它们形容的都是一个东东,即环境栈(这里我就拿第一个词来形容了,你们知道就好)面试

执行上下文栈 (Execution context stack, ECS)

javascript代码块有三种运行环境分别是:编程

1.全局(Global code)redux

默认执行环境,也就是没有任何函数包裹在window下执行的代码片断。segmentfault

//window
 var a = 12;

 (function(){
    a = 13;
 }())

就好比这两段代码前者在全局window下执行了一个新建变量与赋值的操做。后者是一个匿名自调函数,它的上下文必定是Global,因此,不管在哪里运行它,它只能访问自身的活动对象以及Global的变量对象数组

2.函数(Function code)浏览器

function fun(){
   a = 24;
   return a;
 }

 var foo = new Function("var a = 12; return a;");

 foo();
 fun();

图片描述 除外的Function它表示在函数当中执行的代码.闭包

3.eval (Eval code)app

以上两种状况指的是在特定情境下的代码片断,可是不包括另外一种状况,也就是eval环境 (这里应指动态环境,由于据我所知以目前JS版本,只有eval能够建立动态执行环境吧? )。

var codeStr = "var a = 12";
  eval(codeStr);

以上三种执行上下文构成了执行上下文栈(ECS),在整个栈中有依次排列的上下文(Execution Context),位于最底层的是Global全局环境,上面依次是函数执行环境。而eval则会在运行中javascript引擎临时去建立,也就是没有在预编译前几毫秒进行编译代码整合。

图片描述

在每一个EC当中存有几个特别重要的属性,变量对象,做用域链,this指向

  • 变量对象(Variable Object,VO)

    • 变量
    • 函数的声明
    • 函数的参数
  • 做用域链(Scope Chain)
    构成做用域的开山鼻祖? (确切的说应该是词法做用域,但不能排除eval建立的动态环境)
  • this 指向
    没错,它储存一个对象的引用地址,但不是在词法阶段定义的,而是在运行时绑定的,它引用的值取决于函数调用时的各类条件。只取决于函数的调用方式。

变量对象里面存储了在上下文中定义的变量和函数声明。在函数上下文环境下不能直接使用变量对象,而是要等到执行流进入当前环境下来激活,被称做活动对象

注:上面说了在一个EC中它们是属于特别重要的属性(其构成了最基本的做用域规则),也就是说还有其余javascript内部实现须要的属性,它们大概包含函数的调用方式,在哪里被调用等等.

好了整理一下,`在这一小节里面,咱们大体了解了ECS及EC的概念以及this与他们的关系及其this的定义.

使用 this

// Global Context
 var obj = {
   fun:()=>()=>{ console.log(this); },
   foo:()=>{ console.log(this); }
 }
 
 obj.foo.name = "tongtong";

 /*
    把foo函数转赋给context变量,这时foo函数对象身上的属性是具有`复合类型条件`的,即引用.
 */

 var context = obj.foo;

 //经过返回的函数来二次调用,一样属于全局环境下,由于内部函数调用时已经不属于obj对象了.
 obj.fun()();  //window
 
 context();  //window
 context.name; // "tongtong"

函数不是简单类型值,是复合类型,咱们同样能够用.操做符像对待对象同样为它添加属性与方法。与其余两位小伙伴(Array Object)不一样的是,它能够调用呀。只要调用环境变化了,this固然也就随波逐流。

以上这个例子,咱们须要注意,在把它们赋给其余变量时,因为它的执行环境是会变化的,即每次调用都会刷新this,对于新手来讲,这点必定要搞清楚。

//global Context

//no.1
(function(){ console.log(this); }()) //window

//no.2
var x = 12;
var foo = function(){
    this.x = 12;
}

new foo();

x //12
  • 在闭包中this是指向window的. 如若否则呢?它没有任何调用者,它是自调,只能是window,也能够这样理解 : 全部以函数调用的方式this必定是window,即foo()。而 obj.foo(); new foo()它们的this是有意义的。
  • 建立实例时的构造函数中的this,永远指向那个实例后对象,不是外部环境. 使用new来调用函数时,先改变其上下文环境,在对其构造函数进行调用。对顺序不清楚的同窗,请自行查阅关于 javascript生成实例步骤.

call 与 apply

这两个方法位于Function.prototype,拥有更改上下文的功能,前者需手动填写函数参数,后者能够经过数组来表示。

// Global Context
 
 var obj = {};

 //call
 var foo = function(num){ 
    console.log(this);
    console.log(num + 8);
 };

 foo.call(12); // obj 20
 foo(12);  //window 12

 //apply
 var fun = function(){     
    console.log(this);
 console.log(Object.prototype.slice.call(arguments).redux((x,y)=> x + y); ) 
 }

 fun.call(obj,[12,34,56,78]);
 fun(null,[12,34,56,78]);

他们都把第一个参数做为上下文并调用。那么如何更改一个函数的上下文调用环境呢?咱们以bind来简单模拟一下。

var _bind = function(fun,context){
    
    //取第一个以后的参数列表.
    var params = [].slice.call(arguments).slice(2);
 
    //给咱们的函数指定上下文对象。
    context.fun = fun;
    
    return function(){ var result = params.concat(Array.from(arguments)); return context.fun(result)};
}

至于call,apply方法的模拟实现,这里就不说了,感兴趣的童鞋能够去网上搜索一下.

常见面试题

var a = “111”;
var b = “222”;
function test() { 

  var a = “333”;
  var b = “444”; 
  alert(this.a);
  alert(this.b); 

}
var test= new test() ;
alert(test.a);
alert(test.b);

在建立一个实例时,它的内部的this必然是这个实例。他的顺序大体是

  1. 建立上下文环境
  2. 执行构造函数
  3. 把构造函数的prototype,放到实例的原型上

这里咱们只须要知道它先建立上下文,再进行调用构造函数.


var obj = {
    Pagination:function(){ return this; }
};

new obj.Pagination(); // Pagination {}

查找this的机制与做用域一致,都是就近原则。


setTimeout(function(){ return this; },0);  //window

setTimeout回调的执行环境下的this对象是window,几乎浏览器的全部原生方法回调函数的this都是window,可是若是咱们在其场景使用回调,这个this就变成不可预期的了,由于咱们不知道此函数,到底在哪里执行了.

忆之获

  • this是执行时决定的,也能够说函数调用时,决定的。
  • EC中附带的三类对象(非所有),其中this做为其中一员,每次执行流进入EC时,它都有可能会更新。而this的改变正是依赖这种现象。
  • this使用就近原则,从当前环境向外延伸,直至找到离它最近的那个对象为止。
  • 列表项目

题外话

我特别但愿你们能在文章中给我提出意见,文中内容都是博主我的理解,算不上是对的,因此我建议你们只作参考,或看其余相似文章作一个本身的总结,每一个人理解javascript都不同,若是不看底层编译原理,在这个层面之上,咱们只能靠这种方法理解了。。。可是记住,凡事都要本身求证的,别人说的是他人的观点,本身理解出来的才是最适合你的.

相关文章
相关标签/搜索