Js中this机制全解
JavaScript中有很多令人困惑的地方,或者叫做机制。
但是,就是这些东西让JavaScript显得那么美好而与众不同。
比方说函数也是对 象、闭包、原型链继承等等,而这其中就包括颇让人费解的this机制。
不管是新手还是老手,不仔细深抠一下还真闹不明白this倒地咋回事捏。
今天,我们 就一起看一下this倒地咋回事,别再为了this发愁了。
this是啥?
        简言之,this是JavaScript语言中定义的众多关键字之一,它的特殊在于它自动定义于每一个函数域内,但是this倒地指引啥东西却让很多人张二摸不着头脑。希望看完这篇文章了你能回答出来this到底指引个甚。
this有啥用?
有人肯定会问,既然this这么难以理解,那么为个甚还要用它呢?我们来看个例子:
function identify() {
        return this.name.toUpperCase();
}
function sayHello() {
        var greeting = "Hello, I'm " + identify.call( this );
        console.log( greeting );
}
var person1= {
        name: "Kyle"
    };
var person2= {
        name: "Reader"
};
identify.call( person1); // KYLE
identify.call( person2); // READER
sayHello.call( person1); // Hello, I'm KYLE
sayHello.call( person2); // Hello, I'm READER
这段代码很简单,我们定义了两个函数,分别为identify和sayHello。并且在不同的对象环境下执行了它们,达到了复用的效果,而不用为了在不 同的对象环境下执行而必须针对不同的对象环境写对应的函数了。
简言之,this给函数带来了复用。有人就会说,我不用this一样可以实现,如:
function identify(context) {
        return context.name.toUpperCase();
}
function sayHello(context) {
       var greeting = "Hello, I'm " + identify( context);
       console.log( greeting );
}
var person1= {
        name: "Kyle"
};
var person2= {
        name: "Reader"
};
identify( person1); // KYLE
identify( person2); // READER
sayHello( person1); // Hello, I'm KYLE
sayHello( person2); // Hello, I'm READER
仔细一看,这位客官给出的解决方法的确也达到了类似的效果。赞一个!我想说的是,随着代码的增加,函数嵌套、各级调用等变得越来越复杂,那么传递一个对象 的引用将变得越来越不明智,它会把你的代码弄得非常乱,甚至你自己都无法理解清楚。
而this机制提供了一个更加优雅而灵便的方案,传递一个隐式的对象引 用让代码变得更加简洁和复用。
关于this的误解
相信很多童鞋是学过其它语言的,在很多编程语言中都有this的机制,惯性思维把其它语言里对它的理解带到了JavaScript中。同时,由于this这个单词的理解导致了我们产生了对它各种各样的误解。所以,开始前,我们先澄清下对它的误解。
误解一:this引用function本身
我们都知道,在函数里引用函数可以达到递归和给函数属性赋值的效果。而这在很多应用场景下显得非常有用。所以,很多人都误以为this就是指引function本身。例如:
function fn(num) {
        console.log( "fn: " + num );
        // count用于记录fn的被调用次数
        this.count++;
}
fn.count = 0;
var i;
for (i=0; i<10; i++) {
        if (i > 5) {
            fn( i );
        }
}
// fn: 6
// fn: 7
// fn: 8
// fn: 9
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来引用函数(不推荐)。
那么既然第二种方法不推荐,匿名函数咋引用呢?用第一种,并且
给匿名函数一个函数名即可(推荐)。
误解二:this引用的是function的词法作用域
这种误解欺骗的人可能更多一些。首先,澄清一下,this并没有引用function的词法作用域。的确JS的引擎内对词法作用域的实现的确像是一个对象,拥有属性和函数,但是这仅仅是JS引擎的一种实现,对代码来说是不可见的,也就是说词法作用域“对象”在JS代码中取不到。看个错误的例子:
function fn1() {
     var a = 2;
     this.fn2();//以为this引用的是fn1的词法作用域
}
function fn2() {
         console.log( this.a );
}
fn1(); //undefined
上面的代码明显没有执行出想要的结果,从而可以看到this并没有引用函数的词法作用域。甚至,可以肯定的说,这个例子里fn2可以在fn1里正确执行都是偶然的(理解了词法作用域你就知道为什么这里执行不报错了)
this到底跟啥有关?
好了,扯了那么多都没上干货,有的观众都开始关闭当前页开始离席了。这里,我们郑重声明:this跟函数在哪里定义没有半毛钱关系,函数在哪里调用才 决定了this到底引用的是啥。也就是说this跟函数的定义没关系,跟函数的执行有大大的关系。所以,记住,“函数在哪里调用才决定了this到底引用 的是啥”。
this机制的四种规则
this到底绑定或者引用的是哪个对象环境决定于函数被调用的地方。而函数的调用有不同的方式,在不同的方式中调用决定this引用的是哪个对象是由四种规则确定的。我们一个个来看。
默认绑定全局变量
这条规则是最常见的,也是默认的。当函数被单独定义和调用的时候,应用的规则就是绑定全局变量。如下:
function fn() {
         console.log( this.a );
}
var a = 2;
fn(); // 2 -- fn单独调用,this引用window
隐式绑定
隐式调用的意思是,函数调用时拥有一个上下文对象,就好像这个函数是属于该对象的一样。例如:
function fn() {
     console.log( this.a );
}
var obj = {
     a: 2,
     fn: fn
};
obj.fn(); // 2 -- this引用obj。
需要说明的一点是,最后一个调用该函数的对象是传到函数的上下文对象(绕懵了)。如:
function fn() {
  console.log( this.a );
}
var obj2 = {
      a: 42,
      fn: fn
};
var obj1 = {
      a: 2,
     obj2: obj2
};
obj1.obj2.fn(); // 42 -- this引用的是obj2.
还有一点要说明的是,失去隐式绑定的情况,如下:
function fn() {
     console.log( this.a );
}
var obj = {
      a: 2,
      fn: fn
};
var bar = obj.fn; // 函数引用传递
var a = "全局"; // 定义全局变量
bar(); // "全局"
如上,第8行虽然有隐式绑定,但是它执行的效果明显是把fn赋给bar。这样bar执行的时候,依然是默认绑定全局变量,所以输出结果如上。
显示绑定
学过bind()\apply()\call()函数的都应该知道,它接收的第一个参数即是上下文对象并将其赋给this。看下面的例子:
function fn() {
     console.log( this.a );
}
var obj = {
     a: 2
};
fn.call( obj ); //
如果我们传递第一个值为简单值,那么后台会自动转换为对应的封装对象。如果传递为null,那么结果就是在绑定默认全局变量,如:
function fn() {
      console.log( this.a );
}
var obj = {
      a: 2
};
var a = 10;
fn.call( null); //
new新对象绑定
如果是一个构造函数,那么用new来调用,那么绑定的将是新创建的对象。如:
function fn(a) {
     this.a = a;
}
var bar = new fn( 2 );
console.log( bar.a );//
注意,一般构造函数名首字母大写,这里没有大写的原因是想提醒读者,构造函数也是一般的函数而已。
Js中this机制全解的更多相关文章
- 彻底搞懂 JS 中 this 机制
		彻底搞懂 JS 中 this 机制 摘要:本文属于原创,欢迎转载,转载请保留出处:https://github.com/jasonGeng88/blog 目录 this 是什么 this 的四种绑定规 ... 
