bind函数,bind函数js

http://www.itjxue.com  2023-01-22 05:14  来源:未知  点击次数: 

关于原生js中bind函数的简单实现

今天继续研究了bind函数的实现,也知道了shim和polyfill的说法,现在总结一下,

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

()

{

return

fToBind.apply(this

instanceof

fNOP

oThis

?

this

:

oThis

||

window,

aArgs.concat(Array.prototype.slice.call(arguments)));

};

fNOP.prototype

=

this.prototype;

fBound.prototype

=

new

fNOP();

return

fBound;

};

}

这是官方文档上的实现,我分二个方面来谈我要说的东西,

第一个是参数,agruments的使用

var

aArgs

=

Array.prototype.slice.call(arguments,

1),这里是将bind函数的参数数组取出来,第一个参数不要(就是不要oThis)也就是要被绑定方法的那个对象,第二个是

aArgs.concat(Array.prototype.slice.call(arguments)));

这里是用了数组的方法,把参数插在参数数组后面,要注意,这个函数是要被return

出去然后执行的,他的参数数组是return出去的那个fBound函数的参数数组,所以上下两个参数数组是不一样的,有点像柯里化。

第二个是上下文,在其中上下文的变化比较难理解,bind函数主要就是为了绑定上下文来使用的

fToBind

=

this

这里是保存了对象的上下文,紧接着下面的apply方法让要被绑定的那个对象可以使用该上下文

fNOP.prototype

=

this.prototype;

fBound.prototype

=

new

fNOP();

这里是以fNOP为中介把this.prototype这个原对象的属性给fBound,确保fBound是在定义的时候的那个上下文里面执行。本来

bound.prototype

=

self.prototype就可以将原属性集成过来了,但是这样两个对象属性都指向同一个地方,修改

bound.prototype

将会造成self.prototype

也发生改变,这样并不是我们的本意。所以通过一个空函数

nop

做中转,能有效的防止这种情况的发生。

以上这篇关于原生js中bind函数的简单实现就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持脚本之家。

手动实现bind函数

为什么要自己去实现一个bind函数?

所以,为了理想主义和世界和平(所有浏览器上都能随心所欲调用它),必要的时候需要我们自己去实现一个bind。那么,一个bind函数需要具备什么功能呢?

绑定this、定义初始化参数是它存在的主要意义和价值。MDN对它的定义如下:

鉴于这两个核心作用,我们可以来实现一个简单版看看:

由于arguments是类数组对象,不拥有数组的slice方法,所以需要通过call来将slice的this指向arguments。 args就是调用bind时传入的初始化参数(剔除了第一个参数oThis)。将args与绑定函数执行时的实参arguments通过concat连起来作为参数传入,就实现了bind函数初始化参数的效果。

bind函数的另外一个也是最主要的作用:绑定this指向,就是通过将调用bind时的this(self)指向指定的oThis来完成。这样当我们要使用bind绑定某个对象时,执行绑定函数,它的this就永远固定为指定的对象了~

到这里,我们已经可以用上面的版本来使用大部分场景了。但是~

但是,这种方案就像前面说的,它会永远地为绑定函数固定this为指定的对象。如果你仔细看过MDN关于bind的描述,你会发现还有一个情况除外:

我们可以通过一个示例来试试看原生的bind对于使用new的情况是如何的:

试验结果发现,obj.name依然是lily而没有变成tom,所以就像MDN描述的那样,如果绑定函数cat是通过new操作符来创建实例对象的话,this会指向创建的新对象tom,而不再固定绑定指定的对象obj。

而上面的简易版却没有这样的能力,它能做到的只是永久地绑定指定的this(有兴趣的朋友可以在控制台使用简易版bind试下这个例子看看结果)。这显然不能很好地替代原生的bind函数~

那么,如何才能区分绑定函数有没有通过new操作符来创建一个实例对象,从而进行分类处理呢?

我们知道 检测一个对象是否通过某个构造函数使用new实例化出来的最快的方式是通过 instanceof :

A instanceof B //验证A是否为B的实例

那么,我们就可以这样来实现这个bind:

假设我们将调用bind的函数称为C,将fBound的prototype原型对象指向C的prototype原型对象(上例中就是self),这样的话如果将fBound作为构造函数(使用new操作符)实例化一个对象,那么这个对象也是C的实例,this instanceof self就会返回true。这时就将self指向新创建的对象的this上就可以达到原生bind的效果了(不再固定指定的this)。否则,才使用oThis,即绑定指定的this。

但是这样做会有什么影响?将fBound的prototype原型对象直接指向self的prototype原型对象,那么当修改fBound的prototype对象时,self(上述C函数)的prototype对象也会被修改!!考虑到这个问题,我们需要另外一个function来帮我们做个中间人来避免这个问题,我们看看MDN是怎么实现bind的。

MDN针对bind没有被广泛支持的兼容性提供了一个实现方案:

