call、apply、bind之间的区别与应用


一、this的绑定规则

this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,实际上this的最终指向的是那个调用它的对象(箭头函数不一样,箭头函数没有自己的this, 它的this是继承而来; 默认指向在定义它时所处的对象(宿主对象))
以下是this绑定的五种规则
1、默认绑定(严格/非严格模式)
2、隐式绑定
3、显式绑定
4、new绑定
5、箭头函数绑定

1.默认绑定(严格和非严格模式)

独立函数调用时,此时this绑定采用默认绑定,this指向window
严格模式下,不能将全局对象用于默认绑定,this会绑定到undefined。只有函数运行在非严格模式下,默认绑定才能绑定到全局对象。

var a = "window";

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

foo()//此时输出的值为:window
"use strict";
var a = "window";

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

foo()//报错,Uncaught TypeError: Cannot read property 'a' of undefined

2.隐式绑定

当函数引用有上下文对象时,隐式绑定规则会把函数中的this绑定到这个上下文对象。对象属性引用链中只有上一层或者说最后一层在调用中起作用。

function fn() {
	console.log(this.a);
}

var obj = {
	a:100,
	fn:fn
}
obj.fn(); //输出结果是100

3.显示绑定

通过call(…) 或者 apply(…)方法。第一个参数是一个对象,在调用函数时将这个对象绑定到this。因为直接指定this的绑定对象,称之为显示绑定。

function fn() {
	console.log(this.a)
}

var obj = {
	a:100
}

fn.call(obj); //100
fn.apply(obj); //100

4.new绑定

在JS中,构造函数只是使用new操作符时被调用的普通函数,他们不属于某个类,也不会实例化一个类。
包括内置对象函数(比如Number(…))在内的所有函数都可以用new来调用,这种函数调用被称为构造函数调用。
实际上并不存在所谓的“构造函数”,只有对于函数的“构造调用”。

使用new来调用函数,或者说发生构造函数调用时,会自动执行下面的操作。

1、创建(或者说构造)一个新对象。
2、这个新对象会被执行[[Prototype]]连接。
3、这个新对象会绑定到函数调用的this。
4、如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象。
使用new来调用foo(…)时,会构造一个新对象并把它(bar)绑定到foo(…)调用中的this。

function fn(a) {
	this.a = a;
}

var obj = new fn(a);

console.log(obj.a)

5.箭头函数绑定

其实大部分情况下可以用一句话来概括,this总是指向调用该函数的对象。

但是对于箭头函数并不是这样,是根据外层(函数或者全局)作用域(词法作用域)来决定this。

看一下例子:
普通函数:

var a = "window"

var obj1 = {
	a:"obj1",
	fn(){
		console.log(this.a)
	}
}
var obj2 = {
	a:"obj2"
}

obj1.fn()  //输出为obj1
obj1.fn.call(obj2) //输出为obj2,call改变了this的指向,指向obj2

箭头函数

var a = "window"

var obj1 = {
	a:"obj1",
	fn: () => {
		console.log(this.a)
	}
}
var obj2 = {
	a:"obj2"
}

obj1.fn() //输出为window
obj1.fn.call(obj2) //输出为window,call无法改变箭头函数的this的指向

对于箭头函数的this总结如下:

箭头函数不绑定this,箭头函数中的this相当于普通变量。

箭头函数的this寻值行为与普通变量相同,在作用域中逐级寻找。

箭头函数的this无法通过bind,call,apply来直接修改(可以间接修改,改变作用域中this的指向可以改变箭头函数的this。)。

二、call、apply、bind的区别

call()和apply()和bind()共同点都是可以改变this指向

call和apply的区别在于两者接受参数不同,call()接受的是一个个的参数,而apply()接受的是一个包含多个参数的数组

看一下例子:

var fn = function(a,b,c){
	...
};
var obj = {...};

fn.call(obj,a,b,c);  //call,参数列表
fn.apply(obj,[a,b,c]) //apply,参数为数组

三、call、apply、bind的实现

1.call

Function.prototype.Mycall = function(){
  let [obj,...args] = [...arguments];
  if(!obj){
    obj = typeof window === "undefine"?global:window;
  }
  obj.fn = this;
  let result = obj.fn(...args);
  delete obj.fn;
  return result;
}

2.apply

Function.prototype.Myapply = function(){
  let [obj,args] = [...arguments];
  if(!obj){
    obj = typeof window === "undefine"?global:window;
  }
  obj.fn = this;
  var result;
   if(args){
     if(!Array.isArray(args)){
       throw new Error("参数应为数组")
     }else{
       result = obj.fn(...args);
     }
   }else{
     result = obj.fn();
   }
  delete obj.fn;
  return result;
}

3.bind

```Function.prototype.Mybind = function () {
  let [ctx,...args] = [...arguments];
  const self = this;
  return function (...args2) {
      return self.apply(ctx, [...args, ...args2])
  }
}

总结

本文仅供自己学习保留

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值