JavaScript中的百变大咖~this
原文链接:http://www.jeffjade.com/2015/08/03/2015-08-03-javascript-this/
JavaScript作为一种脚本语言身份的存在,因此被很多人认为是简单易学的。然而情况恰恰相反,JavaScript支持函数式编程、闭包、基于原型的继承等高级功能。由于其运行期绑定的特性,JavaScript 中的 this 含义要丰富得多,它可以是全局对象、当前对象或者任意对象,这完全取决于函数的调用方式。JavaScript中函数的调用有以下几种方式:作为对象方法调用,作为函数调用,作为构造函数调用,和使用 apply 或 call 调用。本文就采撷些例子以浅显说明在不同调用方式下的不同含义。
全局的this
全局this一般指向全局对象,浏览器中的全局对象就是 window。例如:
console.log(this.document === document); //true
console.log(this === window); //true
this.a = 91;
console.log(window.a); //91
一般函数的 this
function f1 () {
    return this;
}
console.log(f1() === window);//true, global object
可以看到一般函数的 this 也指向 window,在 nodeJS 中为 global object
function f2 () {
    "use strict";//使用严格模式
    return this;
}
console.log(f1() === undefined);//true
严格模式中,函数的 this 为 undefined
作为对象方法的函数的 this
var o = {
    prop: 37,
    f: function() {
        return this.prop;
    }
};
console.log(o.f()); // 37
上述代码通过字面量创建对象 o。
f 为对象 o 的方法。这个方法的 this 指向这个对象,在这里即对象 o。
var o = {
    prop: 37
};
function independent() {
    return this.prop;
}
o.f = independent;
console.log(o.f()); // 37
上面的代码,创建了对象 o,但是没有给对象 o,添加方法。而是通过 o.f = independent 临时添加了方法属性。这样这个方法中的 this 同样也指向这个对象 o。
作为函数调用
函数也可以直接被调用,此时 this 绑定到全局对象。在浏览器中,window 就是该全局对象。比如下面的例子:函数被调用时,this被绑定到全局对象,接下来执行赋值语句,相当于隐式的声明了一个全局变量,这显然不是调用者希望的。
function makeNoSense(x) {
    this.x = x;
}
makeNoSense(5);
x;// x 已经成为一个值为 5 的全局变量
对于内部函数,即声明在另外一个函数体内的函数,这种绑定到全局对象的方式会产生另外一个问题。以下面moveTo方法为例,内定义两个函数,分别将 x,y 坐标进行平移。结果可能出乎大家意料,不仅 point 对象没有移动,反而多出两个全局变量 x,y。
var point = {
    x : 0,
    y : 0,
    moveTo : function(x, y) {
        // 内部函数
        var moveX = function(x) {
            this.x = x;//this 绑定到了哪里?
        };
        // 内部函数
        var moveY = function(y) {
            this.y = y;//this 绑定到了哪里?
        }; 
        moveX(x);
        moveY(y);
    }
};
point.moveTo(1, 1);
console.log(point.x) //0
console.log(point.x) //0
console.log(x)       //1
console.log(y)       //1
这属于 JavaScript 的设计缺陷,正确的设计方式是内部函数的this应该绑定到其外层函数对应的对象上,为了规避这一设计缺陷,聪明的JavaScript程序员想出了变量替代的方法,约定俗成,该变量一般被命名为 that。
对象原型链上的this
var o = {
    f: function() {
        return this.a + this.b;
    }
};
var p = Object.create(o);
p.a = 1;
p.b = 2;
console.log(p.f()); //3
通过 var p = Object.create(o) 创建的对象,p 是基于原型 o 创建出的对象。
p 的原型是 o,调用 f() 的时候是调用了 o 上的方法 f(),这里面的 this 是可以指向当前对象的,即对象 p。
get/set 方法与 this
function modulus() {
    return Math.sqrt(this.re * this.re + this.im * this.im);
}
var o = {
    re: 1,
    im: -1,
    get phase() {
        return Math.atan2(this.im, this.re);
    }
};
Object.defineProperty(o, 'modulus', {
    get: modulus,
    enumerable: true,
    configurable: true
});
console.log(o.phase, o.modulus); // -0.78 1.4142
get/set 方法中的 this 也会指向 get/set 方法所在的对象的。
构造器中的 this
function MyClass() {
    this.a = 25;
}
var o = new MyClass();
console.log(o.a); //25
new MyClass() 的时候,MyClass()中的 this 会指向一个空对象,这个对象的原型会指向 MyClass.prototype。MyClass()没有返回值或者返回为基本类型时,默认将 this 返回。
function C2() {
    this.a = 26;
    return {
        a: 24
    };
}
o = new C2();
console.log(o.a); //24
因为返回了对象,将这个对象作为返回值
call/apply 方法与 this
function add(c, d) {
    return this.a + this.b + c + d;
}
var o = {
    a: 1,
    b: 3
};
add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16
add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34
function bar() {
    console.log(Object.prototype.toString.call(this));
}
bar.call(7); // "[object Number]"
bar.call(); //[object global]
bar.call("7");//[object String]
bar.call(true);//[object Boolean]
console.log(add.call(o,5,7));//16
bind 方法与 this
function f() {
    return this.a;
}
var g = f.bind({
    a: "test"
});
console.log(g()); // test
var o = {
    a: 37,
    f: f,
    g: g
};
console.log(o.f(), o.g()); // 37, test
绑定之后再调用时,仍然会按绑定时的内容走,所以 o.g() 结果是 test
JavaScript中this的些许看似怪异现象
<body>
    <!--JavaScript伪协议和内联事件对于this的指向不同-->
    <a href="#" onclick="alert(this.tagName);">click me</a> <!--弹出A-->
    <a href="javascript:alert(this.tagName);">click me</a>  <!--弹出undefined-->
    <a href="javascript:alert(this==window);">click me</a>  <!--弹出true-->
    <input id="btn" type="button" value="this demo" name="button"/>
