如果问初学者js中什么东西比较难懂,很多回答会是this关键字。this在不同场景下所指向的对象不同,这就有一种扑朔迷离的感觉,放佛魔法一般神秘:this到底是什么?这里有四种绑定规则。

1. 默认绑定

默认绑定是无法应用其他调用规则时的绑定方式,看如下代码:

 var a = 1;

 function foo(){
console.log(this.a);
} foo(); //
 "use strict"; 

 var a = 1;

 function foo(){
console.log(this.a);
} foo(); // TypeError: Cannot read property 'a' of undefined

这是最基本的一个函数调用,在第一张图中,非严格模式下,this绑定到全局对象,因此this.a指向全局变量a。第二张图中,严格模式下,全局对象无法使用默认绑定,因此this会绑定到undefined。

那么如何判断是默认绑定呢,其实很简单,我们观察foo的调用位置,这里foo是直接被调用的,foo没有被引用到任何其他对象或着被显式绑定到指定对象(显式绑定稍候会说明),因此只能使用默认绑定规则。

 2. 隐式绑定

隐式绑定需要考虑调用位置是否有上下文对象,或者说是够被某个对象包含或拥有,比如以下代码:

 function foo(){
console.log(this.a);
} var obj = {
a: 1,
foo: foo
}; obj.foo(); //

在声明obj时,包含了foo,因此调用obj.foo()时,this绑定到obj,this.a就是obj.a。如果有多个层级的包含关系,this会绑定到最后一层的上下文对象上,比如以下代码:

 function foo(){
console.log(this.a);
} var obj2 = {
a: 2,
foo: foo
}; var obj1 = {
a: 1,
obj2: obj2
}; obj1.obj2.foo(); //

此时this会绑定到obj2上。

还有一种情况,叫隐式绑定丢失,看如下代码:

 function foo(){
console.log(this.a);
} var obj = {
a: 1,
foo: foo
}; var a = 'global'; var bar = obj.foo; bar(); // 严格模式下是undefined,非严格模式下是global

这里指定了bar为obj.foo的一个别名(或者说引用),但是很重要的是,这里bar实际上引用的是foo本身,所以这里调用bar()相当于一个默认绑定,适用于上面讲到的默认绑定规则。如果确实需要函数别名并且把this绑定到指定的对象上,可以使用显式绑定,比如bind、call、apply之类的,后面会陆续谈到。

隐式绑定丢失还会出现在传入回调函数的时候:

 function foo(){
console.log(this.a);
} function caller(func){
func();
} var obj = {
a: 1,
foo: foo
}; var a = 'global'; caller(obj.foo); // 严格模式下是undefined,非严格模式下是global

在前端的js编程中,由于是事件驱动的,调用回调函数经常发生在用户交互之后,由于绑定丢失,我们经常需要手动把this绑定到某个对象上。

3. 显式绑定

如果我们想在某个对象上强制调用函数,可以是用显式绑定。js中的函数的原型是Function对象,它提供了一些通用方法。就显式绑定来说,我们可以使用apply和call这两个方法,具体用法是:

 func.apply(obj, [arg1, arg2,...]);
func.call(obj, arg1, arg2,...);

先不用纠结后面参数的格式(其实apply和call只是在传参格式上不一样而已),apply和call都是将func的this绑定到第一个参数obj上。看以下代码:

 function foo(){
console.log(this.a);
} var obj = {
a: 1
}; var a = 'global'; foo.call(obj); //

此时foo的this绑定到了obj上面。

apply和call的第一个参数也可以是null,即不绑定到任何对象,但实际上这样会绑定到全局对象:

 function foo(){
console.log(this.a);
}
var obj = {
a: 1
};
var a = 'global';
foo.call(null); // 严格模式下是undefined,非严格模式下是global

虽然apply和call可以把this绑定到指定对象,但是还是没有解决回调函数的问题,因为apply和call都是在当下立刻执行,而回调函数的执行时间是不确定的。而且回调函数的上下文也是不确定的,在回调函数的上下文中可能很难获得我们想要的那个this绑定对象。

为了解决回调函数绑定丢失的问题,我们可以使用硬绑定bind。bind很有用,它可以对this强制绑定一个对象,而且绑定后无法修改。这对我们事件驱动的编程模型很有帮助,可以大量运用在回调函数中。另外bind在js的函数式编程中也是一项利器。看以下代码:

 function foo(){
console.log(this.a);
}
var obj1 = {
a: 1
};
var obj2 = {
a: 2
};
var bar = foo.bind(obj1); // bar的this永远只会指向obj1
bar(); //
bar.call(obj2); // 1 因为无法改变bind后的this绑定,所以还是1

4. new绑定

new绑定是使用new操作符对函数进行调用产生的绑定。js中的new和其他面向对象编程语言的new不同。一般的OOP中new操作符会调用类的构造函数,生成一个全新的类实例。js中没有类,也没有构造函数,使用new操作符调用函数实际上做了以下4件事情:

(1) 创建一个全新的对象。

(2) 这个新对象会和它的原型对象进行连接。

(3) 这个新对象会被绑定到函数调用的this。

(4) 如果函数没有返回其他对象,那这个new表达式将自动返回这个新对象。

