js 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
每个函数都包含两个非继承而来的两个方法apply()和call()
通过call和apply改变this的指向和函数的作用域
//构造函数不需要使用 return语句返回对象,它的返回时自动完成的
function fun(name,sex){
this.name=name;
this.sex=sex;
this.value=function(){
return this.name+"的武器是"+'\n'+this.sex;
}
}
var clert=new fun('孙悟空','金箍棒');
console.log(clert.value())
console.log(clert instanceof fun)
var cevt=new Object();
fun.call(cevt,'爷爷','葫芦1')//对象冒充
fun.apply(cevt,['爷爷','葫芦2'])
console.log(cevt.value())
console.log(cevt.name)
一般函数的 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
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
这是因为setTimeout
等函数内this默认的指向是Window。这一点类似于曾探所写的《JavaScript设计模式与开发实践》中提到的丢失的this
。看下面代码:
var obj ={
myName: 'jeff',
getName: function(){
return this.myName;
}
};
console.log(obj.getName()); //jeff
var getName2 = obj.getName;
console.log( getName2() ); //undefined
//调用getName2时是普通函数方式,this指向全局window,故而结果是undefined;
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
});
这是JavaScript新手们经常犯的一个错误,为了避免这种错误,许多JavaScript框架都提供了手动绑定 this 的方法。比如Dojo就提供了lang.hitch,该方法接受一个对象和函数作为参数,返回一个新函数,执行时this绑定到传入的对象上。使用 Dojo,可以将上面的例子改为:
button.onclick = lang.hitch(angela, angela.say);
其实在我们使用比较多的jQuery也提供了对应的解决方案:jQuery.proxy(function, scope).返回一个新函数,并且这个函数始终保持了特定的作用域。其作用跟Dojo就提供了lang.hitch类似。其中有例如:
<div id='test'>Click Here<\/div>
var obj = {
name: "John",
test: function() {
alert( this.name );
$("#test").unbind("click", obj.test);
}
};
$("#test").click( jQuery.proxy( obj, "test" ) );
//强制设置函数的作用域,让this指向obj而不是#test对象。
// 以下代码跟上面那句是等价的:
// $("#test").click( jQuery.proxy( obj.test, obj ) );
// 可以与单独执行下面这句做个比较。
// $("#test").click( obj.test );
匿名函数调整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();
第三方库or框架中的this
比如,使用backbone框架中events时间回调中的this,其指向的就是对应的视图,而不是Dom元素,因为该回调时通过events哈希绑定的,实质上也是自对应视图那里callback到对应的函数;
Javascript中的eval 方法
JavaScript 中的 eval 方法可以将字符串转换为 JavaScript 代码,使用 eval 方法时,this 指向哪里呢?答案很简单,看谁在调用 eval 方法,调用者的执行环境(ExecutionContext)中的 this 就被 eval 方法继承下来了。(悪,还没用过,有待实践下)!
但是:在严格模式之下,eval的作用域也被改变了。正常模式下,eval语句的作用域,取决于它处于全局作用域,还是处于函数作用域。严格模式下,eval语句本身就是一个作用域,不再能够生成全局变量了,它所生成的变量只能用于eval内部。
"use strict";
var x = 2;
console.info(eval("var x = 5; x")); // 5
console.info(x); // 2
js this 指向的更多相关文章
- 白话js this指向问题
前言 通过本文,你大概能了解this基础指向的问题,抛开例子去说this太虚幻,这里还是结合几篇博文做个整理,算是个人的记录了. 先说概念,this指向与申明无关,永远指向距离自己最近的最终调用者 ...
- 彻底搞懂js this指向问题
在这里必须要提一句的是,this指向是学习js必须要掌握的(必须),再开始之前先看底部的总结,然后回上面看例子便一目了然. 例子1: function a(){ var user = "Ta ...
- JS this指向
正常模式 在正常模式下独立函数的的 this 指向 undefined 或 window. <script type="text/javascript"> functi ...
- JS this指向问题
<button onclick=(function(){alert(this)})()>I'm button</button>//this指代window <button ...
- js this指向理解
1.如果调用this的函数上级有多个对象,this只会指向上一级对象 下面实例fn函数调用this时,this指向b对象,如果b里面有a属性就输出值: 如果没有就是undefined 在来看下下面的实 ...
- JS—-this指向
箭头函数中this对象就是定义时所在的作用域,也就是说箭头函数本身没有this,内部的this就是外层代码块作用域中的this. 1.独立函数 var a = 0var test = ()=> ...
- JS this指向总结
使用 JavaScript 开发的时候,很多开发者多多少少会被 this 的指向搞蒙圈,但是实际上,关于 this 的指向,记住最核心的一句话:哪个对象调用函数,函数里面的this指向哪个对象. 下面 ...
- js this指向汇总
this指向 普通函数 window 定时器函数 window 事件函数 事件源 箭头函数 父function中的this,没有就是window 对象函数 对象本身 构造函数 实例化 ...
- 可能是史上最强大的js图表库——ECharts带你入门
PS:之前的那篇博客Highcharts——让你的网页上图表画的飞起 ,评论中,花儿笑弯了腰 和 StanZhai 两位仁兄让我试试 ECharts ,去主页看到<Why ECharts ?&g ...
随机推荐
- 纯css实现瀑布流(multi-column多列及flex布局)
瀑布流的布局自我感觉还是很吸引人的,最近又看到实现瀑布流这个做法,在这里记录下,特别的,感觉flex布局实现瀑布流还是有点懵的样子,不过现在就可以明白它的原理了 1.multi-column多列布局实 ...
- jsc2019_qualC Cell Inversion
先吐槽一下这个比赛的奇怪名字 这个破名字让我实在不知道博客标题该叫啥/px 题目大意 给你一个长度为2n的序列 包括W和B 每次可以选一个区间将里面的颜色反转 但是每个点只能被作为端点选一次 问将序列 ...
- Win32下session和window station以及desktop一些介绍和应用
会话(session).工作站(WindowStation).桌面(Disktop).窗口(window) https://blog.csdn.net/hlswd/article/details/77 ...
- scrapy-redis源码浅析
原文链接 前言 分析这个项目的源码原因是需要有去重过滤,增量爬取两个功能,而scrapy-redis项目已经帮我们实现了,想看看他是怎么实现的.这里只贴出部分主要代码,查看时请打开源码对照,笔记有点长 ...
- MyEclipse上有main函数类运行报错:Editor does not contain a
MyEclipse下有main函数类运行报错:Editor does not contain a main type?出现这种问题的原因是,该java文件 MyEclipse下有main函数类运行 ...
- 实验吧 Web的WriteUp
每次看别人的Writeup都有一种感觉,为什么有了WriteUp我还是不会,每次都打击自己的积极性,所以自己尝试写一篇每个萌新都能看懂的Writeup. 0x01 天下武功唯快不破 题目提示 : 看看 ...
- Java IO(3)
字符流相关 字符流基本上可以类比字节流 只不过是将字节流的byte 换为char. 最根本的两个类是Reader以及Writer Reader的子类有:BufferedReader, CharArra ...
- 02 - Jmeter4.x正则表达式以及跨线程使用变量
话不多说 直接开撸 上图可以看出,有两个请求,其中第二个请求返回了登录超时,结合第一个登录接口来看,这个是需要header请求内容的也就是 token:当然设置一个token又怎么可能难得倒我们,无非 ...
- 2019Android阿里&腾讯&百度&字节面试汇总(附面试题总结、Android书单)
1.基本情况 先简单说说我今年的面试经历吧,本人2018届211软件工程硕士生,Android开发岗.此文主要是2019年年初春招的面试和秋招面试经验汇总,最终拿到了阿里,腾讯,字节跳动,百度等off ...
- ubuntu下mysql定时备份
一:ubuntu下自动备份mysql数据库 转载来源:https://jingyan.baidu.com/article/ab0b563097cabac15afa7dbc.html 1.创建保存备份文 ...