1、this是啥?

  简言之,this是JavaScript语言中定义的众多关键字之一,它的特殊在于它自动定义于每一个函数域内,但是this倒地指引啥东西却让很多人张二摸不着头脑。这里我们留个小悬念,希望看完这篇文章了你能回答出来this到底指引个甚。


  2、this有啥用?

  那边观众又该问了,既然this这么难以理解,那么为个甚还要用它呢?我们来看个例子:

 1 function identify() {
2 return this.name.toUpperCase();
3 }
4 function sayHello() {
5 var greeting = "Hello, I'm " + identify.call( this );
6 console.log( greeting );
7 }
8 var person1= {
9 name: "Kyle"
10 };
11 var person2= {
12 name: "Reader"
13 };
14 identify.call( person1); // KYLE
15 identify.call( person2); // READER
16 sayHello.call( person1); // Hello, I'm KYLE
17 sayHello.call( person2); // Hello, I'm READER

  这段代码很简单,我们定义了两个函数,分别为identify和sayHello。并且在不同的对象环境下执行了它们,达到了复用的效果,而不用为了在不同的对象环境下执行而必须针对不同的对象环境写对应的函数了。简言之,this给函数带来了复用。那边客官又问了,我不用this一样可以实现,如:

 1 function identify(context) {
2 return context.name.toUpperCase();
3 }
4 function sayHello(context) {
5 var greeting = "Hello, I'm " + identify( context);
6 console.log( greeting );
7 }
8 var person1= {
9 name: "Kyle"
10 };
11 var person2= {
12 name: "Reader"
13 };
14 identify( person1); // KYLE
15 identify( person2); // READER
16 sayHello( person1); // Hello, I'm KYLE
17 sayHello( person2); // Hello, I'm READER

  仔细一看,这位客官给出的解决方法的确也达到了类似的效果。赞一个!我想说的是,随着代码的增加,函数嵌套、各级调用等变得越来越复杂,那么传递一个对象的引用将变得越来越不明智,它会把你的代码弄得非常乱,甚至你自己都无法理解清楚。而this机制提供了一个更加优雅而灵便的方案,传递一个隐式的对象引用让代码变得更加简洁和复用。好了,知道了this的用处,那么再看看我们对它的误解。

  3、关于this的误解


  相信很多童鞋是学过其它语言的,在很多编程语言中都有this的机制,惯性思维把其它语言里对它的理解带到了JavaScript中。同时,由于this这个单词的理解导致了我们产生了对它各种各样的误解。所以,开始前,我们先澄清下对它的误解。

  3.1 误解一:this引用function本身

  我们都知道,在函数里引用函数可以达到递归和给函数属性赋值的效果。而这在很多应用场景下显得非常有用。所以,很多人都误以为this就是指引function本身。例如:

 1 function fn(num) {
2 console.log( "fn: " + num );
3 // count用于记录fn的被调用次数
4 this.count++;
5 }
6 fn.count = 0;
7 var i;
8 for (i=0; i<10; i++) {
9 if (i > 5) {
10 fn( i );
11 }
12 }
13 // fn: 6
14 // fn: 7
15 // fn: 8
16 // fn: 9
17
18 console.log( fn.count ); // 0 -- 耶?咋不是4捏?

  上面我们想要记录fn被调用的次数,可是明显fn被调用了四次但count仍然为0。咋回事捏?这里简单解释下,fn里第4行的自增隐式的创建了一个全局变量count,由于初始值为undefined,所以每一次自增其实依然不是一个数字,你在全局环境下打印count(window.count)输出的应该是NaN。而第6行定义的函数熟悉变量count依然没变,还是0。如果对这个执行结果不清楚的,欢迎去看我前些天的那篇博文(聊一下JS中的作用域scope和闭包closure scope和closure),在这里你只需要知道,this引用的是function这种理解是错误的就行。

  这边就会又有人问了,既然this不是引用function,那么我要实现递归函数,该咋引用呢?这里简单回答下介个问题,两种方法:①函数体内用函数名来引用函数本身②函数体内使用arguments.callee来引用函数(不推荐)。那么既然第二种方法不推荐,匿名函数咋引用呢?用第一种,并且给匿名函数一个函数名即可(推荐)。
  3.2 误解二:this引用的是function的词法作用域

  这种误解欺骗的人可能更多一些。首先,澄清一下,this并没有引用function的词法作用域。的确JS的引擎内对词法作用域的实现的确像是一个对象,拥有属性和函数,但是这仅仅是JS引擎的一种实现,对代码来说是不可见的,也就是说词法作用域“对象”在JS代码中取不到。(关于词法作用域,如果不理解,可以参考之前的一篇博文《聊一下JS中的作用域scope和闭包closure scope和closure》)。看个错误的例子:

