一句话让你永远也忘不了javascrpt 中的this

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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值