JS中的this指向

在常见的编程语言中,几乎都有this这个关键字,但是JavaScript中的this和常见的面向对象语言中的this不太一样:

常见面向对象的编程语言中,比如Java、C++、Swift、Dart等等一系列语言中,this通常只会出现在类的方法中。

也就是你需要有一个类,类中的方法(特别是实例方法)中,this代表的是当前调用对象。

但是JavaScript中的this更加灵活,无论是它出现的位置还是它代表的含义。

在全局作用域下this指向window,但是在真正的开发中我们很少在全局作用域下去使用this,通常都是在函数中使用

this到底指向什么?

我们定义一个函数,采用三种不同的方法对他进行调用,他产生了三种不同的结果。

// 定义一个函数
function foo(){
	console.log(this)
}

// 方式一:直接调用
foo(); //window

// 方式二:将foo放到一个对象里,在调用
var obj = {
	name:"aaa",
	foo:foo
}
obj.foo() //obj对象

// 方式三:通过call/apply调用
foo.call("abc") //String{"abc"}对象

由此可见this的绑定和定义的位置没关系,和调用的位置有关系,this是在运行时被绑定的。

this的绑定规则

规则一:默认绑定

// 默认绑定: 独立函数调用
// 1.案例一:
 function foo() {
	console.log(this);
 }
foo();  // window

// 2.案例二:
function foo1() {
  console.log(this) // window
}

function foo2() {
  console.log(this) // window
  foo1()
}

function foo3() {
  console.log(this) // window
  foo2()
}

foo3()

// 3.案例三:
var obj = {
  name: "why",
  foo: function() {
    console.log(this)
  }
}

var bar = obj.foo
bar() // window

// 4.案例四:
function foo() {
  console.log(this)
}
var obj = {
  name: "why",
  foo: foo
}

var bar = obj.foo
bar() // window

// 5.案例五:
function foo() {
  function bar() {
    console.log(this)
  }
  return bar
}

var fn = foo()
fn() // window

var obj = {
  name: "why",
  eating: fn
}

obj.eating() // obj

规则二:隐式绑定

// 隐式绑定: object.fn()
// object对象会被js引擎绑定到fn函数的中this里面
// 1.案例一:
var obj = {
  name: "why",
  foo: foo
}

obj.foo() // obj对象

// 2.案例二:
var obj = {
	name: "why",
	eating: function () {
		console.log(this.name + "在吃东西");
	},
	running: function () {
		console.log(obj.name + "在跑步");
	},
};

obj.eating() //obj
obj.running() //obj

var fn = obj.eating
fn() // window

// 3.案例三:
var obj1 = {
	name: "obj1",
	foo: function () {
		console.log(this);
	},
};

var obj2 = {
	name: "obj2",
	bar: obj1.foo,
};

obj2.bar(); // obj2

规则三:显式绑定

显示绑定-apply-call

// 1.foo直接调用和call/apply调用的不同在于this绑定的不同
function foo() {
	console.log("函数被调用了", this);
}
var obj = {
  name: "obj"
}
call/apply都可以指定this的绑定对象
foo.call(obj) //obj
foo.apply(obj) //obj
foo.apply("aaaa") //aaaa

// 2.call和apply有什么区别?
function sum(num1, num2, num3) {
	console.log(num1 + num2 + num3, this);
}

sum.call("call", 20, 30, 40); //String{'call'}
sum.apply("apply", [20, 30, 40]);//String{'allpy'}

// 总结
// call和apply在执行函数时,是可以明确的绑定this, 这个绑定规则称之为显示绑定
// 两者第一个参数是相同的,都是绑定到某一个对象上,区别在于后面的参数,apply为数组,call为参数列表;

显示绑定-bind

//如果我们希望一个函数总是显示的绑定到一个对象上,那么我们就是可以使用bind绑定
function foo() {
  console.log(this)
}
var newFoo = foo.bind("aaa")

newFoo()
newFoo()
newFoo()
newFoo()
newFoo()
newFoo() // String{'aaa'}
var bar = foo
console.log(bar === foo) // true
console.log(newFoo === foo) //false

call、apply、bind 总结

相同点:

  1. 都可以改变函数内部的this指向。

区别点:

  1. call 和 apply 会调用函数,并且改变函数内部this指向。
  2. call 和 apply 传递的参数不一样,call 传递参数arg1,arg2…形式 apply 必须数组形式[arg]
  3. bind 不会调用函数,可以改变函数内部this指向。

主要应用场景:

  1. call 经常做继承。
  2. apply 经常跟数组有关系,比如借助于数学对象实现数组最大值最小值。
  3. bind 不调用函数,但是还想改变this指向,比如改变定时器内部的this指向。

规则四:new绑定

// 我们通过一个new关键字调用一个函数时(构造器), 这个时候this是在调用这个构造器时创建出来的对象
// this = 创建出来的对象
// 这个绑定过程就是new 绑定

function Person(name, age) {
  this.name = name
  this.age = age
}

var p1 = new Person("why", 18)
console.log(p1.name, p1.age) 

var p2 = new Person("kobe", 30)
console.log(p2.name, p2.age)

规则优先级:

new绑定和call、apply是不允许同时使用的,所以不存在谁的优先级更高

new>bind(显示绑定)>隐式绑定>默认绑定

this规则之外

如果在显示绑定中,我们传入一个null或者undefined,那么这个显示绑定会被忽略,使用默认规则:

function foo(){
	console.log(this)
}

var obj = {
	name:"why"
}

foo.call(obj); // obj
foo.call(null); // window
foo.call(undefined); // window

var bar = foo.bind(null)
bar(); //window

另外一种情况,创建一个函数的 间接引用,这种情况使用默认绑定规则。

function foo(){
	console.log(this)
}
var obj1 = {
	name:"obj1",
	foo:foo
};
var obj2 = {
	name:"obj2"
}

obj1.foo(); //obj1
(obj2.foo = obj1.foo)() //window

箭头函数

在箭头函数中,不是根据函数的调用位置来决定this的指向,而是根据外层作用域来决定this的指向。

var obj = {
  data: [],
  getData: function() {
    // 发送网络请求, 将结果放到上面data属性中
    // 在箭头函数之前的解决方案
    // var _this = this
    // setTimeout(function() {
    //   var result = ["abc", "cba", "nba"]
    //   _this.data = result
    // }, 2000);
    // 箭头函数之后
    setTimeout(() => {
      var result = ["abc", "cba", "nba"]
      this.data = result
    }, 2000);
  }
}

obj.getData()

面试题:摘自coderwhy大神

前端面试之彻底搞懂this指向 (qq.com)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值