您的位置:首页 > Web前端 > JavaScript

JavaScript学习笔记(四十) 借用方法

2013-08-01 23:13 483 查看

借用方法(Borrowing Methods)

有时候你可能只需要一个已经存在的对象的一个或两个方法。你想复用他们,但你真的不想和那个对象够成父子(parent-child )关系。
你只想使用你需要的方法,而不要继承其他的你永远也不会需要的方法。
通过借用方法模式,这是可能的,得益于function的方法call()和apply()。你已经在前面见过这种模式了,在extendDeep()实现中。

正如你知道的,函数在JavaScript中是对象,它们有一些它们自己的有趣的方法,比如call()和apply()。
二者之间唯一的区别一个在被调用时接收一个参数数组,另一个的接收一个一个的参数。
你可以使用这些方法从已经存在的对象借用函数功能:
// call() example
notmyobj.doStuff.call(myobj, param1, p2, p3);
// apply() example
notmyobj.doStuff.apply(myobj, [param1, p2, p3]);
这里你有个对象叫做myobj并且你知道另一个对象叫做notmyobj有个有用的方法叫做doStuff()。
避免继承的麻烦和继承一些你的myobj永远不需要的方法,你可以简单的临时借用doStuff()方法。

你传入你的对象和一些参数,借用的方法绑定你的对象作为它的this。
实际上,你的对象伪装成其它对象为了从你需要的方法获得一些好处。就好像你得到了遗产(getting an inheritance)但没有支付继承税(这里的"税"是以额外的你不需要的方法和属性的形式出现)。

例子:从数组借用(Example: Borrow from Array)

这种模式一个常见用法就是借用数组方法。
数组拥有有用的方法,那些类数组对象(array-like objects)比如arguments类数组对象(array-like objects)比如arguments没有的方法。所以arguments可以借用数组的方法,比如slice()方法。这里是个例子:
function f() {
var args = [].slice.call(arguments, 1, 3);
return args;
}
// example
f(1, 2, 3, 4, 5, 6); // returns [2,3]
在这个例子中,有个空数组被创建仅是为了使用它的方法。一个法实现相同的效果稍微长点的方式是直接从从Array的prototype借用,使用Array.prototype.slice.call(...)。
这种方式输入起来有点长,但是你节省了创建空数组的工作。

借用和绑定(Borrow and Bind)

无论是通过call()/apply()还是简单赋值语句借用方法,在借用的方法中this指向的对象取决于调用表达式。但有时候最好能将this的值锁定或者预先绑定给一个指定的对象。
让我们看一个例子。有一个对象叫做one,有一个say()方法:
var one = {
name: "object",
say: function(greet) {
return greet + ", " + this.name;
}
};
// test
one.say('hi'); // "hi, object"
现在另外一个对象two没有say()方法,但它可以从one借:

var two = {
name: "another object"
};
one.say.apply(two, ['hello']); // "hello, another object"
在前面这个情况,在say()里面的this指向two,this.name因此是"another object"。但在这些场景下会怎么样?你将函数赋值给一个全局变量或者你将函数作为一个回调传递。在客户端编程中有大量的事件和回调,所以这些场景发生很多:

// assigning to a variable
// `this` will point to the global object
var say = one.say;
say('hoho'); // "hoho, undefined"
// passing as a callback
var yetanother = {
name: "Yet another object",
method: function(callback) {
return callback('Hola');
}
};
yetanother.method(one.say); // "Holla, undefined"
在这些情况下在say()this都指向全局对象(global object),且整个代码片段都不能像预期那样工作。固定(fix)(换言之,绑定(bind))一个方法到一个对象。
我们可以使用像下面的简单函数:
function bind(o, m) {
return function() {
return m.apply(o, [].slice.call(arguments));
};
}
这个bind()函数接收一个对象o和一个方法m,将二者绑定在一起,并返回另一个函数。这个返回的函数能通过闭包(closure)访问的om,都将始终指向原来的对象和方法。
让我们用bind()创建一个新函数:

var twosay = bind(two, one.say);
twosay('yo'); // "yo, another object"
就如你看到的,虽然twosay()被作为全局函数创建,this没有指向全局对象,但this指向传递给bind()的对象twothis将永远被绑定到two

拥有一个奢侈的bind你付出的代价是一个额外的闭包。

Function.prototype.bind()

ECMAScript 5 给Function.prototype添加了一个方法bind(),使它更容易使用apply()和call()。所以你能使用像这样的表达式:

var newFunc = obj.someFunc.bind(myobj, 1, 2, 3);
这个意味着将someFunc()和myobj绑定在一起并且预先填充了someFun()期望的前三个参数。这也是一个部分函数应用的例子。

让我们看一下你如何能够实现 Function.prototype.bind()当你在程序运行在ES-5之前的环境:
if (typeof Function.prototype.bind === "undefined") {
Function.prototype.bind = function(thisArg) {
var fn = this,
slice = Array.prototype.slice,
args = slice.call(arguments, 1);
return function() {
return fn.apply(thisArg, args.concat(slice.call(arguments)));
};
};
}
这个实现可能看起来有一点熟悉,它使用了部分函数应用(partial application)且连接了传递给bind()的参数(除了第一个)和bind()函数创建的新函数在后面被调用时传递的函数。这里是一个使用的例子:
var twosay2 = one.say.bind(two);
twosay2('Bonjour'); // "Bonjour, another object"
在前面这个例子中,你没有传递任何参数给bind()除了要被绑定的对象。在下一个例子,我们传递一个参数去被部分应用(partially applied):
var twosay3 = one.say.bind(two, 'Enchanté');
twosay3(); // "Enchanté, another object"
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: