乐趣区

关于javascript:Bind

Bind

  • Function.prototype.bind()

题目 内容
Bind 的定义 什么是 Bind 函数?
Bind 的应用 如何应用 Bind 函数?
Bind 的实现原理 如何实现 Bind 函数?
Bind 的示例 Bind 函数实现继承等

Bind 的定义

  • bind()办法创立一个 新的函数 ,在bind() 被调用时,这个新函数的 this 被指定为 bind()第一个参数 ,而 其余参数将作为新函数的参数,供调用时应用。
const jackdan = {
  age: 27,
  height: 185,
  getAge: function() {return this.age;},
  getHeight: function() {return this.height;}
}

const age = jackdan.getAge;
console.log(age()); // The function gets invoked at the global scope
// undefined

const age1 = jackdan.getAge();
console.log(age1);
// 27

const jackdanAge = age.bind(jackdan);
console.log(jackdanAge());
// 27

语法

function.bind(thisArg[, arg1[, arg2[, ...]]]);

参数阐明

thisArg

  • 调用 绑定函数 时作为 this 参数传递给 指标函数 的值。如果应用 new 运算符结构绑定函数,则疏忽该值 (为什么?)。当应用bindsetTimeout中创立一个函数 (作为回调提供) 时,作为 thisArg 传递的任何原始值都转换为 object。如果bind 函数的参数列表为空,或者 thisArgnull或者 undefined,执行作用域的this 将被视为新函数的thisArg
  • 为什么在 new 运算符结构绑定函数时就疏忽 thisArg 了?这其实依据 new 运算符自身的个性无关,new运算符的个性如下:
1. 创立一个空的简略 JavaScript 对象(即{});2. 为步骤 1 新创建的对象增加属性 `__proto__`,将该属性链接至构造函数的原型对象;3. 将步骤 1 新创建的对象作为 `this` 的上下文;4. 如果该函数没有返回对象,则返回 `this`。
  • bind 的作用: 创立一个新的函数 (咱们其实也能够称 —— 创立调用该函数的正本),然而将this 的值设置为 传递给它的第一个参数 也就是thisArgs
  • new 的作用: 将 this 的值设置为 新创建的空对象
  • 因而,如果应用 new 尝试调用与 this 关键字的值绑定的函数,则 new关键字将笼罩 bind 并将 this 设置为空对象
function A() {// console.log(this);
  console.log('test');
}

var a = new A();
// 输入 test
  • 原生实现:
function A() {console.log('test');
}

function New(fn) {var obj = {};
  obj.__proto__ = fn.prototype;
  fn.apply(obj, arguments);
  return obj;
}

var a = New(A);
// 输入 test
  • new具体的介绍参考![new]()。

arg1, arg2, …

  • 指标函数 被调用时,被预置入 绑定函数 的参数列表中的参数。

返回值

  • 返回一个 原函数的拷贝 ,并领有指定的this 值和 初始参数

bind()形容

  • bind()函数会创立一个 新的绑定函数 (bound function, BF)。绑定函数是一个exotic function object(怪异函数对象),它包装了原函数对象。调用 绑定函数 通常会导致执行 包装函数
  • 绑定函数 具备以下外部属性:
1. [[BoundTargetFunction]] - 包装的函数对象
2. [[BoundThis]] - 在调用包装函数时始终作为 `this` 值传递得值。3. [[BoundArguments]] - 列表,在对包装函数做任何调用都会优先用列表元素填充参数列表。4. [[Call]] - 执行与此对象关联的代码。通过函数调用表达式调用。外部办法的参数是一个 `this` 值和一个蕴含通过调用表达式传递给函数的参数的列表。
  • 当调用绑定函数时,它调用 [[BoundTargetFunction]] 上的外部办法 [[Call]],就像这样 Call(boundThis, args)。其中,boundThis[[BoundThis]]args[[BoundArguments]]加上通过函数调用传入的参数列表。
  • 绑定函数也能够应用 new 运算符结构,它会体现为 指标函数曾经被构建结束了似的 。提供的this 值会被疏忽,但 前置参数仍会提供给模仿函数
// 心愿能够认真跑下这个实例,十分值得钻研和重复验证
function myFunc() {
  console.log('this=', this.name,
    'constructor=', this.constructor.name, 
    this.constructor === myFunc)
}