1 function fn1() {
2 var a = 2;
3 this.fn2();//以为this引用的是fn1的词法作用域
4 }
5 function fn2() {
6 console.log( this.a );
7 }
8 fn1(); //ReferenceError

  上面的代码明显没有执行出想要的结果,从而可以看到this并没有引用函数的词法作用域。甚至,可以肯定的说,这个例子里fn2可以在fn1里正确执行都是偶然的(理解了词法作用域你就知道为什么这里执行不报错了)。


  4、this到底跟啥有关?

  好了,扯了那么多都没上干货,有的观众都开始关闭当前页开始离席了。这里,我们郑重声明:this跟函数在哪里定义没有半毛钱关系,函数在哪里调用才决定了this到底引用的是啥。也就是说this跟函数的定义没关系,跟函数的执行有大大的关系。所以,记住,“函数在哪里调用才决定了this到底引用的是啥”。


  5、this机制的四种规则

  this到底绑定或者引用的是哪个对象环境决定于函数被调用的地方。而函数的调用有不同的方式,在不同的方式中调用决定this引用的是哪个对象是由四种规则确定的。我们一个个来看。

  5.1 默认绑定全局变量

  这条规则是最常见的,也是默认的。当函数被单独定义和调用的时候,应用的规则就是绑定全局变量。如下: 

1 function fn() {
2 console.log( this.a );
3 }
4 var a = 2;
5 fn(); // 2 -- fn单独调用,this引用window

  5.2 隐式绑定

  隐式调用的意思是,函数调用时拥有一个上下文对象,就好像这个函数是属于该对象的一样。例如:

1 function fn() {
2 console.log( this.a );
3 }
4 var obj = {
5 a: 2,
6 fn: fn
7 };
8 obj.fn(); // 2 -- this引用obj。

  需要说明的一点是,最后一个调用该函数的对象是传到函数的上下文对象(绕懵了)。如:

 1 function fn() {
2 console.log( this.a );
3 }
4 var obj2 = {
5 a: 42,
6 fn: fn
7 };
8 var obj1 = {
9 a: 2,
10 obj2: obj2
11 };
12 obj1.obj2.fn(); // 42 -- this引用的是obj2.

  还有一点要说明的是,失去隐式绑定的情况,如下:

 1 function fn() {
2 console.log( this.a );
3 }
4 var obj = {
5 a: 2,
6 fn: fn
7 };
8 var bar = obj.fn; // 函数引用传递
9 var a = "全局"; // 定义全局变量
10 bar(); // "全局"

  如上,第8行虽然有隐式绑定,但是它执行的效果明显是把fn赋给bar。这样bar执行的时候,依然是默认绑定全局变量,所以输出结果如上。

 5.3 显示绑定

  学过bind()\apply()\call()函数的都应该知道,它接收的第一个参数即是上下文对象并将其赋给this。看下面的例子:

1 function fn() {
2 console.log( this.a );
3 }
4 var obj = {
5 a: 2
6 };
7 fn.call( obj ); // 2

  如果我们传递第一个值为简单值,那么后台会自动转换为对应的封装对象。如果传递为null,那么结果就是在绑定默认全局变量,如:

1 function fn() {
2 console.log( this.a );
3 }
4 var obj = {
5 a: 2
6 };
7 var a = 10;
8 fn.call( null); // 10

  5.4 new新对象绑定

  如果是一个构造函数,那么用new来调用,那么绑定的将是新创建的对象。如:

1 function fn(a) {
2 this.a = a;
3 }
4 var bar = new fn( 2 );
5 console.log( bar.a );// 2

  注意,一般构造函数名首字母大写,这里没有大写的原因是想提醒读者,构造函数也是一般的函数而已。

