js中this的含义非常丰富,可以是全局对象,当前对象或者是任意对象,都取决于函数的调用方式,函数有以下4种调用方式:作为对象方法调用、作为函数调用、作为构造函数调用、apply或call调用,又名默认绑定,隐式绑定,new绑定,硬绑定
优先级:new绑定 > 硬绑定 > 隐式绑定 > 默认绑定
严格模式和非严格模式
定时器中的函数,由于没有默认的宿主对象,所以this指向window
经典示例:https://mp.weixin.qq.com/s/Zk03gItX9coymj_fUnX82Q
一.作为对象方法调用的时候,this会被绑定到该对象。
var point = {
x : 0,
y : 0,
moveTo : function(x, y) {
this.x = this.x + x;
this.y = this.y + y;
}
};
point.moveTo(1, 1)//this 绑定到当前对象,即 point 对象
这里我想强调一点内容,就是this是在函数执行的时候去获取对应的值,而不是函数定义时。即使是对象方法调用,如果该方法的函数属性以函数名的形式传入别的作用域,也会改变this的指向。我举一个例子:
var a = {
aa : 0,
bb : 0,
fun : function(x,y){
this.aa = this.aa + x;
this.bb = this.bb + y;
}
};
var aa = 1;
var b = {
aa:0,
bb:0,
fun : function(){return this.aa;}
}
a.fun(3,2);
document.write(a.aa);//3,this指向对象本身
document.write(b.fun());//0,this指向对象本身
(function(aa){//注意传入的是函数,而不是函数执行的结果
var c = aa();
document.write(c);//1 , 由于fun在该处执行,导致this不再指向对象本身,而是这里的window
})(b.fun);
这里this指向的是window,是不是有些蒙了?其实是因为你没有理解一句话,这句话同样至关重要。
// this永远指向的是最后调用它的对象,也就是看它执行的时候是谁调用的,例子4中虽然函数fn是被对象b所引用,但是在将fn赋值给变量j的时候并没有执行所以最终指向的是window
二.作为函数调用,这时候this指向全局对象
var x = 1;
function test(){
this.x = 0;
}
test();
alert(x); //0
但这样就会出现一些问题,就是在函数内部定义的函数,其this也会指向全局,而和我们希望的恰恰相反。代码如下:
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);
point.x; //==>0
point.y; //==>0
x; //==>1
y; //==>1
我们会发现不但我们希望的移动呢效果没有完成,反而会多出两个全局变量。那么如何解决呢?只要要进入函数中的函数时将this保存到一个变量中,再运用该变量即可。代码如下:
var point = {
x : 0,
y : 0,
moveTo : function(x, y) {
var that = this;
// 内部函数
var moveX = function(x) {
that.x = x;
};
// 内部函数
var moveY = function(y) {
that.y = y;
}
moveX(x);
moveY(y);
}
};
point.moveTo(1, 1);
point.x; //==>1
point.y; //==>1
三.构造函数调用
在javascript中自己创建构造函数时可以利用this来指向新创建的对象上。这样就可以避免函数中的this指向全局了。
var x = 2;
function test(){
this.x = 1;
}
var o = new test();
alert(x); //2
alert(o.x) //1
特殊的两种情况:
当this遇到return时
// 第一种:当this遇到return
function fn() {
this.user = '剃了胡子';
return {};
}
var a = new fn;
console.log(a.user); //undefined
function fn() {
this.user = '剃了胡子';
return function () {};
}
var a = new fn;
console.log(a.user); //undefined
function fn() {
this.user = '剃了胡子';
return 1;
}
var a = new fn;
console.log(a.user); //剃了胡子
function fn() {
this.user = '剃了胡子';
return undefined;
}
var a = new fn;
console.log(a.user); //剃了胡子
function fn() {
this.user = '剃了胡子';
return null;
}
var a = new fn;
console.log(a.user); //剃了胡子
总结:如果返回值是一个对象,那么this指向这个对象,若不是则this还指向函数的实例
比较特殊的就是null,虽然也是对象,但是指向的是函数的实例
四.apply和call调用和bind调用
这两个方法可以切换函数执行的上下文环境,也就是改变this绑定的对象。apply和call比较类似,区别在于传入参数时一个要求是数组,一个要求是分开传入。所以我们以apply为例:
var name = "window";
var someone = {
name: "Bob",
showName: function(){
alert(this.name);
}
};
var other = {
name: "Tom"
};
someone.showName(); //Bob
someone.showName.apply(); //window
someone.showName.apply(other); //Tom
对于bind:
// bind方法和call、apply方法有些不同,如下:
var a = {
user:"剃了胡子",
fn:function(){
console.log(this.user);
}
}
var b = a.fn;
b.bind(a); //代码没有被打印
// 我们发现代码没有被打印,对,这就是bind和call、apply方法的不同,实际上bind方法返回的是一个修改过后的函数。
var a = {
user:"剃了胡子",
fn:function(){
console.log(this.user);
}
}
var b = a.fn;
var c = b.bind(a);
console.log(c); //function() { [native code] }
// 函数c看看,能不能打印出对象a里面的user
var a = {
user:"剃了胡子",
fn:function(){
console.log(this.user); //剃了胡子
}
}
var b = a.fn;
var c = b.bind(a);
c();
// 同样bind也可以有多个参数,并且参数可以执行的时候再次添加,但是要注意的是,参数是按照形参的顺序进行的
var a = {
user:"剃了胡子",
fn:function(e,d,f){
console.log(this.user); //剃了胡子
console.log(e,d,f); //10 1 2
}
}
var b = a.fn;
var c = b.bind(a,10);
c(1,2);
五.补充,对于箭头函数的this的指向
var obj = {
say: function () {
var f1 = () => {
console.log(this); // obj
setTimeout(() => {
console.log(this); // obj
})
}
f1();
}
}
obj.say()
箭头函数没有自己的this, 它的this是继承而来; 默认指向在定义它时所处的对象(宿主对象)
var obj = {
say: function () {
var f1 = function () {
console.log(this); // window, f1调用时,没有宿主对象,默认是window
setTimeout(() => {
console.log(this); // window
})
};
f1();
}
}
obj.say()
结果: 都是 window,因为 箭头函数在定义的时候它所处的环境相当于是window, 所以在箭头函数内部的this函数window
简要介绍:箭头函数中的this,指向与一般function定义的函数不同,比较容易绕晕,箭头函数this的定义:箭头函数中的this是在定义函数的时候绑定,而不是在执行函数的时候绑定。
1、何为定义时绑定
我们来看下面这个例子:
(1)
var x=11;
var obj={
x:22,
say:function(){
console.log(this.x)
}
}
obj.say();
//console.log输出的是22
var x=11;
var obj={
x:22,
say:function(){
console.log(this.x)
}
}
obj.say();
//console.log输出的是22
一般的定义函数跟我们的理解是一样的,运行的时候决定this的指向,我们可以知道当运行obj.say()时候,this指向的是obj这个对象。
(2)
var x=11;
var obj={
x:22,
say:()=>{
console.log(this.x);
}
}
obj.say();
//输出的值为11
var x=11;
var obj={
x:22,
say:()=>{
console.log(this.x);
}
}
obj.say();
//输出的值为11
这样就非常奇怪了如果按照之前function定义应该输出的是22,而此时输出了11,那么如何解释ES6中箭头函数中的this是定义时的绑定呢?
所谓的定义时候绑定,就是this是继承自父执行上下文!!中的this,比如这里的箭头函数中的this.x,箭头函数本身与say平级以key:value的形式,也就是箭头函数本身所在的对象为obj,而obj的父执行上下文就是window,因此这里的this.x实际上表示的是window.x,因此输出的是11。(this只有在函数被调用,或者通过构造函数new Object()的形式才会有this)
类似的还有:
(3)
var a=11
function test1(){
this.a=22;
let b=function(){
console.log(this.a);
};
b();
}
var x=new test1();
输出11
var a=11
function test1(){
this.a=22;
let b=function(){
console.log(this.a);
};
b();
}
var x=new test1();
输出11
箭头函数情况:
var a=11;
function test2(){
this.a=22;
let b=()=>{console.log(this.a)}
b();
}
var x=new test2();
//输出22
很奇怪对不对,我是这样理解的,ES6中定义的时候绑定this的具体含义,应该继承的是父执行上下文里面的this,切忌是父执行上下文!!!这样就很多箭头函数中的指向不明确就迎刃而解了。
注意:简单对象(非函数)是没有执行上下文的!
2017年8月13日补充:
箭头函数中,this指向的固定化,并不是因为箭头函数内部有绑定this的
机制,实际原因是箭头函数根本没有自己的this,导致内部的this就是外
层代码块的this。正是因为它没有this,所以也就不能用作构造函数。
我们可以来模拟ES5中的箭头函数转化:
// ES6
function foo() {
setTimeout(() => {
console.log('id:', this.id);
}, 100);
}
// ES5
function foo() {
var _this = this;
setTimeout(function () {
console.log('id:', _this.id);
}, 100);
}
所以在定义对象的时候,定义对象属性,里面的this指向的一般是全局,或者这个对象所在的那个环境中的this。
六。严格模式下this的指向
1.全局作用域中的this
在严格模式下,在全局作用域中,this指向window对象
“use strict”;
console.log("严格模式");
console.log("在全局作用域中的this");
console.log("this.document === document",this.document === document);
console.log("this === window",this === window);
this.a = 9804;
console.log('this.a === window.a===',window.a);
2.全局作用域中函数中的this,在严格模式下,这种函数中的this等于undefined
“use strict”;
console.log("严格模式");
console.log('在全局作用域中函数中的this');
function f1(){
console.log(this);
}
function f2(){
function f3(){
console.log(this);
}
f3();
}
f1();
f2();
3.严格模式下对象函数中的this和构造函数中的this和平常相同
4.事件处理函数中的this,在严格模式下,在事件处理函数中,this指向触发事件的目标对象。
“use strict”;
function blue_it(e){
if(this === e.target){
this.style.backgroundColor = "#00f";
}
}
var elements = document.getElementsByTagName('*');
for(var i=0 ; i<elements.length ; i++){
elements[i].onclick = blue_it;
}
//这段代码的作用是使被单击的元素背景色变为蓝色
5.内联时间处理函数中的this
内联事件处理1
<button onclick="'use strict'; alert(this.tagName.toLowerCase());">
内联事件处理2
</button>
<!-- 警告窗口中的字符为button —>
参考链接:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/this