JavaScript中this的指向问题

文章来源:https://juejin.cn/post/6973181948327903245

https://segmentfault.com/a/1190000011817793?utm_source=tag-newest

箭头函数?

var person = {
  name:'张三',
  age:18,
  getName:function(){
     console.log('我的名字是:'+this.name)
  },
  getAge:()=>{
     console.log('我的年龄是:'+this.age)
  }
}

person.getName()
person.getAge()

上述代码的打印结果是:

person.getName() // 我的名字是张三
person.getAge()  // 我的年龄是undefined

person.getName()中的this指向函数的调用者,也就是person实例,因此this.name="张三";

getAge()通过箭头函数定义,箭头函数是没有自己的this的,会继承父作用域的this,因此person.getAge()执行的时候,此时的作用域指向window,window没有定义age属性,所以打印结果是undefined。

从例子可以得出:对象中定义的函数使用箭头函数是不合适的

为什么箭头函数不能作为构造函数

// 构造函数生成实例的过程
function Person(name,age){
  this.name = name
  this.age = age
}
var p = new Person('张三',18)

//new关键字生成实例过程如下
// 1. 创建空对象p
var p = {} 
// 2. 将空对象p的原型链指向构造器Person的原型
p.__proto__ = Person.prototype
// 3. 将Person()函数中的this指向p
// 若此处Person为箭头函数,而没有自己的this,call()函数无法改变箭头函数的指向,也就无法指向p。
Person.call(p) 

箭头函数其他需要注意的点:

(1)不支持call、apply函数特性

call()/apply()函数的作用是改变被调用函数中this的指向。但是箭头函数没有自己的this,而是继承父作用域中的this,所以这两函数没法改变箭头函数的指向。

var Person = {
  name:'张三',
  getName:function(arg){
    let fun = v=>v+this.name
    let boy  = {
      name:'李四'
    }
    // call函数绑定boy实例
    return fun.call(boy,arg)
  }
}

Person.getName('我是') // 我是张三

例子中,倘若箭头函数执行fun.call(b,arg)成功改变this的指向,此时应当输出的是:“我是李四”,但实际上输出的是:“我是张三”,由此可以call函数并没有成功改变箭头函数fun内部this的指向。

(2)不绑定arguments

var fun = ()=>{
   console.log(arguments)
}

fun(1) // 报错:ReferenceError: arguments is not defined

// 解决办法
var fun = (...args)=>{
  console.log(args)
}
fun(1)  // 输出:[1]

(3)没有prototype属性

var fun = ()=>{}
fun.prototype // undefined

(4)原型函数不能定义成箭头函数

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

// 原型函数使用箭头函数,其中的this指向全局对象,而不会指向构造函数
// 因此访问不到构造函数本身,也就访问不到实例属性
Person.prototype.say = ()=>{console.log(this.name)}

箭头函数体内的this对象,如果包裹在函数中就是函数调用时所在的对象,如果放在全局中就是指全局对象window。并且固定不会更改。换句话说内部的this就是外层代码块的this。 

ES5中的this指针?

按照this指针的优先级,列出下面常会遇到的四种情况,从上到下依次是优先级从高到低(后面会详细比较优先级)。

  1. 函数是和new一起被调用的吗(new绑定)?如果是,this就是新构建的对象。

    var bar = new foo()

  2. 函数是用callapply被调用(明确绑定),甚至是隐藏在bind 硬绑定 之中吗?如果是,this就是明确指定的对象。

    var bar = foo.call( obj2 )

  3. 函数是用环境对象(也称为拥有者或容器对象)被调用的吗(隐含绑定)?如果是,this就是那个环境对象。

    var bar = obj1.foo()

  4. 否则,使用默认的this默认绑定)。如果在strict mode下,就是undefined,否则是global对象。 var bar = foo()

以上,就是理解对于普通的函数调用来说的this绑定规则所需的全部。是的···几乎是全部。

 call、apply、bind?

因为apply、call存在于Function.prototype中,所以每个方法都有这两个属性。

  • call
函数名.call(对象,arg1....argn)
//功能:
    //1.调用函数
    //2.将函数内部的this指向第一个参数的对象
    //3.将第二个及以后所有的参数,作为实参传递给函数
  • apply
    主要用途是直接用数组传参
函数名.apply(对象, 数组/伪数组);
//功能:
    //1.调用函数
    //2.将函数内部的this指向第一个参数的对象
    //3.将第二个参数中的数组(伪数组)中的元素,拆解开依次的传递给函数作为实参
//案例求数组中最大值
var a=Math.max.apply( null, [ 1, 2, 5, 3, 4 ] );
console.log(a);// 输出:5
  • bind
  1. 是创建一个新的函数,我们必须要手动去调用:
