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)访问的o和m,都将始终指向原来的对象和方法。
让我们用bind()创建一个新函数:
var twosay = bind(two, one.say); twosay('yo'); // "yo, another object"就如你看到的,虽然twosay()被作为全局函数创建,this没有指向全局对象,但this指向传递给bind()的对象two,this将永远被绑定到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"
相关文章推荐
- JS学习笔记——JavaScript继承的6种方法(原型链、借用构造函数、组合、原型式、寄生式、寄生组合式)
- JavaScript学习笔记(三十二) 经典继承模式二-借用构造方法
- 学习笔记4--JavaScript正确使用substr() 、substring()、slice()、split()、splice()方法
- [原]Java程序员的JavaScript学习笔记(9—— jQuery工具方法)
- JavaScript 函数的call()方法的学习笔记
- JavaScript学习笔记第一天——字符串连接的N种方法
- JavaScript学习笔记29-其他的创建数组的方法
- Javascript学习笔记:3种检测变量类型的方法
- javascript学习笔记(九):DOM操作HTML的各种方法使用
- JavaScript学习笔记 创建数组,数组方法使用
- JavaScript学习笔记30-数组属性和方法
- .Net学习笔记 - javascript方法重载
- JavaScript 学习笔记(九)call和apply方法
- JavaScript across domain 方法 学习笔记
- JavaScript学习笔记(三) 字面量和构造方法
- JavaScript学习笔记:检测数组方法
- JavaScript学习笔记之ES6数组方法
- JavaScript学习笔记4--互动方法
- Javascript学习笔记: Function::apply 方法
- javascript学习笔记——chrome等提示找不到“getElementsByTagName”的一种解决方法