前端中的this,bind,call,apply

作者: 调皮的绅士 | 来源:发表于2019-04-09 17:46 被阅读3次

记不清这是第多少次在总结这个问题了,前端就是这样,知识点繁星漫天,每颗细细研究都有点意思,不过我最终能记住的也就只有那个勺子而已。有些东西时不时就会忘记需要不断巩固记忆。

关于this的指向问题

总结:JavaScript 函数中的 this 指向并不是在函数定义的时候确定的,而是在调用的时候确定的。换句话说,函数的调用方式决定了 this 指向。

javaScript 中,普通的函数调用方式有三种:直接调用方法调用new 调用。除此之外,还有一些特殊的调用方式,比如通过 bind() 将函数绑定到对象之后再进行调用、通过 call()、apply() 进行调用等。而 es6 引入了箭头函数之后,箭头函数调用时,其 this 指向又有所不同。下面就来分析这些情况下的 this 指向。

  1. 直接调用函数:this 指向window
function test () {
  console.log(this)
}

test()
// 或者
(function(){
  test()
})()
  1. 方法调用:this 指向调用对象。
var obj = {
   name: 'zhenganlin',
   say: function() {
        console.log(this.name)
    }
}
obj.say() // 'zhenganlin'
  1. new 调用:this 指向生成后的实例
function Cat () {
  this.name = '大能猫';
  this.say = function(){
    console.log(this.name)
  }
  console.log(this)
}

4.bind、call、apply这三个方法都能改变this的指向,将 this 绑定到他们的第一个参数上。

let a = {
  name: '小a',
  say: function () {
      console.log(this.name)    
  }
}
let b = {name: '甄帅帅'}

a.say.call(b)    // 甄帅帅
a.say.apply(b) // 甄帅帅
a.say.bind(b)();    // 甄帅帅
  1. 箭头函数的方式调用:this等于定义时,函数外面的this。也可以理解成箭头函数中没有this,直接是用的定义时函数外面的this。
const obj = {
   test() {
       const arrow = () => {
           // 这里的 this 是 test() 中的 this,
           // 由 test() 的调用方式决定
           console.log(this === obj);
       };
       arrow();
   },
 
   getArrow() {
       return () => {
           // 这里的 this 是 getArrow() 中的 this,
           // 由 getArrow() 的调用方式决定
           console.log(this === obj);
       };
   }
  };
  obj.test();     // true

  const arrow = obj.getArrow();
  arrow();        // true


call、apply、bind的比较

1、apply 、 call 、bind 三者都是用来改变函数的this对象的指向的;
2、apply 、 call 、bind 三者第一个参数都是this要指向的对象,也就是想指定的上下文;
3、apply 、 call 、bind 三者都可以利用后续参数传参;
4、bind 是返回对应函数,便于稍后调用;apply 、call 则是立即调用 。

apply、call 的应用。

1、比较大小

var  numbers = [5, 458 , 120 , -215 ]; 
var maxInNumbers = Math.max.apply(Math, numbers),   //458

2.验证是否是数组

Object.prototype.toString.call(obj) === '[object Array]' ;

3.类数组变数组

var domNodes = Array.prototype.slice.call(document.getElementsByTagName("*"));

4.切换传参的方式

// 普通重写log函数
function log(){
  console.log.apply(console, arguments);
};

// 添加打印的前缀
function log(){
  var args = Array.prototype.slice.call(arguments);
  args.unshift('(app)');
 
  console.log.apply(console, args);
};

bind的用法

bind()的另一个最简单的用法是使一个函数拥有预设的初始参数。只要将这些参数(如果有的话)作为bind()的参数写在this后面。当绑定函数被调用时,这些参数会被插入到目标函数的参数列表的开始位置,传递给绑定函数的参数会跟在它们后面。

  1. bind 用来预设参数
// 只要将这些参数(如果有的话)作为bind()的参数写在this后面。当绑定函数被调用时,这些参数会被插入到目标函数的参数列表的开始位置,传递给绑定函数的参数会跟在它们后面。
function list() {
  return Array.prototype.slice.call(arguments);
}

var list1 = list(1, 2, 3); // [1, 2, 3]

// 预定义参数37
var leadingThirtysevenList = list.bind(undefined, 37);

var list2 = leadingThirtysevenList(); // [37]
var list3 = leadingThirtysevenList(1, 2, 3); // [37, 1, 2, 3]
  1. 在settimeout中绑定 当前this值
function Bloomer() {
  this.petalCount = Math.ceil(Math.random() * 12) + 1;
}

// 1秒后调用declare函数
Bloomer.prototype.bloom = function() {
  window.setTimeout(this.declare.bind(this), 100);
};

Bloomer.prototype.declare = function() {
  console.log('我有 ' + this.petalCount + ' 朵花瓣!');
};

var bloo = new Bloomer();
bloo.bloom(); //我有 5 朵花瓣!
  1. bind 简化将类数组结构转化成数组
// 之前的方法
var domNodes = Array.prototype.slice.call(document.getElementsByTagName("*"));
// 使用 bind 之后
var unboundSlice = Array.prototype.slice;
var slice = Function.prototype.call.bind(unboundSlice);
slice(arguments);

bind 的实现原理(上面的几个小节可以看出bind()有很多的使用场景,但是bind()函数是在 ECMA-262 第五版才被加入;它可能无法在所有浏览器上运行。这就需要我们自己实现bind()函数了)

if (!Function.prototype.bind) {
  Function.prototype.bind = function(oThis) {
    if (typeof this !== 'function') {
      // closest thing possible to the ECMAScript 5
      // internal IsCallable function
      throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
    }

    var aArgs   = Array.prototype.slice.call(arguments, 1),
        fToBind = this,
        fNOP    = function() {},
        fBound  = function() {
          // this instanceof fBound === true时,说明返回的fBound被当做new的构造函数调用
          return fToBind.apply(this instanceof fBound
                 ? this
                 : oThis,
                 // 获取调用时(fBound)的传参.bind 返回的函数入参往往是这么传递的
                 aArgs.concat(Array.prototype.slice.call(arguments)));
        };

    // 维护原型关系
    if (this.prototype) {
      // Function.prototype doesn't have a prototype property
      fNOP.prototype = this.prototype; 
    }
    // 下行的代码使fBound.prototype是fNOP的实例,因此
    // 返回的fBound若作为new的构造函数,new生成的新对象作为this传入fBound,新对象的__proto__就是fNOP的实例
    fBound.prototype = new fNOP();

    return fBound;
  };
}

相关文章

网友评论

    本文标题:前端中的this,bind,call,apply

    本文链接:https://www.haomeiwen.com/subject/mmfiiqtx.html