1. Trang chủ
  2. » Công Nghệ Thông Tin

Practical prototype and scipt.aculo.us part 29 pot

6 98 0
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 6
Dung lượng 87,63 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

function alertThreeThingsa, b, c {alerta; alertb; alertc; } var alertTwoThings = alertThreeThings.curry“alerted first”; alertTwoThings"foo", "bar"; // alerts "alerted first" // alerts "f

Trang 1

Functions Can Have Their Own Methods

Imagine I’ve got a function that adds numbers together:

function sum() {

var num = 0;

for (var i = 0; i < arguments.length; i++)

num += arguments[i];

return num;

}

The sumfunction will add its arguments together and return a number If no

argu-ments are given, it will return 0

We know the syntax for calling this method:

var result = sum();

To call this function, we append empty parentheses If we take the parentheses off,

though, we’re no longer calling the function—we’re just referring to it:

var result = sum;

Be sure you understand the difference In the first example, resultis set to 0—the

result of calling sumwith no arguments In the second example, resultis set to the sum

function itself In effect, we’re giving suman alias

var result = sum;

result(2, 3, 4); //-> 9

We’re going to look at a few instance methods of Function At first, these may seem

like magic tricks, but they’ll become more intuitive the more you use them

Using Function#curry

Partial application (or currying) is a useful technique in languages where functions are

first-class objects It’s the process by which you “preload” a number of arguments into a

function In other words, I could “curry” a function that expects parameters a,b, and cby

giving it aand bahead of time—getting back a function that expects only c

Confused yet? What I just said is much easier to express in code:

Trang 2

function alertThreeThings(a, b, c) {

alert(a);

alert(b);

alert(c);

}

var alertTwoThings = alertThreeThings.curry(“alerted first”);

alertTwoThings("foo", "bar");

// alerts "alerted first"

// alerts "foo"

// alerts "bar"

We’ve just defined a function that, if we were to write it out ourselves, would behave

like this:

function alertTwoThings(b, c) {

return alertThreeThings("alerted first", b, c);

}

But by using curry, we’re able to express this more concisely and flexibly

argu-ments into alertThreeThings:

var alertOneThing = alertThreeThings.curry("alerted first",

"alerted second");

alertOneThing("foo");

// alerts "alerted first"

// alerts "alerted second"

// alerts "foo"

var alertZeroThings = alertThreeThings.curry("alerted first",

"alerted second", "alerted third");

alertZeroThings();

// alerts "alerted first"

// alerts "alerted second"

// alerts "alerted third"

Let’s look at a less-contrived example We can curry the sumfunction we defined earlier:

Trang 3

var sumPlusTen = sum.curry(10);

typeof sumPlusTen; //-> "function"

sumPlusTen(5); //-> 15

sumPlusTen(); //-> 10

sumPlusTen(2, 3, 4); //-> 19

Each of these examples is equivalent to calling sumwith a value of 10as the first

argu-ment It doesn’t matter how many arguments we pass to the curried function, since we’ve

defined sumin a way that works with any number of arguments The curried function will

simply add 10to the beginning of the argument list and pass those arguments to the

orig-inal function

We’ll explore the use cases for Function#curryin later chapters But it’s important for

you to be familiar with it in the context of the other Functioninstance methods we’re

about to cover

Using Function#delay and Function#defer

JavaScript has no formal “sleep” statement—no way to block all script execution for a

specified amount of time But it does havesetTimeout, a function that schedules code

to be run at a certain time in the future

function remind(message) {

alert("REMINDER:" + message);

}

setTimeout(function() { remind("Be sure to do that thing"); }, 1000);

The built-in setTimeoutfunction takes two arguments The first can be either a

func-tion or a string; if it’s a string, it’s assumed to be code, and will be evaluated (with eval) at

the proper time (It’s much better practice to pass a function.) The second argument

must be a number, in milliseconds, that tells the interpreter how long to wait before

try-ing to run the code we gave it It returns an integer that represents the timer’s internal ID;

if we want to unset the timer, we can pass that ID into the clearTimeoutfunction

In this example, the setTimeoutcall ensures that the function we’ve passed it will get

called at least 1000 ms (1 second) later Because browser JavaScript executes in a

single-threaded context, the interpreter might be busy 1 second later, so there’s no way of

knowing the exact moment But nothing will slip through: as soon as the interpreter is

idle again, it will look at the backlog of things that have been scheduled, running

any-thing that’s past due

Trang 4

So maybe that’s a better way to think of setTimeout: it adds functions to a queue, along with a “do not run before” sticker that bears a timestamp In the preceding exam-ple, the timestamp is computed by adding 1000 ms to whatever the time was when we called setTimeout

As a thought experiment, then, imagine the second argument to setTimeoutgetting smaller and smaller What happens when it hits 0? Does such a thing trigger a wormhole through space-time? No, for our purposes, the result is far more mundane The inter-preter “defers” the function to run whenever it has a spare moment

Prototype thinks the setTimeoutfunction is ugly Prototype prefers to give functions instance methods named delayand defer

Function#delay

wait (not milliseconds, unlike setTimeout)

function annoy() {

alert("HEY! You were supposed to do that THING!");

}

annoy.delay(5);

// alerts "HEY! You were supposed to do that THING!" roughly 5 seconds later

There are those who might say that this is a just a fancy way of calling setTimeout I

think it’s a less fancy way of calling setTimeout, if fanciness can be measured in number of characters typed

// equivalent statements

setTimeout(annoy, 5000);

annoy.delay(5);

The gains are more obvious when you’re calling a function that takes arguments Any arguments given to delayafter the first are passed along to the function itself:

function remind(message) {

alert("REMINDER:" + message);

}

remind.delay(5, "Be sure to do that thing.");

// alerts "REMINDER: Be sure to do that thing." roughly 5 seconds later

Trang 5

Now compare the several ways to say the same thing:

// equivalent statements

setTimeout(function() { remind("Be sure to do that thing." }, 5000);

setTimeout(remind.curry("Be sure to do that thing."), 5000);

remind.delay(5, "Be sure to do that thing.");

We save a few keystrokes through clever use of Function#curry, but we save far more

by using Function#delay

Function#defer

practical timeout in a browser environment) To defer a function call is to say, “Don’t do

this now, but do it as soon as you’re not busy.”

To illustrate this concept, let’s see howdeferaffects execution order:

function remind(message) {

alert("REMINDER:" + message);

}

function twoReminders() {

remind.defer("Don't forget about this less important thing.");

remind("Don't forget about this _absolutely critical_ thing!");

}

twoReminders();

// alerts "Don't forget about this _absolutely critical_ thing!"

// alerts "Don't forget about this less important thing."

In the twoRemindersfunction, we make two calls to remind The first, a deferred call,

fires after the second More specifically, the second call fires immediately, and the first

call fires as soon as the interpreter exits the twoRemindersfunction

The commonest use case for deferis to postpone costly operations:

function respondToUserClick() {

doSomethingCostly.defer(); // instead of doSomethingCostly();

$('foo').addClassName('selected');

}

Here, it’s best to defer the call to doSomethingCostlyuntil after the function exits That

way, the visual feedback (adding a class name to an element) happens without delay,

making the application feel “snappier” to the user

Trang 6

Using Function#bind

To talk about Prototype’s Function#bindis to delve into JavaScript’s confusing rules about scope Scope is the meaning of the keyword thisin a given context

Let’s look back at our Totalerexample In Chapter 1, we talked about how a scope is created whenever an object is instantiated When I call new Totaler, I create a new scope for the instance; Totaler#initializeruns within this scope, as do all other methods in the

any of these methods

var Totaler = Class.create({

initialize: function(element, totalElement, options) {

this.element = $(element);

this.totalElement = $(totalElement);

this.options = Object.extend({

selector: ".number"

}, options || {});

},

For this reason, thisbecomes internal shorthand for the instance itself

Try to take one of these methods out of context, though, and you’ll run into prob-lems If I declare a new Totaleron the page, I might want to alias its updateTotalmethod for convenience:

window.totaler = new Totaler('cities', 'population_total');

var retotal = totaler.updateTotal;

All I’ve done is hand a method reference to retotal I should be able to call retotalon its own later on, but if I try it I run into problems:

retotal();

//-> Error: this.element is undefined

In JavaScript, scope is bound to execution context The interpreter doesn’t decide what thismeans until a function gets called Here we run into trouble—retotaldoesn’t know anything about the Totalerinstance’s scope

the function whose scope is “bound” to that argument Here, we want thisto refer to

Ngày đăng: 03/07/2014, 01:20

TỪ KHÓA LIÊN QUAN