myFunc.prototype.name = "PROTOTYPE";
let myObj = {name: 'jackdan'};

myFunc();

// let myFunc1 = new myFunc();
// var bound = myFunc.bind({name: 'jackdan'});
myObj.f = myFunc; 
myObj.f();

var func = new myFunc();
var bound1 = myFunc.bind({name: 'jackdan'});

Bind 的应用

创立绑定函数

  • bind()最简略的用法是 创立一个函数 ,不论怎么调用, 这个函数都有同样的 this
  • JavaScript 老手常常犯的一个谬误是 将一个办法从对象中拿进去 而后再调用 ,冀望办法中的this 是原来的对象 (比方在 回调中传入这个办法)。
  • 如果不做非凡解决的话,个别会失落原来的对象。基于这个函数,用原始的对象创立一个绑定函数,奇妙地解决了这个问题:
// 非严格模式下
this.age = 30;
var obj = {
  age: 27,
  getAge: function() {return this.x;}
}

obj.getX(); // 27
var totalX = obj.getX;totalX(); // 返回 30 - 因为函数是在全局作用域中调用的

// 创立一个新函数,把 'this' 绑定到 obj 对象
// 老手可能会将全局变量 age 与 obj 的属性 age 混同
var boundX = totalX.bind(obj);
boundX(); // 27

偏函数

  • bind()的另一个最简略的用法是使一个函数领有预设的初始参数。只有将这些参数 (如果有的话) 作为 bind() 的参数写在 this 前面。当绑定函数被调用时,这些参数会被插入到指标函数的参数列表的开始地位,传递给绑定函数的参数会跟在它们前面。
function toArray() {return Array.prototype.slice.call(arguments);
}

function addArguments(arg1, arg2) {return arg1 + arg2;}

var argList = toArray(1, 2, 3); // [1, 2, 3]
var result1 = addArguments(1, 2); // 3

// 创立一个函数,它领有预设参数列表。// 创立一个函数,它领有预设参数列表。var leadingThirtysevenList = toArray.bind(null, 37);

// 创立一个函数,它领有预设的第一个参数
var addThirtySeven = addArguments.bind(null, 37);

var list2 = leadingThirtysevenList();
// [37]

var list3 = leadingThirtysevenList(1, 2, 3);
// [37, 1, 2, 3]

var result2 = addThirtySeven(5);
// 37 + 5 = 42

var result3 = addThirtySeven(5, 10);
// 37 + 5 = 42,第二个参数被疏忽

配合 setTimeout

  • 在默认状况下,应用 window.setTimeout() 时,this关键字会指向 window(或者 global) 对象。当类的办法中须要 this 指向类的实例时,你可能须要显式地把 this 绑定到回调函数,就不回失落该实例的援用。
function LateBloomer() {this.petalCount = Math.ceil(Math.random() * 12) + 1;
}

// 在 1 秒钟后申明 bloom
LateBloomer.prototype.bloom = function() {window.setTimeout(this.declare.bind(this), 1000);
};

LateBloomer.prototype.declare = function() {
  console.log('I am a beautiful flower with' +
    this.petalCount + 'petals!');
};

var flower = new LateBloomer();
flower.bloom();  // 一秒钟后, 调用 'declare' 办法

作为构造函数应用的绑定函数

  • 绑定函数主动适应于 应用 new 操作符去结构一个由指标函数创立的新实例 。当一个绑定函数是用来 构建一个值的 ,原来提供的this 就会被疏忽。不过 提供的参数列表依然会插入到结构函数调用时的参数列表之前(之前也有验证)。

备注: 这部分演示了 JavaScript 的能力并且记录了 bind() 的超前用法。以下展现的办法并不是最佳的解决方案,且可能不应该用在任何生产环境中。

function Point(x, y) {
  this.x = x;
  this.y = y;
}

Point.prototype.toString = function() {return this.x + ',' + this.y;};

var p = new Point(1, 2);
p.toString(); // '1,2'

var emptyObj = {};
var YAxisPoint = Point.bind(emptyObj, 0/*x*/);

// 下方的 polyfill 不反对运行这行代码,// 但应用原生的 bind 办法运行是没问题的:var YAxisPoint = Point.bind(null, 0/*x*/);