- Bom和Dom编程以及js中prototype的详解
		一.Bom编程: 1.事件练习: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "h ... 
- JS中表格的全选和删除要注意的问题
		在项目开发中,由于刚刚开始做项目,我对js还不是很精通,所以在用js对表格的全选和删除中遇到了不少问题,后来通过查找资料解决了,之后总结了一下关于js表格的全选和删除出现的一些问题,希望能帮助到大家. ... 
- js面试题知识点全解(一变量类型和计算)
		1.js中使用typeof能得到哪些类型 2.何时使用===和== 3.js中的内置函数 4.js变量按存储方式区分为哪些类型,并描述其特点 5.如何理解json 以下对这些问题的知识点做一些总结: ... 
- JavaScript面向对象(一)——JS OOP基础与JS 中This指向详解
		前 言 JRedu 学过程序语言的都知道,我们的程序语言进化是从"面向机器".到"面向过程".再到"面向对象"一步步的发展而来.类似于 ... 
- js中Date()对象详解
		var myDate = new Date(); myDate.getYear(); //获取当前年份(2位) myDate.getFullYear(); //获取完整的年份(4位,1970-???? ... 
- js中的逻辑运算符详解(||、&&、!)
		视频地址:https://www.bilibili.com/video/BV1Y7411K7zz?p=1 一直以来都没弄清楚js中的逻辑运算符是怎么回事 , 一直都以为他们的用法和java一样 , 今 ... 
- Java中反射机制详解
		序言 在学习java基础时,由于学的不扎实,讲的实用性不强,就觉得没用,很多重要的知识就那样一笔带过了,像这个马上要讲的反射机制一样,当时学的时候就忽略了,到后来学习的知识中,很多东西动不动就用反射, ... 
- JS中this关键字详解
		本文主要解释在JS里面this关键字的指向问题(在浏览器环境下). 阅读此文章,还需要心平气和的阅读完,相信一定会有所收获,我也会不定期的发布,分享一些文章,共同学习 首先,必须搞清楚在JS里面,函数 ... 
随机推荐
- mooc linux学习总结
			通过八周的学习获得了很多知识. 首先,通过网课老师形象生动的讲述和描述一些专业词汇,使我更加深刻的记住并掌握了这些内容:动态的展示堆栈的变化,更容易理解一段汇编代码:分析操作系统的工作,记 ... 
- nginx+tpmcat+redis实现session共享
			nginx+tpmcat+redis实现session共享 版本:nginx nginx-1.8.0.tar.gztomcat apache-tomcat-7.0.78.tar.gzredis re ... 
- springboot整合fastJson遇到重定向问题
			通过网上教程使用springboot整合fastJson后遇到页面重定向问题(使用的springboot版本是2.0.2.RELEASE ,其他的版本可能不会出现以下问题),如下图: 我的项目结构如下 ... 
- wuziqi
			五子棋结对人崔保雪的博客连接http://www.cnblogs.com/nuoxiaomi/ 题目简介 我们实现了一个五子棋的软件,该软件由初始化模块.下棋操作模块.人机对战模块.人人对 ... 
- git工具
			1.Git Bash常用命令: pwd 当前工作目录 clear 清屏 ls 列举当前目录下的文件及文件夹 cd 更改目录 mkdir 创建目录 touch 创建空文件 cp 拷 ... 
- “数学口袋精灵”第二个Sprint计划(第六~八天)
			“数学口袋精灵”第二个Sprint计划----第六天~第八天进度 任务分配: 冯美欣:欢迎界面的背景音乐完善 吴舒婷:游戏界面的动作条,选择答案后的音效 林欢雯:代码算法设计 第六天: 进度: 冯美欣 ... 
- JavaScript中给onclick绑定事件后return false遇到的问题
			<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ... 
- 使用YII框架的migrate迁移数据库
			框架版本:2.0.13 官网手册说明:http://www.yiichina.com/doc/guide/2.0/db-migrations 创建迁移 命令的格式: php yii migrate/c ... 
- [Wiki].NET框架
			.NET框架 建议将.NET Framework 3.0并入本条目或章节.(讨论) .NET框架 .NET框架的组件堆栈 开发者 Microsoft 初始版本 2002年2月13日,16年前 稳定 ... 
- sort和uniq的应用实例
			sort 排序 uniq 1.语法:sort [option]... [file]... 2.选项:-k key,关键子,指定以那个列来排序.如果不指定,默认将正行作为关键字排序-n 对数值排序.默认 ... 
