插件窝 干货文章 深入了解JavaScript中的this关键字指向

深入了解JavaScript中的this关键字指向

person 函数 绑定 person2 582    来源:    2024-10-30

全局作用域的this

  • 游览器:非严格模式指向window,严格模式指向undefined
  • Node环境:指向{}
    • 把文件看作module -> 加载编译 -> 放到一个函数中 -> 通过 apply({})/call({}) 执行这个函数

调用方式不同,this指向不同

在大多情况下,this出现在函数中。this指向什么,跟函数定义的位置无关,跟函数调用方式有关(绑定规则相关)

function foo(){
    console.log(this);
}
var obj = {
    foo: foo
}
//不同的调用方式this的指向不同,不同的调用方式对应着不同的绑定规则
foo();//window
obj.foo();//obj
foo.call("123");//String{"abc"}

this的绑定规则

  1. 默认绑定:函数独立调用方式,this指向全局对象
  2. 隐式绑定:通过对象调用方法,会隐式的将this指向当前对象
  3. 显示绑定:通过函数的apply、call、bind方法改变函数中this的指向
    • apply/call:会自动调用,第一个参数都是设置this的指向,apply第二个参数为数组,call后面传入为参数列表。
    • bind:不会自动调用,会返回一个已经绑定好this的函数,传参与call一样
  4. new绑定 :通过new关键字调用函数,new 函数()
    • 创建一个全新的对象
    • 这个新对象会被执行prototype连接
    • 执行构造函数中的代码,为新对象添加属性(this绑定在这个步骤完成)
    • 如果函数没有返回其他对象,表达式会返回这个新对象

this绑定优先级(权重)

  • 示绑定(bind>call/apply) > 隐式绑定 > 默认绑定
function fn() {
  console.log(this);
}
fn.bind(123).apply(321);
  • new 绑定 > 隐式绑定 > 默认绑定
  • new 绑定 > 显示绑定(通过bind绑定this的函数,可以被new关键字改变this指向)
function Fn(){
  console.log(this);
}
fn.bind("132");
var obj = new Fn();
  • new关键字不能和call/apply一起使用,因为new会主动调用函数,call/apply也会主动调用函数,这样会产生冲突报错

绑定优先级:new 绑定 > 显示绑定 > 隐式绑定 > 默认绑定

this规则之外

忽略显示绑定

apply/call/bind:当传入null/undefined时,自动将this绑定成全局对象

function fn(){
  console.log(this);
}
fn.apply(null);
fn.apply(undefined);

间接函数引用

var obj1 = {
    foo: function(){
        console.log(this);
    }
}
var obj2 = {}
;(obj2.bar = obj1.foo)()//括号中包含赋值表达式,属于独立函数调用,是默认绑定,指向window

箭头函数 将头函数不会绑定this,arguments属性箭头函数不能作为构造函数来使用(不能new对象) 简写

  • 简写一:当参数只有一个,可以省略括号
  • 简写二:当函数体只有一行,可以省略大括号,并且会将这一行代码的返回值return
var nums = [0, 1, 2, 3, 4, 5];
var result = nums
  .filter(item => item % 2 === 0)
  .map(item => item * 10)
  .reduce((preVal, curVal) => preVal + curVal, 0);
console.log(result);
  • 简写三:如果只有一个行,且需要返回一个对象,那么要将对象用括号包裹。因为省略"{}“后,不知道将对象的”{}"如何解析
var foo = () => ({ name: "foo", age: 18 });//这样解析时会将对象当作一个整体

箭头函数的this绑定

箭头函数不遵循this的四种标准规则,箭头函数内部没有绑定this,也不能通过call/apply/bind绑定this,箭头函数的this指向根据外层作用域决定

var obj = {
  getData() {
    setTimeout(function () {
      console.log(this);
    }, 200);
  },
};
//setTimeout中的函数属于独立调用,所以指向的是window
//如果想让this指向obj对象,setTimeout中使用箭头函数
obj.getData();

this相关面试题

面试题一:

var name = "window";
var person = {
  name: "person",
  sayName: function () {
    console.log(this.name);
  }
};
function sayName() {
  var sss = person.sayName;
  sss(); 
  person.sayName(); 
  (person.sayName)(); 
  (b = person.sayName)(); //包含赋值表达式,属于间接函数引用
}
sayName();

答案:window(默认绑定)、person(隐式绑定)、person(隐式绑定)、window(间接函数引用)

面试题二:

