JS 关于 this、new、bind、call、apply

一、关于 this 的指向

当一个函数被调用时,会创建一个执行上下文,它包含函数在哪里被调用(调用栈)、函数的调用方式、传入的参数等信息,this 就是这个记录的一个属性,它会在函数执行的过程中被用到。


一句话比较易懂的:this 指向最后调用它的那个对象。


this 的绑定规则:

  • 默认绑定
  • 隐式绑定
  • 显式绑定(硬绑定)
  • new 绑定

this 绑定的优先级:

this 根据优先级来确定指向。
优先级为:new 绑定 > 显示绑定 > 隐式绑定 > 默认绑定


默认绑定

通常是独立函数调用。

请看以下代码:

var name = "windowName";
function test() {
  var name = "zhangsan";
  console.log(this); // Window对象
  console.log(this.name); // windowName
}
test();
console.log(this); // Window对象

test() 实际是 window.test() ,省略了全局对象window 而已。test()window 调用,所以 this 指向 window。此处是默认绑定。
但是这里未使用严格模式。若使用严格模式,则全局对象是 undefined 是没有定义的,会报错。


隐式绑定

函数是否在某个上下文对象中调用,若是则 this 绑定的是那个上下文对象。

请再看以下代码:

var name = "windowName";
var obj = {
  name: "zhangsan",
  getName: function () {
    console.log(this.name)  // zhangsan
  }
}
obj.getName()

这里的函数,obj.getName() ,被 obj 调用,所以 this 指向 obj
当然,也可 obj 前面也省略了 window,加上是 window.obj.getName(),this 指向最后调用它的那个对象,所以 this 还是指向 obj。

若 obj 没有 name 属性呢? this.name 会打印出什么?
答案:会打印 undefined ,表示 this 指向最后调用它的对象,且不会向上层对象寻找属性。(即 this 仅仅寻找上下文)

注意下面这个例子:

var name = "windowName";
var obj = {
  name: "zhangsan",
  getName: function () {
    console.log(this.name)  // windowName
  }
}

var f = obj.getName;
console.log(f)  // ƒ () {...}
f() 

var f = obj.getNameobjgetName 赋值给了 f,也就是说,f 被赋值成了 函数,调用 f() ,就又是 window 了,而不是 obj 。所以此时 this 指向 window ,打印出 windowName


显式绑定

通过 call、apply、bind 可以修改函数绑定的 this,使其成为我们想指定的对象。通过这些方法的第一个参数我们可以显式地绑定 this。


new 绑定

函数如果作为构造函数使用 new 调用时, this 绑定的是新创建的构造函数的实例。

实际上使用 new 调用构造函数时,会依次执行下面的操作(即 new 的过程)

var yourfood = new Food("面包");
// 上行代码实际内部过程是下面:
new Food {
    var obj = {}
    obj.__proto__ = Food.prototype
    var result = Food.call(obj, "面包")
    return typeof result === 'obj'? result : obj
}

解析如下:

  • 创建一个新对象obj ;
  • 构造函数的 prototype 被赋值给这个新对象的 __proto__
  • 使用 call 将新对象赋给当前的 this 并 执行构造函数;
  • 如果构造函数若返回一个对象,则new 表达式中的函数调用返回该对象。若没有返回对象类型的值,则会返回这个新对象obj 。


二、改变 this 指向 的几种方法:

先看个问题:

var name = "windowName";
var obj = {
  name: "zhangsan",
  getName: function () {
    console.log(this.name); // 报错
  },

  waitGet: function () {
    setTimeout(function () {
      this.getName();
    }, 500);
  },
};

obj.waitGet()

其中 setTimeout 中回调没有使用箭头函数的情况下,因最后调用 setTimeout 的 是 window 所以 this 指向的 是 window ,而 window 中没有 getName() ,所以会报错!!

1、使用 箭头函数

箭头函数中没有 this 绑定,如果 箭头函数 被 非箭头函数 包裹,则 this 指向最近一层的非箭头函数的 this,否则,this 为 undefined

使用 箭头函数 解决上面代码报错问题:

var name = "windowName";
var obj = {
  name: "zhangsan",
  getName: function () {
    console.log(this.name); // zhangsan
  },

  waitGet: function () {
    setTimeout(() => {
      this.getName();
    }, 500);
  },
};

obj.waitGet();

2、使用 that = this

var name = "windowName";
var obj = {
  name: "zhangsan",
  getName: function () {
    console.log(this.name); // zhangsan
  },

  waitGet: function () {
    // 将 that 赋值为 obj对象
    var that = this;
    setTimeout(function () {
      // 此处用that就是obj调用getName。若用this,则在setTimeout中this指向window,而window无getName方法就会报错。
      that.getName();
    }, 500);
  },
};

obj.waitGet();

3、使用 call、apply、bind

var name = "windowName";
var obj = {
  name: "zhangsan",
  getName: function () {
    console.log(this.name); // zhangsan
  },

  waitGet: function () {
    setTimeout(
      function () {
        this.getName();
      }.call(obj),
      // }.apply(obj),
      // }.bind(obj)(),
      500
    );
  },
};

obj.waitGet();

call、apply、bind 的区别

都具有一个指定的 this 值,不同的是 call 接收若干个参数列表, apply 接收一个包含多个参数的数组。bind 接收若干个参数列表,但 不自动执行函数,需手动调用,其后加 ()。而 callapply 改变this指向后会 自动执行函数。

var obj = {
  getName: function (a, b) {
    console.log(a, b); // 1 2
  },
};
var f1 = obj.getName;
f1.call(obj, 1, 2);

var f2 = obj.getName;
f2.apply(obj, [1, 2]);

var f3 = obj.getName;
f3.bind(obj, 1, 2)();


例:

var food = {
  name: "张三",
  price: "5元",
  getPrice: function (type) {
    console.log(type, this.price);
  },
};

food.getPrice("面包"); // 面包 5元

// 改变 getPrice的this指向,指向传入的{ sex: "女", price: "10元" },所以this.price="10元"
var getPrice2 = food.getPrice.bind({ sex: "女", price: "10元" }, "烤肠");
getPrice2(); // 烤肠 10元
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

欢莱

为您解决问题,此项目我成功完成

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

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

打赏作者

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

抵扣说明:

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

余额充值