您的位置 首页 > 数码极客

bind、bind软件

bind 函数对于写react的人来说并不陌生。哦!是的,没错我的朋友,它的一个用处就是用来改变函数this指向的。如果细究一下bind的实现,发现里面还是有不少东西的,我们今天展开讨论一下。

在说bind之前呢,我们还要先来讲讲我们的老熟人this。说到this,我们在《前端面试之js相关问题(一)》也有提到过,this的工作方式。今天我们再来看看它的四种绑定规则

This的四种绑定规则

1.默认绑定

独立函数调用时,this指向全局对象,如果使用严格模式,那么全局对象无法使用默认绑定,this绑定至undefined并抛错(TypeError: this is undefined)

2.隐式绑定

当函数作为引用属性被添加到对象中,隐式绑定规则会把函数调用中的this绑定到这个上下文对象

3.显示绑定

运用apply call 方法,在调用函数时候绑定this,也就是指定调用的函数的this值

4.new绑定

就是使用new操作符的时候的this绑定

上述四条规则优先级由上到下依次递增。

由于js多样的绑定规则,带来了绑定隐式丢失问题,即函数中的this丢失绑定对象,即它会应用第 1 条的默认绑定规则,从而将this绑定到全局对象或者undefined上。

例如:绑定至上下文对象的函数被赋值给一个新的函数,然后调用这个新的函数时

var obj = { a: 2, foo: function () { con) } } var a = 2 setTimeou, 0) // 2

还记得我们当年我们是怎么做的吗?

... var me = this; return function () { me.xxx() } ...

还有就是用call 或者apply来显示的绑定:

function foo() { con( ); } var obj = { a: 2 }; var bar = function() { (obj); }; bar(); // 2 setTimeout(bar, 100); // 2

由于这种用法太多了,所以呢ES5的时候给出了一个方法Func(),自从有了它,前端工程师的生活似乎好过了很多,一个bind可以解决很多问题。

先看bind定义

MDN 给出的定义是:

bind()方法创建一个新的函数, 当这个新函数被调用时其this置为提供的值,其参数列表前几项置为创建时指定的参数序列。

(thisArg[, arg1[, arg2[, ...]]])

bind() 函数会创建一个新绑定函数,绑定函数与被调函数具有相同的函数体(在 ECMAScript 5 中)。调用绑定函数通常会导致执行包装函数 绑定函数也可以使用new运算符构造:这样做就好像已经构造了目标函数一样。提供的this值将被忽略,而前置参数将提供给模拟函数

总的来说bind有如下三个功能点:

  1. 改变原函数的 this 指向,即绑定上下文,返回原函数的拷贝
  2. 当绑定函数被调用时,bind的额外参数将置于实参之前传递给被绑定的方法。
  3. 注意,一个绑定函数也能使用new操作符创建对象,这种行为就像把原函数当成构造器,thisArg 参数无效。也就是 new 操作符修改 this 指向的优先级更高。

说了那么多,我们现在可不可以实现一个bind呢?

其实有时候去研究这些JS API的实现还是蛮好玩的,你能学到很多知识。今天我们就手摸手写一下吧。

从bind的定义描述中可以看到,我们要写的这个函数的输入输出基本确定了:

  • 输入:接受一个或者多个参数,第一个是要绑定的上下文,额外参数当作绑定函数的前置参数。输出:返回原函数的拷贝,即返回一个函数,这个函数呢具备原函数的功能
// 定义这个方法为myBind Func = function(thisArg) { if (typeof this !== 'function') { return; } var _self = this; var args = Array.(arguments, 1) //从第二个参数截取 return function() { return _(thisArg, args.concat(Array.(arguments))); // 注意参数的处理 } }

我们来测试一下:

function foo(name) { = name; } var obj = {} //上下文 功能 done var bar = (obj) bar('jack') con) //'jack' // 参数 功能 done var tar = (obj, 'rose'); tar() con) //'rose' // new 功能 error var alice = new bar('alice') con) //alice obj name should be 'jack' con) //undefined, alice name should be 'alice'

可以看到使用new实例化被绑定的方法,上下文还指向了传入的obj,这个方法有点问题,我们需要考虑到的是在myBind的实现里面,需要检测new的操作

我们先考虑一下new操作符在调用构造函数时做了哪些操作?

比如说 var a = new b()

  1. 创建一个新的对象 newObj{}
  2. 继承被实例化函数的原型 :newObj.__proto__ = b.prototype
  3. 将这个对象newObj绑定到构造函数b中的 this
  4. 如果没有返回其他对象,new 操作符调用的函数则会返回这个对象newObj

所以我们做了如下修改:

Func = function(thisArg) { if (typeof this !== 'function') { return; } var _self = this; var args = Array.(arguments, 1) var fnBound = function () { // 检测 New // 如果当前函数的this指向的是构造函数中的this 则判定为new 操作 var _this = this instanceof _self ? this : thisArg; return _(_this, args.concat(Array.(arguments))); } // 为了完成 new操作 // 还需要做一件事情 执行原型 链接 (思考题,为什么? = ; return fnBound; }

测试OK了:

function foo(name) { = name; } var obj = {}; var bar = (obj); bar('Jack'); con); // Jack var alice = new bar('Alice'); con); // Jack con); // Alice

这个用例中我们来讨论一下

bar的this指向:

  • 变量 bar 是绑定之后的函数,也就是 fnBound, _self 是原函数 foo 的引用
  • 如果直接bar('jack') 它指向window 或undefined。
  • 而new bar('alice') (相当于 new foo('alice'))过程中,fnBound的this指向了new表达式返回的对象alice
  • 如果是 new 调用绑定函数,此时绑定函数中的 this 是由 new 调用绑定函数返回的实例对象,这个对象的构造函数是 fnBound,
  • 当我们忽略掉原型连接那行代码时,其原型对象并不等于原函数 self 的原型,所以 this instanceof self ? this : oThis 得到的值还是指定的传入的对象,而不是 new 返回的对象

所以 = 是有必要的,!!但注意 这个原型赋值是有问题的:

原因我在这里先不说了留给各位讨论啦。

Func = function(thisArg) { if (typeof this !== 'function') { return } var _self = this var args = Array.(arguments, 1) var fnNop = function () {} // 定义一个空函数 var fnBound = function () { var _this = this instanceof _self ? this : thisArg return _(_this, args.concat(Array.(arguments))) } // 维护原型关系 if () { = ; } = new fnNop(); return fnBound; }

这里我们创建了一个空函数来做中间人,承接原函数的原型丢给绑定函数,好了问题就搞完了,是不是很多知识点慢慢看吧,我去踢球去了。

责任编辑: 鲁达

1.内容基于多重复合算法人工智能语言模型创作,旨在以深度学习研究为目的传播信息知识,内容观点与本网站无关,反馈举报请
2.仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证;
3.本站属于非营利性站点无毒无广告,请读者放心使用!

“bind,bind软件,bind是什么意思”边界阅读