代码如下:

 function foo(a){
this.a = a;
}
var bar = new foo(1);
console.log(bar.a); //

总结

本文介绍了this的四种情况,需要再强调的是,判断this的绑定,不要看函数被定义的地方,而要看函数被调用的地方,或者说上下文。

深入解析JavaScript中的this关键字的更多相关文章

  1. 深入解析Javascript中this关键字的使用

    深入解析Javascript中面向对象编程中的this关键字 在Javascript中this关键字代表函数运行时,自动生成的一个内部对象,只能在函数内部使用.比如: function TestFun ...

  2. 转载 深入理解JavaScript中的this关键字

    转载原地址: http://www.cnblogs.com/rainman/archive/2009/05/03/1448392.html 深入理解JavaScript中的this关键字   1. 一 ...

  3. 深度解析javascript中的浅复制和深复制

    原文:深度解析javascript中的浅复制和深复制 在谈javascript的浅复制和深复制之前,我们有必要在来讨论下js的数据类型.我们都知道有Number,Boolean,String,Null ...

  4. JavaScript中的this关键字的用法和注意点

    JavaScript中的this关键字的用法和注意点 一.this关键字的用法 this一般用于指向对象(绑定对象); 01.在普通函数调用中,其内部的this指向全局对象(window); func ...

  5. 全面解析JavaScript中“&&”和“||”操作符(总结篇)

    1.||(逻辑或), 从字面上来说,只有前后都是false的时候才返回false,否则返回true. ? 1 2 3 4 alert(true||false); // true alert(false ...

  6. 如何理解JavaScript中的this关键字

    前言 王福朋老师的 JavaScript原型和闭包系列 文章看了不下三遍了,最为一个初学者,每次看的时候都会有一种 "大彻大悟" 的感觉,而看完之后却总是一脸懵逼.原型与闭包 可以 ...

  7. javaScript中的this关键字解析

    this是JavaScript中的关键字之一,在编写程序的时候经常会用到,正确的理解和使用关键字this尤为重要.接下来,笔者就从作用域的角度粗谈下自己对this关键字的理解,希望能给到大家一些启示, ...

  8. 关于javascript中的this关键字

    this是非常强大的一个关键字,但是如果你不了解它,可能很难正确的使用它. 下面我解释一下如果在事件处理中使用this. 首先我们讨论一下下面这个函数中的this关联到什么. function doS ...

  9. 浅显易懂的理解JavaScript中的this关键字

    在JavaScript中this变量是一个令人难以摸清的关键字,this可谓是非常强大,充分了解this的相关知识有助于我们在编写面向对象的JavaScript程序时能够游刃有余. 1. 一般用处 对 ...

随机推荐

  1. [基础架构]PeopleSoft工作原理(从浏览器发送请求开始)

    PeopleSoft体系结构是由几大组成部分构成,之前文章已经详细讲过,了解这几大组成部分是怎么协同工作的更为重要.在本文中将帮助您了解PeopleSoft的工作原理以及用户发送的请求是如何被解析以及 ...

  2. js实现单张图片(或者多张)的预览功能

      这个是预览单张图片的,如果要预览多张图片,改下面红色标记的地方就好了 <html> <head> <meta http-equiv="Content-Typ ...

  3. Java中的二进制及基本的位运算

    Java中的二进制及基本的位运算 二进制是计算技术中广泛采用的一种数制.二进制数据是用0和1两个数码来表示的数.它的基数为2,进位规则是"逢二进一",借位规则是"借一当二 ...

  4. TCP传输协议使用

    TCP传输协议,也称之为套接字连接,比较安全,三次握手!,必须确保对方计算机存在,才能连接,而且是长时间连接. 缺点是传输速度有点慢. 你用 socket 去连接 ServiceSocaket 服务器 ...

  5. 第一篇:webservice初探

    接触webservice也有一段时间了,为了查缺补漏,把知识点系统化,准备写几篇博文梳理下webservice的知识点,这是第一篇,对webservice进行大致的介绍. 1.什么是webservic ...

  6. 动态rem与1px边框问题的理解

    当我们在项目开发中,拿到设计师的设计图,满怀欣喜的准备按照设计图将页面实现出来的时候,我们通常会遇到这个问题: 如何将页面的内容按照在不同手机屏幕浏览的情况下,比例都是不变的呢?这个时候我们就需要使用 ...

  7. LeetCode-Minimum Path Sum[dp]

    Minimum Path Sum Given a m x n grid filled with non-negative numbers, find a path from top left to b ...

  8. Android学习笔记- Fragment实例 底部导航栏的实现

    1.要实现的效果图以及工程目录结构: 先看看效果图吧: 接着看看我们的工程的目录结构: 2.实现流程: Step 1:写下底部选项的一些资源文件 我们从图上可以看到,我们底部的每一项点击的时候都有不同 ...

  9. Android学习笔记-EditText(输入框)(一)

    1.设置默认提示文本 默认提示文本的两个属性如下: android:hint="默认提示文本" android:textColorHint="#95A1AA" ...

  10. WCF Cookie处理

    wcf操作cookie看似很简单,如果不对Wcf研究一把还是很难的,基本上无从下手. 这里上一图 上面的结果,岂止是坑爹,简直就是坑爹!!! 废话不多说,直接上代码: cookie读取 var req ...