资讯专栏INFORMATION COLUMN

关于javascript中的this指向

苏丹 / 554人阅读

摘要:当中的是一个用于指向当前上下文对象的关键字。创建实例时的构造函数中的,永远指向那个实例后对象,不是外部环境使用来调用函数时,先改变其上下文环境,在对其构造函数进行调用。

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

上下文对象概念

在我的深入贯彻闭包思想一章中其实已经讲了很多关于环境栈(即上下文对象)的相关内容,但内容过于冗长,不便阅读,时间充足的同学可以去看下,但这篇中,我们还是独立开来,下面我总结了几条有关上下文的知识,希望能帮助大家。

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

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

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

1.全局(Global code)

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

 //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)

以上两种情况指的是在特定情境下的代码片段,但是不包括另一种情况,也就是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必然是这个实例。他的顺序大致是

创建上下文环境

执行构造函数

把构造函数的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都不一样,如果不看底层编译原理,在这个层面之上,我们只能靠这种方法理解了。。。但是记住,凡事都要自己求证的,别人说的是他人的观点,自己理解出来的才是最适合你的.

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/89220.html

相关文章

  • 关于javascript 中的高级定时器的若干问题

    摘要:闭包闭包是指有权访问另一个函数作用域中的变量的函数当某个函数被调用时,会创建一个执行环境及相应的作用域链。要注意通过第句声明的这个方法属于构造函数生成的对象,而不属于构造函数的变量对象,也就是说,并不存在于作用域链中。 看到评论里有仁兄建议我试试箭头函数,真是受宠若惊,本来写这篇文章也只是想记录写要点给自己日后看的。今天早上看到一篇总结javascript中this的文章JavaScr...

    zr_hebo 评论0 收藏0
  • 关于javascript中的bind、call、apply等函数的用法

    摘要:其实它们都很简单,但是在处理一些与相关的函数的时候,用来改变函数中的指向,却是必不可少的工具,所以必须掌握好它们的用法。 关于javascript中的bind、call、apply等函数的用法 我GitHub上的菜鸟仓库地址: 点击跳转查看其他相关文章 文章在我的博客上的地址: 点击跳转         前面的文章已经说到this的指向了,那么这篇文章就要说一说和this相关的三个...

    lordharrd 评论0 收藏0
  • [JavaScript 随笔] 关于 this 你必须知道这几点

    摘要:关于中的坑大家都踩过。那这里的和是严格相等的。这里介绍的是通过创建对象时的。提示一下,数组对象的函数本身就是有这个功能的,也就是说可以达到要求。事件有两种记法,一个是也是类似,那么在中出现的表示触发该事件的元素,也就是。 TL;DR: this 指向调用该方法的对象,只有函数执行时,this 才有定义。 关于 JavaScript 中 this 的坑大家都踩过。像本文开头的这句话,道理...

    邹强 评论0 收藏0
  • 关于JavaScript函数调用的几种模式

    摘要:函数的调用有五种模式方法调用模式,函数调用模式,构造器调用模式,调用模式以及回调模式,下面分别对这几种模式进行说明。构造器调用模式构造函数的调用方式被称为构造器调用模式,这是模拟类继承式语言的一种调用方式。 函数的调用有五种模式:方法调用模式,函数调用模式,构造器调用模式,apply/call调用模式以及回调模式,下面分别对这几种模式进行说明。 1.函数调用与方法调用模式: 1.1 声...

    邹强 评论0 收藏0
  • 关于javascriptthis指向

    摘要:关于中的指向我上的菜鸟仓库地址点击跳转查看其他相关文章文章在我的博客上的地址点击跳转学习,必不可少的肯定要理解的指向。 关于javascript中this的指向 我GitHub上的菜鸟仓库地址: 点击跳转查看其他相关文章 文章在我的博客上的地址: 点击跳转         学习javascript,必不可少的肯定要理解this的指向。要学习this指向之前,就要先理解了我前面写的几...

    shinezejian 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<