当前位置: 首页 > news >正文

Brief introduction of how to 'Call, Apply and Bind'

关于 this

在绝大多数情况下,函数的调用方式决定了this的值。this不能在执行期间被赋值,并且在每次函数被调用时this的值也可能会不同。

全局 this

window.something = 'I love JavaScript'
console.log(this.something) // 'I love JavaScript'
console.log(window === this) // true

调用全局 function

var a = 1
function test() { console.log(this.a) }
test() // 1 - still remains the window reference

调用对象中的 function

this.a = 'I am in the global scope'
function Test() {
  this.a = 'I am in the test scope'
  this.show = function() { console.log(this.a) }
}
Test.prototype.display = function () { console.log(this.a) }
var test = new Test() // updated the scope of this
test.show() // I am in the test scope
test.display() // I am in the test scope 

关于 call / apply

JavaScript 内部提供了一种机制,让我们可以自行手动设置 this 的指向。它们就是 call 与 apply。所有的函数都具有着两个方法。它们除了参数略有不同,其功能完全一样。它们的第一个参数都为 this 将要指向的对象。

一个最简单的继承

function Laptop(name, storage) {
  this.name = name
  this.storage = storage
}

function Dell(name, storage, company) {
  Laptop.call(this, 'Dell', 1024)
  this.company = company
}

console.log(new Dell('Dell', 1024, 'Dell Inc').storage)

改变 this

var obj = {
  entry: 'mammals-banana-tower',
  duration: 0
}

function breed(name) {
  console.log('Show this breeding info', name, this.entry, this.duration)
  console.log(this === obj)
}

breed() // this => window
breed.call(obj, 'Frank') // this => obj

注:当没有传递任何参数作为 call() 的第一个参数时,在非严格模式下,this 会指向 window。

实现一个简单的 call

var _call = function (that) {
  that = that ? Object(that) : window
  that.func = this

  function formatArgs(oArgs, sign) {
    var _args
    for (var i = 1, len = oArgs.length; i < len; i++) {
      _args.push(sign ? ('_param_' + i) : oArgs[i])
    }
    return _args
  }

  var args = formatArgs(arguments)
  var newFunc = (new Function('args', 'return that.func(' + formatArgs(args, true).toString() + ')'))(args)

  that.func = null
  return newFunc
}

关于 bind

() => {} 和 bind this

用过 React 的同学都知道,当使用 class component 时,需要在 constructor 绑定当前的成员函数,或者针对事件委托的情况下,也需要进行绑定;ES6 箭头函数可以让我们更专注于具体的实现逻辑,简化了 this 操作

// ES5
// <a onclick={this.handleClick.bind(this)}></a>
// constructor() { this.handleClick = this.handleClick.bind(this) }

// ES6
// <a onclick={() => handleClick()}></a>
// handleClick = () => {}

无效的 re-bound

var f = function() { console.log(this.text) }
f = f.bind({ text: 'I was bound' }).bind({ text: 'I won't be bound' })
f() // I was bound

很容易发现,f.bind() 返回的绑定函数对象仅在创建是保留当前的上下文(或者传入的参数),因此无法在第二次进行重绑定。

一个相对完善的 bind

var _bind = function (that) {

  var fBound,
    target = this,
    slice = Array.prototype.slice,
    toStr = Object.prototype.toString,
    args = slice.call(arguments, 1); // except that

  if (typeof target !== 'function' || toStr.call(target) !== '[object Function]') {
    throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
  }

  var binder = function () {
    var oArgs = args.concat(slice.call(arguments))
    if (this instanceof fBound) {
      var result = target.apply(this, oArgs);
      return Object(result) === result ? result : this;
    } else {
      return target.apply(that, oArgs);
    }
  };

  var i = 0,
    params = [],
    paramLength = Math.max(0, target.length - args.length);

  for (; i < paramLength; i++) {
    params.push('_param_' + i);
  }

  fBound = (new Function(
    'binder',
    'return function(' + params.join(',') + ') { return binder.apply(this,arguments); }'
  ))(binder);

  // maintain the reference of prototype
  if (target.prototype) {
    var fNOP = function () { };
    fNOP.prototype = target.prototype;
    fBound.prototype = new fNOP();
    fNOP.prototype = null;
  }

  return fBound;
};

参考

https://developer.mozilla.org...
https://developer.mozilla.org...
https://developer.mozilla.org...
https://developer.mozilla.org...
https://developer.mozilla.org...
https://developer.mozilla.org...
https://www.ecma-internationa...
https://javascript.info/bind
https://juejin.im/post/5c0605...
https://github.com/mqyqingfen...

相关文章:

  • CSS样式:覆盖规则
  • 猴子数据域名防封接口降低小说被封的风险
  • hadoop搭建之hive安装
  • 构造函数(constructor)与原型链(prototype)关系
  • css3省略……
  • ASP.NET MVC从视图传参到控制器的几种形式
  • Nginx 通过 Lua + Redis 实现动态封禁 IP
  • 前端性能优化--懒加载和预加载
  • 如何封装使用api形式调用的vue组件
  • 小白修神传~程序员成神之路
  • 26、springboot与消息
  • Javascript基础之Array数组API
  • redhat6.4提权Ⅱ
  • 运算符和自增自减
  • ☆1033
  • 【391天】每日项目总结系列128(2018.03.03)
  • 【翻译】babel对TC39装饰器草案的实现
  • Babel配置的不完全指南
  • docker-consul
  • HTTP那些事
  • JavaScript 是如何工作的:WebRTC 和对等网络的机制!
  • javascript 总结(常用工具类的封装)
  • Linux快速配置 VIM 实现语法高亮 补全 缩进等功能
  • Puppeteer:浏览器控制器
  • Transformer-XL: Unleashing the Potential of Attention Models
  • vue从入门到进阶:计算属性computed与侦听器watch(三)
  • 大快搜索数据爬虫技术实例安装教学篇
  • 等保2.0 | 几维安全发布等保检测、等保加固专版 加速企业等保合规
  • 更好理解的面向对象的Javascript 1 —— 动态类型和多态
  • 树莓派 - 使用须知
  • 我从编程教室毕业
  • 移动端解决方案学习记录
  • 交换综合实验一
  • ​Java并发新构件之Exchanger
  • ​一、什么是射频识别?二、射频识别系统组成及工作原理三、射频识别系统分类四、RFID与物联网​
  • (04)odoo视图操作
  • (11)MATLAB PCA+SVM 人脸识别
  • (arch)linux 转换文件编码格式
  • (Java实习生)每日10道面试题打卡——JavaWeb篇
  • (大众金融)SQL server面试题(1)-总销售量最少的3个型号的车及其总销售量
  • (附源码)springboot炼糖厂地磅全自动控制系统 毕业设计 341357
  • (附源码)springboot码头作业管理系统 毕业设计 341654
  • (教学思路 C#之类三)方法参数类型(ref、out、parmas)
  • (每日持续更新)jdk api之FileReader基础、应用、实战
  • (十三)Maven插件解析运行机制
  • (转)自己动手搭建Nginx+memcache+xdebug+php运行环境绿色版 For windows版
  • .net 4.0 A potentially dangerous Request.Form value was detected from the client 的解决方案
  • .net FrameWork简介,数组,枚举
  • .NET/C# 使窗口永不获得焦点
  • .net6Api后台+uniapp导出Excel
  • @RequestBody与@ModelAttribute
  • @RequestMapping用法详解
  • [ 常用工具篇 ] AntSword 蚁剑安装及使用详解
  • []利用定点式具实现:文件读取,完成不同进制之间的
  • [Android]竖直滑动选择器WheelView的实现