js中的this

作者: little_short | 来源:发表于2018-12-05 15:33 被阅读0次

前言

近期对this很感兴趣,于是乎简单整理了一些关于this的技术点,加深一下对this的理解。

非箭头函数

在非箭头函数下,this指向调用其所在函数的对象,而且是离谁近就是指向谁(此对于常规对象,原型链,getter & setter等都适用;构造函数下,this与被创建的新对象绑定;DOM事件,this指向触发事件的元素;内联事件分两种情况,bind绑定,call & apply 方法等,容以下一步一步讨论。

解析

  • 全局环境下this 始终指向全局对象(window), 无论是否严格模式。
  • 函数直接调用的情况下。
  • 普通函数内部的this分两种情况,严格模式和非严格模式。
  • 非严格模式下,this默认指向全局对象window。
function f1(){
  return this;
}
f1() === window; // true
  • 其它函数的情况
  • 情况1:如果一个函数中有this,但是它没有被上一级的对象所调用,那么this指向的就是window,这里需要说明的是在js的严格版中this指向的不是window,但是我们这里不探讨严格版的问题,你想了解可以自行上网查找。
  • 情况2:如果一个函数中有this,这个函数有被上一级的对象所调用,那么this指向的就是上一级的对象。
  • 情况3:如果一个函数中有this,这个函数中包含多个对象,尽管这个函数是被最外层的对象所调用,this指向的也只是它上一级的对象。
  • 而严格模式下, this为undefined
function f2(){
  "use strict"; // 这里是严格模式
  return this;
}
f2() === undefined; // true
  • 对象中的this
  • 函数的定义位置不影响其this指向,this指向只和调用函数的对象有关。
  • 多层嵌套的对象,内部方法的this指向离被调用函数最近的对象(window也是对象,其内部对象调用方法的this指向内部对象,而非window)。
//1
const o = {
  prop: 37,
  f: function() {
    return this.prop;
  }
};
console.log(o.f());  //37
const a = o.f;
console.log(a()):  //undefined
const o = {prop: 37};
function independent() {
  return this.prop;
}
o.f = independent;
console.log(o.f()); // logs 37

//2
o.b = {
  g: independent,
  prop: 42
};
console.log(o.b.g()); // logs 42
  • 原型链中this
  • 原型链中的方法的this仍然指向调用它的对象
var o = {
  f : function(){ 
    return this.a + this.b; 
  }
};
var p = Object.create(o);
p.a = 1;
p.b = 4;
console.log(p.f()); // 5
  • 可以看出,在p中没有属性f,当执行p.f()时,会查找p的原型链,找到f函数并执行,但这与函数内部this指向对象p没有任何关系,只需记住谁调用指向谁。以上对于函数作为getter & setter调用时同样适用。
const o = {
  f : function(){ 
    return this.a + this.b; 
  }
};
const p = Object.create(o);
p.a = 1;
p.b = 4;
console.log(p.f()); // 5
  • 构造函数中this
  • 构造函数中的this与被创建的新对象绑定。
  • 注意:当构造器返回的默认值是一个this引用的对象时,可以手动设置返回其他的对象,如果返回值不是一个对象,返回this。
function C(){
  this.a = 37;
}
const o = new C();
console.log(o.a); // logs 37
function C2(){
  this.a = 37;
  return {a:38};
}
const b = new C2();
console.log(b.a); // logs 38
  • 以上两个例子内部的this都指向对象o, 看到这里的你不妨在控制台执行下以上代码,看看对象o和b ,这些是属于构造函数的内容了,此处不多做介绍。(C2函数中的this.a = 37对整个过程完全没有影响的,可以被忽略的)。
  • call & apply
  • 当函数通过Function对象的原型中继承的方法 call() 和 apply() 方法调用时, 其函数内部的this值可绑定到 call() & apply() 方法指定的第一个对象上, 如果第一个参数不是对象,JavaScript内部会尝试将其转换成对象然后指向它。
function add(c, d){
  return this.a + this.b + c + d;
}
const 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 tt() {
  console.log(this);
}
tt.call(5);  // Number {[[PrimitiveValue]]: 5} 
tt.call('asd'); // String {0: "a", 1: "s", 2: "d", length: 3, [[PrimitiveValue]]: "asd"}
  • bind 方法
  • bind方法在ES5引入, 在Function的原型链上, Function.prototype.bind。通过bind方法绑定后, 函数将被永远绑定在其第一个参数对象上, 而无论其在什么情况下被调用。
function f(){
  return this.a;
}

const g = f.bind({a:"azerty"});
console.log(g()); // azerty

const o = {a:37, f:f, g:g};
console.log(o.f(), o.g()); // 37, azerty
  • DOM 事件处理函数中的 this & 内联事件中的 this
  • 1、当函数被当做监听事件处理函数时,其this指向触发该事件的元素(针对于addEventListener事件)。
// 被调用时,将关联的元素变成蓝色
    function bluify(e){
      //在控制台打印出所点击元素
      console.log(this);
      //阻止时间冒泡
      e.stopPropagation();
      //阻止元素的默认事件
      e.preventDefault();      
      this.style.backgroundColor = '#A5D9F3';
    }

    // 获取文档中的所有元素的列表
    const elements = document.getElementsByTagName('*');

    // 将bluify作为元素的点击监听函数,当元素被点击时,就会变成蓝色
    for(let i=0 ; i<elements.length ; i++){
      elements[i].addEventListener('click', bluify, false);
    }
  • 内联事件
  • 内联事件中的this指向分两种情况:
  • 1、当代码被内联处理函数调用时,它的this指向监听器所在的DOM元素。
  • 2、当代码被包括在函数内部执行时,其this指向等同于函数直接调用的情况,即在非严格模式指向全局对象window, 在严格模式指向undefined
<button onclick="console.log(this);">show me</button> //<button onclick="console.log(this);">show me</button> 
<button onclick="(function(){console.log(this)})()"></button> //window
<button onclick="(function(){'use strict';console.log(this)}())">use strict</button> //undefined
  • setTimeout & setInterval
  • 对于延时函数内部的回调函数的this指向全局对象window(当然我们可以通过bind方法改变其内部函数的this指向)
    看下边代码.
//默认情况下代码
function Person() {  
    this.age = 0;  
    setTimeout(function() {
        console.log(this);
    }, 3000);
}

const p = new Person();//3秒后返回 window 对象
==============================================
//通过bind绑定
function Person() {  
    this.age = 0;  
    setTimeout((function() {
        console.log(this);
    }).bind(this), 3000);
}
const p = new Person();//3秒后返回构造函数新生成的对象 Person{...}

规则

  • 默认绑定全局变量
function fn() {
    console.log( this.a );
}
const a = 2;
fn(); // 2 -- fn单独调用,this引用window
  • 隐式绑定
  • 1、隐式调用的意思是,函数调用时拥有一个上下文对象,就好像这个函数是属于该对象的一样。
function fn() {
    console.log( this.a );
}
var obj = {
    a: 2,
    fn: fn
};
obj.fn(); // 2 -- this引用obj。
  • 2、需要说明的一点是,最后一个调用该函数的对象是传到函数的上下文对象(绕懵了)。
function fn() {
     console.log( this.a );
 }
 var obj2 = {
     a: 42,
     fn: fn
 };
 var obj1 = {
     a: 2,
     obj2: obj2
 };
 obj1.obj2.fn(); // 42 -- this引用的是obj2.
  • 3、还有一点要说明的是,失去隐式绑定的情况。
function fn() {
     console.log( this.a );
 }
 var obj = {
     a: 2,
     fn: fn
 };
 var bar = obj.fn; // 函数引用传递
 var a = "全局"; // 定义全局变量
 bar(); // "全局"
  • 显示绑定
  • 1、学过bind()\apply()\call()函数的都应该知道,它接收的第一个参数即是上下文对象并将其赋给this。
function fn() {
     console.log( this.a );
 }
 var obj = {
     a: 2
 };
 fn.call( obj ); // 2
  • 2、如果我们传递第一个值为简单值,那么后台会自动转换为对应的封装对象。如果传递为null,那么结果就是在绑定默认全局变量
function fn() {
      console.log( this.a );
  }
  var obj = {
      a: 2
  };
 var a = 10;
 fn.call( null); // 10
  • new新对象绑定
  • 1、如果是一个构造函数,那么用new来调用,那么绑定的将是新创建的对象。
function fn(a) {
    this.a = a;
}
var bar = new fn( 2 );
console.log( bar.a );// 2

用法

  • 在一般函数方法中使用 this 指代全局对象
function test(){
    this.x = 1;
    alert(this.x);
  }
  test(); // 1
  • 作为对象方法调用,this 指代上级对象
function test(){
  alert(this.x);
}
var o = {};
o.x = 1;
o.m = test;
o.m(); // 1
  • 作为构造函数调用,this 指代new 出的对象
function test(){
    this.x = 1;
  }
  var o = new test();
  alert(o.x); // 1
    //运行结果为1。为了表明这时this不是全局对象,我对代码做一些改变:
  var x = 2;
  function test(){
    this.x = 1;
  }
  var o = new test();
  alert(x); //2
  • apply 调用 ,apply方法作用是改变函数的调用对象,此方法的第一个参数为改变后调用这个函数的对象,this指代第一个参数
var x = 0;
  function test(){
    alert(this.x);
  }
  var o={};
  o.x = 1;
  o.m = test;
  o.m.apply(); //0
//apply()的参数为空时,默认调用全局对象。因此,这时的运行结果为0,证明this指的是全局对象。如果把最后一行代码修改为
  o.m.apply(o); //1

箭头函数中的this

  • 1、箭头函数中的this是在定义函数的时候绑定,而不是在执行函数的时候绑定,所谓的定义时候绑定,就是this是继承自父执行上下文!
  • 2、箭头函数的this绑定看的是this所在的函数定义在哪个对象下,绑定到哪个对象则this就指向哪个对象
  • 3、如果有对象嵌套的情况,则this绑定到最近的一层对象上
  • 4、不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
  • 5、不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
  • 6、不可以使用yield命令,因此箭头函数不能用作 Generator 函数。

解析

  • 由于箭头函数不绑定this, 它会捕获其所在(即定义的位置)上下文的this值, 作为自己的this值
  • 1、call()/apply()/bind() 方法对于箭头函数来说只是传入参数,对它的this毫无影响。
function Person() {  
    this.age = 0;  
    setInterval(() => {
        // 回调里面的 `this` 变量就指向了期望的那个对象了
        this.age++;
    }, 3000);
}
const p = new Person();
  • 2、考虑到 this 是词法层面上的,严格模式中与 this 相关的规则都将被忽略。
const adder = {
  base : 1,
  add : function(a) {
    var f = v => v + this.base;
    return f(a);
  },
  addThruCall: function inFun(a) {
    const f = v => v + this.base;
    const b = {
      base : 2
    };
            
    return f.call(b, a);
  }
};

console.log(adder.add(1));         // 输出 2
console.log(adder.addThruCall(1)); // 仍然输出 2(而不是3,其内部的this并没有因为call() 而改变,其this值仍然为函数inFun的this值,指向对象adder
  • 对于是否严格模式示例代码
const f = () => {'use strict'; return this};
const p = () => { return this};
console.log(1,f() === window);
console.log(2,f() === p());
//1 true
//2 true
  • 以上的箭头函数都是在方法内部,总之都是以非方法的方式使用,如果将箭头函数当做一个方法使用会怎样呢?
const obj = {
     i: 10,
     b: () => console.log(this.i, this),
     c: function() {
       console.log( this.i, this)
     }
   }
   obj.b();  // undefined window{...}
   obj.c();  // 10 Object {...}

相关文章

  • JavaScript 04 (do...while循环/for

    js循环,js循环嵌套,js do...while循环,js的for循环,js中的break,js中的contin...

  • iOS原生&JS交互

    OC 获取js中的key OC调用js方法 JS调用OC中不带参数的block JS调用OC中带参数的block ...

  • JS 对象

    JS对象 JS对象的意义和声明 在JS中,对象(OBJECT)是JS语言的核心概念,也是最重要的数据类型。在JS中...

  • 单引号和双引号

    JS中 js中单引号和双引号的区别(html中属性规范是用双引号,js中字符串规定是用单引号)(js中单引号区别和...

  • js中的this

    一句话,call的第一个参数 看几个例子例1. 这里的this是什么?不要靠猜,是call的第一个参数,不知道去看...

  • js 中的this

    首先js中函数的this在函数被调用时总是指向一个对象(this对象是在运行时基于函数的执行环境绑定的) 然后 它...

  • JS中的this

    JS中的this 众所周知,JS中this的代表的是当前函数调用者的上下文。JS是解释性的动态类型语言,函数在调用...

  • js中的!!

    !!一般用来将后面的表达式强制转换为布尔类型的数据(boolean),也就是只能是true或者false。 var...

  • js中的this

    目标:js中this的指向? 问题的引出 指出this指向什么 js中函数的三种调用形式 func(p1, p2)...

  • JS中的this

    初学JavaScript经常被this绕晕,所以我总结一下JavaScript中的this。首先声明本文讨论的是非...

网友评论

    本文标题:js中的this

    本文链接:https://www.haomeiwen.com/subject/wqyicqtx.html