</body>
var name = 'somebody';
var angela = {
    name: 'angela',
    say: function () {
        alert("I'm " + this.name);
    }
};
var btn = document.getElementById('btn');
setTimeout和setInterval也会改变this的指向
angela.say();//I'm  angela
setTimeout(angela.say, 1000);  //I'm  somebody
setInterval(angela.say, 1000); //I'm  somebody
on...也会改变this的指向
angela.say(); //I'm  angela
btn.onclick = angela.say; //I'm  button
click等回调也会改变this指向
$("#btn").click = angela.say;  // I'm  button
$("#btn").click(angela.say);   // I'm  button
如果在say中用了this,this会绑定在angela上么?显然这里不是,赋值以后,函数是在回调中执行的,this会绑定到$(“#btn”)元素上。这个函数被完整复制到onclick属性(现在成为了函数)。因此如果这个even thandler被执行,this将指向HTML元素;因此,结果显示的是"I'm button"。而,匿名函数可以调整this指向,EG:
$("#btn").click(function(){
    angela.say();  //I'm  angela
});
匿名函数调整this指向比如:
    setTimeout(function () { angela.say(); }, 1000); //I'm  angela
    setInterval(function () { angela.say(); }, 1000) //I'm  angela
    btn.onclick = function () { angela.say(); };     //I'm  angela
    setTimeout(function () { alert(this == window); }, 1000);//true
    btn.onclick = function () { alert(this == btn); }//true
匿名函数赋值给了click属性(好吧,现在成了函数),此时这个匿名函数指向的即是Html属性。因此所调用的函数(比如angela.say())this上下文没有被更改,所以其打印出来的结果就是'I'm angela'。事实上,也用这样的方法来消解this在回调函数中不堪使用的'特色'。
$("#btn").click(function(){
    if(window == this){
        alert("window == this");
    }else{
        alert("window != this")  //弹出来
    }
    alert(this.name); // button
    angela.say();    //I'm  angela
});
将this指向的对象保存到变量(一般用that)
    var mydemo = {
        name: 'angela',
        say: function () { alert("I'm " + this.name); },
        init: function () {
            var that = this;
            document.getElementById('btn').onclick = function () {
                that.say();  //弹出Alert:I'm angela
                this.say();  //这儿报错: undefined is not a function (evaluating 'this.say()')
            }
        }
    };
    mydemo.init();
Javascript中的eval 方法
JavaScript 中的 eval 方法可以将字符串转换为 JavaScript 代码,使用 eval 方法时,this 指向哪里呢?答案很简单,看谁在调用 eval 方法,调用者的执行环境(ExecutionContext)中的 this 就被 eval 方法继承下来了。(悪,还没用过,有待实践下)
后记:由于javascript的动态性(解释执行,当然也有简单的预编译过程),this的指向在运行时才确定,因此在只要足够留心其运行时的上下文,即可无痛挥霍this的强大。
参考AJavaScript 中的 this
参考BJavaScript中this的一些怪异现象
参考CJavascript的this用法-阮一峰
参考D深入浅出 JavaScript 中的 this
JavaScript中的百变大咖~this的更多相关文章
- Java工程师:四个月小白变大咖,你能做到吗?
		你眼中的Java工程师是什么样子? 技术大牛?闷骚男?IT民工?没有女朋友?全是汉子?很邋遢?贼眉鼠眼? 今天,中软国际卓越工程师,Java精英班正式开课啦.你想看看他们都是一群怎样的人吗? 今天的武 ... 
- Javascript中,实现十大排序方法之一(冒泡排序及其优化设想)
		冒泡排序的Javascript实现 首先定义一个取值范围在(0~100000)之间的随机值的长度为10万的数组, function bubbleSort(arr) { console.time('冒泡 ... 
- <a>中的背景色变大
		想要调整文字链接背景颜色或图片的大小可以用padding属性: 但火狐和IE数值相同显示相同,但与360数值相同显示不同(上下宽度会变小.) 
- IDEA 在使用的过程中字符间距变大的问题
		解决办法:shift+空格半角全角快捷键 
- C#保留2位小数几种场景总结    游标遍历所有数据库循环执行修改数据库的sql命令    原生js轮盘抽奖实例分析(幸运大转盘抽奖)  javascript中的typeof和类型判断
		C#保留2位小数几种场景总结 场景1: C#保留2位小数,.ToString("f2")确实可以,但是如果这个数字本来就小数点后面三位比如1.253,那么转化之后就会变成1.2 ... 
- [转载]JavaScript 中小数和大整数的精度丢失
		标题: JavaScript 中小数和大整数的精度丢失作者: Demon链接: http://demon.tw/copy-paste/javascript-precision.html版权: 本博客的 ... 
- 【JavaScript】JavaScript中的陷阱大集合
		本文主要介绍怪异的Javascript,毋庸置疑,它绝对有怪异的一面.当软件开发者开始使用世界上使用最广泛的语言编写代码时,他们会在这个过 程中发现很多有趣的“特性”.即便是老练的Javascript ... 
- iframe ios中h5页面 样式变大
		实际项目开发中,iframe在移动设备中使用问题还是很大的,说一说我的那些iframe坑 做过的这个后台管理框架,最开始的需求是PC,但随着业务需要,需要将项目兼容到ipad,后台的框架也是使用的开源 ... 
- word中插入myth type公式行距变大的问题
		在写文章时,我遇到了在word中插入myth type公式时,行距明显变大的问题,我通过改变段落中的行距没有解决问题,在网上查了一下,找到一些解决方法,仅供参考. 解决办法 
随机推荐
- C++-文件【1】-按行读文本文件的两种方法
			测试环境—— 系统:Win7 64bit 编译器:TDM-GCC 4.9.2 64-bit Release #include <iostream> #include <fstream ... 
- HDU5838 Mountain(状压DP + 容斥原理)
			题目 Source http://acm.hdu.edu.cn/showproblem.php?pid=5838 Description Zhu found a map which is a N∗M ... 
- php安装程序
			php安装程序 制作原理和步骤 检查目录或文件权限 修改或者添加配置文件 检查配置文件的正确性 导入数据库 锁定或删除安装文件 用到函数 iswritable("data/config.ph ... 
- Exchange管理界面
			Exchange有三种管理方式,一个是通过Powershell命令行操作进行管理.Exchange自带的Powershell是在其基础上建的,加入了Exchange管理操作的函数,能够进行另外两种方式 ... 
- linux工具
			sudo yum install yum-utils 
- github for windows 安装 使用
			遇到无数的未知问题.光是安装就搞了好久. 安装程序显示安装了.NET Framework4.5,然后提示重启.重启后,自动开始下载文件,最多到2%就走不动了. 后来请求了下面这个链接,才开始下载了(虽 ... 
- 2015-12-21(box-sizing:border-box)
			最近新学了一个方法box-sizing:border-box,可以忽略margin,padding,border等所要占的位置,比如,你在做响应式网页时,当你所做的网页宽度是符合当前电脑屏幕宽度时,但 ... 
- AmazeUI 框架知识点-布局和样式整理
			1.Amaze UI 将所有元素的盒模型设置为 border-box.这下好了,妈妈再也不用担心没计算好 padding.border 而使布局破相了. 2.Amaze UI 将浏览器的基准字号设置为 ... 
- “LC.exe”错误
			错误“LC.exe”已退出,代码为 -1. 可能的原因是: 这个第三方组件是个商业组件,他在组件的主使用类定义了 LicenseProvider(typeof(LicFileLicenseProvid ... 
- DSY2933*地图
			Description 一个人口统计办公室要绘制一张地图.由于技术的原因只能使用少量的颜色.两个有相同或相近人口的区域在地图应用相同的颜色.例如一种颜色k,则A(k) 是相应的数,则有: 在用颜色 ... 