var name = 'window'
var person1 = {
  name: 'person1',
  foo1: function () {
    console.log(this.name)
  },
  foo2: () => console.log(this.name),
  foo3: function () {
    return function () {
      console.log(this.name)
    }
  },
  foo4: function () {
    return () => {
      console.log(this.name)
    }
  }
}
var person2 = { name: 'person2' }
person1.foo1(); 
person1.foo1.call(person2); 
person1.foo2();
person1.foo2.call(person2);
person1.foo3()();
person1.foo3.call(person2)();
person1.foo3().call(person2);
person1.foo4()();
person1.foo4.call(person2)();
person1.foo4().call(person2);

答案解析:

person1.foo1(); //person1:隐式绑定
person1.foo1.call(person2); //person2:显示绑定
person1.foo2();//window:箭头函数的this指向上层作用域的this
person1.foo2.call(person2);//window:箭头函数的this指向上层作用域的this,且不能改变箭头函数指向
person1.foo3()();//window:先执行person1.foo3(),然后拿到返回的函数再调用,属于独立调用,所以指向window
person1.foo3.call(person2)();//window:先执行person1.foo3.call(person2),拿到返回的函数再调用,属于独立调用
person1.foo3().call(person2);//person2:先执行person1.foo3(),拿到返回的函数通过call调用this指向了person2
person1.foo4()(); //person1:拿到返回的箭头函数后,箭头函数没有this,根据上层作用域决定,上层foo4函数指向的是person1对象(foo4是被person1直接调用的),所以内部箭头函数也指向peroson1
person1.foo4.call(person2)(); //person2:给foo4绑定this为person2,箭头函数调用this指向上层作用域,上层foo4的this被显示绑定为了person2,那么内部的箭头函数也是指向person2
person1.foo4().call(person2); //person1:call调用箭头函数,依然去上一层找,所以依然是person1

面试题三:

var name = 'window'
function Person (name) {
  this.name = name
  this.foo1 = function () {
    console.log(this.name)
  },
  this.foo2 = () => console.log(this.name),
  this.foo3 = function () {
    return function () {
      console.log(this.name)
    }
  },
  this.foo4 = function () {
    return () => {
      console.log(this.name)
    }
  }
}
var person1 = new Person('person1')
var person2 = new Person('person2')
person1.foo1()
person1.foo1.call(person2)
person1.foo2()
person1.foo2.call(person2)
person1.foo3()()
person1.foo3.call(person2)()
person1.foo3().call(person2)
person1.foo4()()
person1.foo4.call(person2)()
person1.foo4().call(person2)

答案与解析:

person1.foo1() //person1
person1.foo1.call(person2) //person2
person1.foo2() //person1:箭头函数,指向上层作用域的this,上层作用域是“Person构造函数”
person1.foo2.call(person2) //person1::箭头函数,指向上层作用域的this,不能被显示绑定
person1.foo3()() //window:拿到返回的函数后调用,属于独立调用
person1.foo3.call(person2)() //window:拿到返回的函数后调用,属于独立调用
person1.foo3().call(person2) //person2:通过call显示绑定
person1.foo4()() //person1:返回的箭头函数去上层作用域找,上层foo的this指向person1
person1.foo4.call(person2)()//person2:返回的箭头函数去上层作用域找,上层this通过call指向了person2
person1.foo4().call(person2)//person1:箭头函数的this不能被显示绑定

面试题四:

var name = 'window'
function Person (name) {
  this.name = name
  this.obj = {
    name: 'obj',
    foo1: function () {
      return function () {
        console.log(this.name)
      }
    },
    foo2: function () {
      return () => {
        console.log(this.name)
      }
    }
  }
}
var person1 = new Person('person1')
var person2 = new Person('person2')
person1.obj.foo1()()
person1.obj.foo1.call(person2)()
person1.obj.foo1().call(person2)
person1.obj.foo2()()
person1.obj.foo2.call(person2)()
person1.obj.foo2().call(person2)

答案与解析:

person1.obj.foo1()() //window:独立调用
person1.obj.foo1.call(person2)() //window:独立调用
person1.obj.foo1().call(person2) //person2:call显示绑定
person1.obj.foo2()() //obj:返回的是箭头函数,this由上层作用域foo2函数决定,foo2的this指向obj(foo2是被obj直接调用的),所以返回的箭头函数this也是指向obj
person1.obj.foo2.call(person2)()//person2:箭头函数上层作用域this被call指向person2,所以箭头函数也是指向person2
person1.obj.foo2().call(person2)//obj:箭头函数this不能被显示绑定

到此这篇关于深入了解JavaScript中的this关键字指向的文章就介绍到这了,更多相关JavaScript中的this内容请搜索插件窝以前的文章或继续浏览下面的相关文章希望大家以后多多支持插件窝!