/*(译注:polyfill 的 bind 办法中,如果把 bind 的第一个参数加上,即对新绑定的 this 执行 Object(this),包装为对象,因为 Object(null) 是 {},所以也能够反对)*/

var axisPoint = new YAxisPoint(5);
axisPoint.toString(); // '0,5'

axisPoint instanceof Point; // true
axisPoint instanceof YAxisPoint; // true
new YAxisPoint(17, 42) instanceof Point; // true
  • 请留神,如果不须要做特地的解决就能够 创立一个和 new 操作符一起应用的绑定函数 。也就是说,你不须要 做特地解决就能够创立一个能够被间接调用的绑定函数 ,即便你更心愿绑定函数是用new 操作符来调用。
function Point(x, y) {
  this.x = x;
  this.y = y;
}

Point.prototype.toString = function() {return this.x + ',' + this.y;};

var p = new Point(1, 2);
p.toString(); // '1,2'

var emptyObj = {};
var YAxisPoint = Point.bind(emptyObj, 0/*x*/);

// 下方的 polyfill 不反对运行这行代码,// 但应用原生的 bind 办法运行是没问题的:var YAxisPoint = Point.bind(null, 0/*x*/);

/*(译注:polyfill 的 bind 办法中,如果把 bind 的第一个参数加上,即对新绑定的 this 执行 Object(this),包装为对象,因为 Object(null) 是 {},所以也能够反对)*/

var axisPoint = new YAxisPoint(5);
axisPoint.toString(); // '0,5'

axisPoint instanceof Point; // true
axisPoint instanceof YAxisPoint; // true
new YAxisPoint(17, 42) instanceof Point; // true

// 依然能作为一个一般函数来调用
// (即便通常来说这个不是被冀望产生的)
YAxisPoint(13);
emptyObj.x + ',' + emptyObj.y; // '0, 13'
  • 如果你心愿一个绑定函数要么 只能用 new 操作符 ,要么只能间接调用,那你必须在 指标函数上显式规定这个限度

快捷调用

  • 在你想要为一个须要特定的 this 值的函数创立一个捷径 (shortcut) 的时候,bind()也很好用。
  • 你能够用 Array.prototype.slice 来将一个相似于数组的对象 (array-like object) 转换成一个真正的数组,就拿它来举例子吧。你能够简略地这样写:
var slice = Array.prototype.slice;

// ...

slice.apply(arguments);
  • bind() 能够使这个过程变得简略。在上面这段代码外面,sliceFunction.prototype.apply() 办法的绑定函数,并且将 Array.prototypeslice()办法作为 this 的值。这意味着咱们压根儿用不着下面那个 apply() 调用了。
// 与前一段代码的 "slice" 成果雷同
var unboundSlice = Array.prototype.slice;
var slice = Function.prototype.apply.bind(unboundSlice);

// ...

slice(arguments);

Bind 的实现不反对 new

// Does not work with `new (funcA.bind(thisArg, args))`
if (!Function.prototype.bind) (function() {
  var slice = Array.prototype.slice;
  Function.prototype.bind = function() {var thatFunc = this, thatArg = arguments[0];
    var args = slice.call(arguments, 1);
    if (typeof thatFunc !== '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');
    }
    return function(){var funcArgs = args.concat(slice.call(arguments))
      return thatFunc.apply(thatArg, funcArgs);
    };
  };
})();
  • 你能够将这段代码插入到你的脚本结尾,从而使你的 bind() 在没有内置实现反对的环境中也能够局部地应用bind

Bind 的实现反对 new

Function.prototype.myBind = function(context) {
  var self = this;
  var args = Array.prototype.slice.call(arguments, 1);

  var fTemp = function() {};

  var fBound = function() {var bindArgs = Array.prototype.slice.call(arguments);
    return self.apply(this instanceof fTemp ? this : context, args.concat(bindArgs));
  }

  fTemp.prototype = this.prototype;
  fBound.prototype = new fTemp();
  return fBound;
}

var jackdan = {
  age: 27,
  height: 185,
  getAge: function() {return this.age;},
  getHeight: function() {return this.height;}
}

var height = jackdan.getHeight;
var jackdanHeight = height.bind(jackdan);
console.log(jackdanHeight());
// 185

https://developer.mozilla.org…

退出移动版