JavaScript 中改变 this 的指向

Posted 知其黑、受其白

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaScript 中改变 this 的指向相关的知识,希望对你有一定的参考价值。

阅读目录

如何灵活的运用 this


面对对象的语言中,this 表示当前对象的一个引用,但在 javascript 中,this 不是固定不变的,它会随之执行环境的改变而改变。

一般情况下 this 指向全局变量 window。


作为对象方法调用时,this 指向上级对象。


作为构造函数调用时,this 指向 new 出的对象。

在函数中,非严格模式下 this 指向全局对象,但是在严格模式下 this 指向 undefined。


在事件中,this 表示接收事件的元素。

改变 this 的指向

在 JavaScript 中,我们可以通过 call,apply,bind 来改变 this 的指向,以满足不同的场景需求。

举个栗子

var apple = 
  name: '苹果',
  color: '红色',
  fn: function()
    console.log(`$this.name$this.color`)
   
 
apple.fn()   //苹果是红色的

有一个 apple 对象,它有 name 和 color 两个属性,还有一个方法 fn 用来打印出 name 和 color。

如果现在有一个新的对象 banana:

var banana = 
  name: '香蕉',
  color: '黄色'

现在我也想打印出 banana 的信息,但是我并不想再写一遍 fn 方法,如果可以直接利用apple 的 fn 方法那就简化了很多。

apple.fn.call(banana)   //香蕉是黄色的

通过 call 方法可以改变 apple.fn 中 this 的指向,指向的对象为接受的第一个参数。
我们还可以在后面传入多个参数,就像下面这样:

var apple = 
  name: '苹果',
  color: '红色',
  fn: function(price, unit)
    console.log(`$this.name$this.color的,$price元/$unit`)
  

apple.fn.call(banana, 3.99, '斤') //香蕉是黄色的,3.99元/斤

apply 与 call 的用法几乎相同,唯一的区别就是传入参数不同,第一个参数同样是目标对象,剩余的参数以一个数组的形式传入,如下:

apple.fn.apply(banana, [3.99, '斤']) //香蕉是黄色的,3.99元/斤

bind 的作用也是改变 this 的指向,但它与 call 和 apply 不同的是,它会返回一个函数,而不是立即执行,传参方式与 call 相同。

var fn2 = apple.fn.bind(banana, 3.99, '斤')
fn2() //香蕉是黄色的,3.99元/斤

当第一个参数为空、null、undefined、NaN、空字符串时,默认传入全局对象

如果第一个参数为 number、boolean 类型的值时,会自动将其转为对应的包装对象。


index.js

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<title>c</title>

</head>
<body>

<script type="text/javascript">
	var banana = 
		name: '香蕉',
		color: '黄色'
	
	var apple = 
		name: '苹果',
		color: '红色',
		fn: function() 
			console.log(`$this.name$this.color`)
		
	
	apple.fn()
	apple.fn.call(banana) //香蕉是黄色的
	var apple = 
		name: '苹果',
		color: '红色',
		fn: function(price, unit) 
			console.log(`$this.name$this.color的,$price元/$unit`)
		
	
	apple.fn.call(banana, 3.99, '斤') //香蕉是黄色的,3.99元/斤
	apple.fn.apply(banana, [3.99, '斤']) //香蕉是黄色的,3.99元/斤

	var fn2 = apple.fn.bind(banana, 3.99, '斤')
	fn2() //香蕉是黄色的,3.99元/斤
</script>

</body>
</html>

常见应用场景

查找数组最大元素

var a = [3, 9, 5, 3, 6]
console.log(Math.max.apply(null, a))   //9
console.log(Math.max.apply(undefined, a))   //9
console.log(Math.max.apply(NaN, a))   //9
console.log(Math.max.apply('', a))   //9
console.log(Math.max.call(null, ...a))   //9

将数组的空元素变为 undefined

var b = [3, ,8 , , 4, 7]
console.log(Array.apply(null, b))   //[3, undefined, 8, undefined, 4, 7]
console.log(Array.call(null, ...b))   //[3, undefined, 8, undefined, 4, 7]

调用对象的原生方法

Object 的 hasOwnProperty() 方法返回一个布尔值,判断对象是否包含特定的自身(非继承)属性。

如果某个对象中重写了 hasOwnProperty 方法,那我们再使用 hasOwnProperty() 是无法得到正确的结果的。

var obj = 
console.log(obj.hasOwnProperty('name'))   //false

obj.hasOwnProperty = function()
	return true

console.log(obj.hasOwnProperty('name'))   //true
console.log(Object.prototype.hasOwnProperty.call(obj, 'name'))   //false

在上面的代码中,在 obj 对象中使用原生的 Object.prototype.hasOwnProperty 方法可以避开使用被覆盖的 hasOwnProperty 。

对于许多原生的方法,我们都可以使用 call、apply、bind 来得到同样的结果,他们是等价的,就像下面这样:

[1, 2, 3].slice(0, 1)  //[1]
Array.prototype.slice.call([1, 2, 3], 0, 1)    //[1]

使用 ES6 手写 call、apply、bind

function add(c, d) 
	return this.a + this.b + c + d;

const obj =  a: 1, b: 2 ;

// ES6 call 实现
Function.prototype.es6call = function (context, ...rest) 
  var context = context || window;
  context.fn = this;
  const result = context.fn(...rest);
  delete context.fn;
  return result;

console.log(add.es6call(obj, 3, 4));  //10

context 代表接收的上下文环境,...rest 接收的其他参数。

1 若 context 为空值,则默认为 window。

2 将被调用对赋值给 context.fn,此时 context.fn 中可以访问到 context 中的其他属性,context.fn 中的 this 不再指向它的原对象,而是新的对象 context。

3 context.fn(…rest) 将其他的参数传入函数中执行得到结果。

Function.prototype.es6apply = function(context, arr)
  var context = context || window
  context.fn = this
  const result =  context.fn(...arr)
  delete context.fn
  return result

​
console.log(add.es6apply(obj, [3, 4]));  //10

apply 的实现与 call 的实现几乎相同,唯一区别就是接受参数的形式不同,一个是多个参数形式,一个是把多个参数转为一个数组的形式。

Function.prototype.es6bind = function(context, ...rest) 
  var self = this;
  return function fn(...args) 
  var result =  this instanceof fn ? new self(...rest, ...args) : self.apply(context, rest.concat(args))
    return result
  

bind 接受参数与 call 相同,返回结果为一个函数。

以上是关于JavaScript 中改变 this 的指向的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript-改变this指向

Javasript中this指向问题和改变this指向的方法

JavaScript 中call() apply() bind()改变this指向理解

JavaScript中改变this的指向方法(call和apple)

改变函数内部 this 指向丨JavaScript 函数进阶

改变javascript函数内部this指针指向的三种方法