JS中的this机制的更多相关文章

  1. 别再为了this发愁了------JS中的this机制

    别再为了this发愁了------JS中的this机制 题记:JavaScript中有很多令人困惑的地方,或者叫做机制.但是,就是这些东西让JavaScript显得那么美好而与众不同.比方说函数也是对 ...

  2. 再次讲解js中的回收机制是怎么一回事。

    在前几天的一篇闭包文章中我们简单的介绍了一下闭包,但是并没有深入的讲解,因为闭包涉及的知识点比较多,为了能够更好的理解闭包,今天讲解一下关于js中的回收机制. 在初识闭包一文中我说过js中有回收机制这 ...

  3. 关于js中的回收机制,通俗版

    在前面的几篇文章中,我讲解过了js中的回收机制,但是对于当时的我来说,我自己对回收机制的这个概念也有些懵懵懂懂,现在对回收机制有了更深入的理解,所以特此发布此文给于总结,也好加深记忆. 如果你想学习闭 ...

  4. 次讲解js中的回收机制是怎么一回事。

    在前几天的一篇闭包文章中我们简单的介绍了一下闭包,但是并没有深入的讲解,因为闭包涉及的知识点比较多,为了能够更好的理解闭包,今天讲解一下关于js中的回收机制. 在初识闭包一文中我说过js中有回收机制这 ...

  5. [转] js中的钩子机制(hook)

    什么是钩子机制?使用钩子机制有什么好处? 钩子机制也叫hook机制,或者你可以把它理解成一种匹配机制,就是我们在代码中设置一些钩子,然后程序执行时自动去匹配这些钩子:这样做的好处就是提高了程序的执行效 ...

  6. 别再为了this发愁了:JS中的this机制

    题记:JavaScript中有很多令人困惑的地方,或者叫做机制.但是,就是这些东西让JavaScript显得那么美好而与众不同.比方说函数也是对象.闭包.原型链继承等等,而这其中就包括颇让人费解的th ...

  7. JS中的存储机制

    一.堆和栈的介绍 1.堆和队,是先进先出:栈,是先进后出,就跟水桶差不多: 2.存储速度:堆和队的存储速度较慢,栈的存储速度较快,会自动释放: 二.js中存储的类型 1.堆,一般用于复杂数据类型,存储 ...

  8. JS中的执行机制(setTimeout、setInterval、promise、宏任务、微任务)

    1.执行机制 JS 是单线程的,处理 JS 任务(程序)只能一个一个顺序执行,所以 JS 中就把任务分为了同步任务和异步任务.同步的进入主线程先执行,异步的进入Event Table并注册函数,当指定 ...

  9. Java中的变量传递机制以及JS中的参数传递机制

    JAVA: 传递基本类型是 就是基本的值传递 不会影响值本身. package com.wuqi.p1; public class ValuePassTest { public static void ...

随机推荐

  1. Python 实现图片上表格的写入

    直接上代码:import matplotlib.pylab as pltimport numpy as npplt.figure()axes=plt.gca()y= np.random.randn(9 ...

  2. CSS padding 属性

    定义和用法 padding 简写属性在一个声明中设置所有内边距属性. 说明 这个简写属性设置元素所有内边距的宽度,或者设置各边上内边距的宽度.行内非替换元素上设置的内边距不会影响行高计算:因此,如果一 ...

  3. 045——VUE中组件之父组件使用scope定义子组件模板结构

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  4. docker把web jar包制作成镜像

    1.新建一个spring boot项目 并使用maven打成jar包,放到linux(centos7)环境上. 运行 java -jar hello.jar  后结果如下(这里项目对外提供的端口是90 ...

  5. java基础---->Zip压缩的使用

    java中提供了对压缩格式的数据流的读写.它们封装到现成的IO 类中,以提供压缩功能.下面我们开始java中压缩文件的使用. 目录导航: 关于压缩的简要说明 GZIP压缩文件的使用 ZIP压缩文件的使 ...

  6. linux查看端口对应的程序及pid

    linux中查看特定端口对应的进程以及进程的pid可以使用下面指令: lsof -i:port_number 杀死进程的指令是: kill -s 9 pid

  7. git 错误 fatal: loose object...is corrupt

    错误描述: error: object file .git/objects/9a/83e9c5b3d697d12a2e315e1777ceaf27ea1bab is empty fatal: loos ...

  8. html5实现本页面元素拖放和本地文件拖放

    HTML5拖放 拖放本地数据   1.HTML拖放 拖放(Drag 和 Drop)是HTML5标准的组成部分 2.拖放开始: ondragStart:调用了一个函数,drag(event),它规定了被 ...

  9. js设计模式整理

    单例模式 恶汉式单例 实例化时 懒汉式单例 调用时构造函数模式 1.实现一 function Car(model, year, miles) { this.model = model; this.ye ...

  10. Java 反射机制介绍

    参考文章:http://www.cnblogs.com/skywang12345/p/3345205.html Java 反射机制.通俗来讲呢,就是在运行状态中,我们可以根据“类的部分已经的信息”来还 ...