var a ={
  name : "Cherry",
  fn : function (a,b) {
   console.log( a + b)
  }
 }
 var b = a.fn;
 b.bind(a,1,2)()   // 3
  • 1.call和apply功能几乎一致,唯一的区别就是传参的方式!!
    2.call和apply的第一个参数如果为null或者undefined,那么this指向window
    3.call和apply的第一个参数如果为值类型的数据,那么会将这个值类型的数据,转换成其对应的引用类型的数据,然后再将this指向这个引用类型的数据
    4.call和apply立即执行这个函数,bind方法可以让对应的函数想什么时候调就什么时候调用,并且可以将参数在执行的时候添加,这是它们的区别,根据自己的实际情况来选择使用。
    5.当参数的个数确定的情况下可以使用call;当参数的个数不确定的情况下可以使用apply

apply应用

JavaScript

var array1 = [12 , "foo" , {name "Joe"} , -2458]; 
var array2 = ["Doe" , 555 , 100]; 
Array.prototype.push.apply(array1, array2); 
/* array1 值为  [12 , "foo" , {name "Joe"} , -2458 , "Doe" , 555 , 100] */

call应用(将伪数组转为数组)

var arrayLike = {0: 'name', 1: 'age', 2: 'sex', length: 3 }
Array.prototype.join.call(arrayLike, '&'); // name&age&sex
Array.prototype.slice.call(arrayLike, 0); // ["name", "age", "sex"] 
// slice可以做到类数组转数组
Array.prototype.map.call(arrayLike, function(item){
    return item.toUpperCase();
}); 
// ["NAME", "AGE", "SEX"]

call应用(判断复杂数据类型)

console.log(
    Object.prototype.toString.call(num),
    Object.prototype.toString.call(str),
    Object.prototype.toString.call(bool),
    Object.prototype.toString.call(arr),
    Object.prototype.toString.call(json),
    Object.prototype.toString.call(func),
    Object.prototype.toString.call(und),
    Object.prototype.toString.call(nul),
    Object.prototype.toString.call(date),
    Object.prototype.toString.call(reg),
    Object.prototype.toString.call(error)
);
// '[object Number]' '[object String]' '[object Boolean]' '[object Array]' '[object Object]'
// '[object Function]' '[object Undefined]' '[object Null]' '[object Date]' '[object RegExp]' '[object Error]'

ES6中的this指针? 

箭头函数体内的this对象,如果包裹在函数中就是函数调用时所在的对象,如果放在全局中就是指全局对象window。并且固定不会更改。换句话说内部的this就是外层代码块的this

下面是对比分析普通函数和箭头函数中this区别

// 普通函数
function foo() {
  setTimeout(function() {
    console.log('id:', this.id);
  });
}

var id = 21;
foo.call({ id: 42 }); //21
// 箭头函数
function foo() {
  setTimeout(() => {
    console.log('id:', this.id);
  }, 100);
}

var id = 21;
foo.call({ id: 42 }); //42
// 上面的匿名函数定义时所在的执行环境就是foo函数,所以匿名函数内部的this执向始终会和foo函数的this执向保持一致,不会更改,如同下面的这个案例
function foo() {
  setTimeout(() => {
    console.log('id:', this.id);
  }, 100);
}

var id = 21;
foo(); //21(没有用call)
// ES5普通函数模拟上面es6函数的执行过程
function foo() {
  var _this = this;

  setTimeout(function () {
    console.log('id:', _this.id);
  }, 100);
}

call的作用就是将foo函数的执行环境从window改成对象{id: 42}
定时器的作用就是延迟执行当前函数的外部执行环境,无论有没有设置延迟时间 

普通函数解释:定义时this指向函数foo作用域,但是在定时器100毫秒之后执行函数时,此时this指向window对象

箭头函数解释:this始终指向定义时所在对象,也就是始终指向foo作用域 

var handler = {
  id: '123456',

  init: function() {
    document.addEventListener('click',
      event => this.doSomething(event.type), false);
  },

  doSomething: function(type) {
    console.log('Handling ' + type  + ' for ' + this.id);
  }
};
handler.init()// Handlingclickfor123456

 箭头函数的this始终指向handler,如果是普通函数,this指向document

this指向的固定化,并不是因为箭头函数内部有绑定this的机制,实际原因是箭头函数根本没有自己的this导致内部的this就是外层代码块的this。正是因为它没有this,所以也就不能用作构造函数。

关于 this指向的问题,得出的结论是: this永远指向函数的调用者。但是在箭头函数中, this指向的是定义时所在的对象,而不是使用时所在的对象。换句话说, 箭头函数没有自己的this,而是继承父作用域中的this
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Delicia_Lani

你的鼓励将是我写作的动力。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值