发现了吗,和上面经过改造的方案相比,最主要的差异就在于它定义了一个空的function fNOP,通过fNOP来传递原型对象给fBound(通过实例化的方式)。这时,修改fBound的prototype对象,就不会影响到self的prototype对象啦~而且fNOP是空对象,所以几乎不占内存。

其实这个思路也是YUI库如何实现继承的方法。他的extend函数如下:

最后一步是将Child的constructor指回Child。

实现一个原生的函数,最重要的是理清楚它的作用和功能,然后逐一去实现它们包括细节,基本上就不会有问题~

这里用到的一些关于 prototype 和 instanceof 的具体含义,可以参考阮一峰老师的 prototype 对象 ,相信对你理解JavaScript的原型链和继承会有帮助~

原文链接:

C++ bind函数

bind是一组用于函数绑定的模板。在对某个函数进行绑定时,可以指定部分参数或全部参数,也可以不指定任何参数,还可以调整各个参数间的顺序。对于未指定的参数,可以使用占位符_1、_2、_3来表示。-1表示绑定后的函数的第1个参数,_2表示绑定后的函数的第2个参数,其他依次类推。

bind可以绑定到普通函数、函数对象、类的成员函数和类的成员变量。下面分别进行介绍。

1、普通函数

1 void nine_arguments(int i1,int i2,int i3,int i4,int i5,int i6,int i7,int i8,int i9);

2 int i1=1, i2=2, i3=3, i4=4, i5=5, i6=6, i7=7, i8=8, i9=9;

3 bind(nine_arguments,_9,_2,_1,_6,_3,_8,_4,_5,_7(i1,i2,i3,i4,i5,i6,i7,i8,i9);

4 bind(nine_arguments,i9,i2,i1,i6,i3,i8,_1,_2,_1)(i8,i9);

5 bind(nine_arguments, i9,i2,i1,i6,i3,i8,i4,i5,i7)();

2、函数对象

1 class CStudent

2 {

3 public:

4 void operator() (string strName, int nAge)

5 {

6 cout strName " : " nAge endl;

7 }

8 };

9 bind(CStudent(), "Mike", _1)(12);

3、类的成员函数

1 struct TAdd

2 {

3 int Add(int x,int y)

4 {

5 return x+y;

6 }

7 };

8 TAdd tAdd;

9 TAdd *p = new TAdd();

10 shared_ptrTAdd *q(p);

11 bind(TAdd::Add, tAdd, 2, 3)();

12 bind(TAdd::Add, p, 2, 3)();

13 bind(TAdd::Add, q, 2, 3)();

4、类的成员变量

1 void Output(const string name)

2 {

3 cout name endl;

4 }

5

6 mapint, string map1;

7 for_each(map1.begin(), map1.end(), bind(Output, bind(mapint,

8 string::value_type::second, _1)));

bind还可以进行嵌套绑定。假设有一个CPerson类,该类有一个获取年龄的接口int GetAge(),现在有一个CPerson对象的vector,需要对其进行排序,则可以如下使用bind:

1 vectorCPerson vctPerson;

2 sort(vctPerson.begin(), vctPerson.end(), bind(lessint(),

3 bind(CPerson::GetAge, _1), bind(CPerson::GetAge, _2)));

假设有一个整数的vector, 现在想要获取其中大于20但小于30的整数的个数,则有:

1 count_if(vctNum.begin(), vctNum.end, bind(logic_andbool(),

2 bind(greaterint(), _1, 20), bind(lessint(), _1, 30)));

在使用bind时,还有一些需要特别注意的地方,下面逐一进行介绍。

1、对于指定了值的参数,bind返回的函数对象会保存这些值,并且缺省是以传值方式保存的。考虑下面的代码:

1 void inc(int a) { a++; }

2 int n = 0;

3 bind(inc, n)();

调用bind返回的函数对象后,n仍然等于0。这是由于bind时,传入的是n的拷贝。如果需要传入n的引用,则可以使用ref或cref函数,比如:

1 bind(inc, ref(n))(); // n现在等于1了

2、bind的第一个参数是一个函数对象,不能用占位符来代替。考虑下面的代码:

1 typedef functionvoid (int) Func;

2 vectorFunc vctFunc;

3 for_each(vctFunc.begin(), vctFunc.end(), bind(_1, 5)); // 编译出错

此时,可以借助apply模板。apply模板的第一个参数是传入的函数对象,后面可以有若干个参数,表示函数对象的参数。比如:

1 applyvoid a; // void是函数对象的返回值类型

2 a(f); // 相当于调用f()

3 a(f, x); // 相当于调用f(x)

4 a(f, x, y); // 相当于调用f(x, y)

使用apply后,我们可以将vctFunc中的元素当作占位符传递过来。参考代码如下:

1 for_each(vctFunc.begin(), vctFunc.end(), bind(applyvoid(), _1, 5));

-- 本文来源于创世软件团队博客, 原文地址:

(责任编辑:IT教学网)

更多

推荐通讯数据软件文章