Test Driven JavaScript Development- P7

Test Driven JavaScript Development- P7

Lượt xem: 129,610Lượt tải: 8Số trang: 20

Mô tả tài liệu

Test Driven JavaScript Development- P7:This book is about programming JavaScript for the real world, using the techniques and workflow suggested by Test-Driven Development. It is about gaining confidence in your code through test coverage, and gaining the ability to fearlessly refactor and organically evolve your code base. It is about writing modular and testable code. It is about writing JavaScript that works in a wide variety of environments and that doesn’t get in your user’s way.

Tóm tắt nội dung

ptg 6.4 6.26 Memoizing the Fibonacci sequence in a fibonacci = (function () { var cache = { if (x < 2) { return 1; } if = - 1) + - version of fibonacci runs many orders of magnitude the original one, and by extension is capable of more numbers in the sequence. However, mixing with caching logic is a bit ugly. Again, we will add a function to to help separate memoize method in Listing 6.27 is capable of wrapping a method, without the 6.27 A general purpose memoize = function () { var cache = {}; var func = function (x) { if (!(x in cache)) = From the Library of purchase PDF on to remove this Applied Functions and method offers a clean way to memoize as seen in Listing 6.28 Memoizing the fibonacci { "test calculate high fib value with () { var = offers a clean solution but only deals that take a single argument. Limiting its use further is the fact that coerces all arguments to strings, by nature of property be discussed in detail in Chapter 7, Objects and improve the memoizer, we would need to serialize all arguments to use as keys. One way to do this, which is only slightly more complex than what we is to simply join the as Listing 6.29 6.29 A slightly better memoize = function () { var cache = {}; var func = this; var join = function () { var key = (!(key in cache)) = version will not perform as well as the previous because it join and uses apply rather than call, because we no longer can number of Also, this version will coerce all arguments to before, meaning it cannot between, e.g., "12" and 12 passed as From the Library of purchase PDF on to remove this Summary Finally, because the cache key is generated by joining the a comma, string arguments that contain commas can cause the wrong value to be loaded, i.e., (1, "b") would generate the same cache key as is possible to implement a proper that can embed type and possibly use tddjs.uid to serialize object and but doing so would impact the ofmemoize in a such that it would only help out in cases that could be in other ways. Besides, object arguments using simple and fast, would cause the method to possibly assign new That would be in most cases and should at the very least be properly this chapter we have worked through a handful of practical function a special focus on closures. With an of the scope chain 5, we have seen how inner functions can keep private state in free Through examples we have seen how to make use of the scope and state offered by closures to solve a range of problems in an elegant of the functions developed in this chapter will make chapters as we build on top of them and add more useful to the tddjs object. the book we will also meet plenty more using the next chapter we will take a look at objects and gain a bet- ter of how property access and work, can help in object oriented in as well as ways to create objects and share behavior between them. From the Library of purchase PDF on to remove this page left blank From the Library of purchase PDF on to remove this and is an object oriented language. However, unlike object oriented does not have classes. Instead, and in which objects inherit from the language offers that create a fact that often confuses and hides its nature. In this how objects and work. We’ll also study chain as well as working through several examples in a Objects and has object literals, i.e., objects can be typed directly into a program syntax, much like string ("a string literal") and number can be typed directly in a program in most Listing 7.1 shows of an object 7.1 An object car = { model: { year: From the Library of purchase PDF on to remove this Objects and 5, doors: ["Air "Electric function () Listing 7.1 shows a few other literals available in as notably the array literal ([] as opposed to new defines a object as an unordered of consist of a name, a value, and a set of Property names string literals, number literals, or may take any (strings, numbers, booleans, null or and When have function objects assigned to them, refer to them as methods. ECMA-262 also defines a set of internal and methods that are not part of the language, but are used by The encloses names of these internal in double brackets, i.e., I will use this notation as Property can be accessed in one of two dot or using a style commonly with The square bracket notation offers a of when looking up It can take any string or returns a string. This means that you can figure out the at run-time and look it up on an object directly using the square benefit of the square bracket notation is that you can access name contain not allowed in such as white space. You can mix dot and bracket notation at will, making it very easy to look up on an you might remember, we used property names spaces to make our test case names more in Chapter 3, Tools of the Trade, as seen 7.2. From the Library of purchase PDF on to remove this Objects and 7.2 A property name with = { "test dots and brackets should behave () { var value = obj = { prop: value Grab the test var name = "test dots and brackets should behave = Mix dot and bracket notation to get number of arguments for the test argc = we get a test method (i.e., a property) from our object using the notation, because the name of the property we are in that are illegal in is possible to get and set on an object using other values literals, number literals, or When you do so, the object will to a string by its toString method if it exists (and returns a string), or its valueOf method. Beware that these methods may be for host and for generic objects the toString method will Object]". I recommend you stick to string literals, literals for property The Prototype Chain In every object has a The property is internal and is as in the ECMA-262 It is an implicit reference to of the that created the object. For generic to The prototype may have a prototype of its own and so on, forming a prototype chain. The prototype chain is used to across objects in and forms the basis for model. This concept is different from classical in 1. Host objects will be discussed in Chapter 10, Feature From the Library of purchase PDF on to remove this Objects and classes inherit from other classes, and objects instances of approach the subject by our study of property you read a property on an object, uses the object’s method. This method checks if the object has a property of the If it has, its value is returned. If the object does not have such a checks if the object has a that is not null has a null If it does, the whether the prototype has the property in question. If it does, its value otherwise the continues up the prototype chain until it If neither the object nor any of the objects in its a property of the given name, undefined is you assign, or put, a value to an object property, the object’s method is used. If the object does not already have a property of the one is created and its value is set to the provided value. If the object a property of the same name, its value is set to the one does not affect the prototype chain. In fact, if we assign a that already exists on the prototype chain, we are shadowing the Listing 7.3 shows an example of property To run the test set up a simple project as described in Chapter 3, Tools of the add a file that loads 7.3 and shadowing { "test setting property shadows property on () { var object1 = {}; var object2 = {}; // Both objects inherit chris = { name: function () { return chris object defines a toString property that is // not the same object as object1 inherits from From the Library of purchase PDF on to remove this Objects and 121 // === Deleting the custom property unshadows the // inherited seen in Listing 7.3, object1 and object2 don’t define a and so they share the same the prototype chain. The chris object, on the defines its own method, shadowing the toString property on chain. If we delete the custom toString property from the using the delete operator, the property no longer exists directly on object, causing the to look up the method from the finding we turn our attention to property we will discuss some of the [[Put]] Extending Objects through the Prototype Chain By the prototype property of we can mod- ify the behavior of every object created by it, including objects created before This also holds for native objects, such as arrays. To see how we’re going to implement a simple sum method for arrays. The test 7.4 what we want to 7.4 the behavior of { "test should summarize numbers in array": function () { var array = [1, 2, 3, 4, 5, this test informs us that there is no sum method for arrays, which is not all that The is a trivial loop, as seen 7.5. From the Library of purchase PDF on to remove this Objects and 7.5 Adding a method to = function () { var sum = 0; for (var i = 0, l = i < l; i++) { sum += all arrays inherit from we’re able to to all arrays. But what happens if there already is a sum method Such a method could be provided by a given browser, a library or running along with ours. If this is the case, we’re method. Listing 7.6 avoids this by placing our inside an if test that verifies that the method we’re adding does not already 7.6 adding a method to (typeof == = function () { // general, this is a good idea when extending native objects or on global objects. This way we make sure our code doesn’t trip up Even so, if there already is a sum method it may not act the way causing our code that relies on our sum to break. We can catch these a strong test suite, but this kind of problem clearly indicates that relying to global objects may not be the best approach when the focus is native like we just did comes with a price. We already saw how this may lead to but there is another drawback to this adding to an object, they are instantly on any inherits it. Listing 7.7 shows an example of looping arrays. From the Library of purchase PDF on to remove this Objects and 7.7 Looping arrays with for and { "test looping should iterate over all () { var array = [1, 2, 3, 4, 5, 6]; var result = []; // Standard (var i = 0, l = i < l; i++) for-in loop should iterate over all () { var array = [1, 2, 3, 4, 5, 6]; var result = []; for (var i in array) two loops both attempt to copy all the elements of one array onto then join both arrays into a string to verify that they do indeed contain the Running this test reveals that the second test fails with the message 7.8 Result of running test in Listing "123456" but was () { [... what’s we need to the for-in for (var property in object) will fetch the first of object. property is assigned the name of the property, and the body of the loop is executed. This is repeated as long as object has more and the body of the loop does not issue break (or return if inside a From the Library of purchase PDF on to remove this Objects and an array object, the only are its numeric methods and the length property provided by are This is why a for-in loop will only reveal the indexes and values for array objects. However, when we add to an object or one of the objects in its prototype chain, they are by default. this fact, these new will also appear in a for-in loop, as shown by the test failure above. I recommend you don’t use for-in on arrays. The problem above can be worked around, as we will see shortly, but not without trading off Using for-in on arrays means we can’t normalize by adding missing methods to without inferring returns true if an a property with the given name. If the object either inherits the property from the prototype chain, or doesn’t have such a property at all, false. This means that we can qualify a for-in loop with a call to ensure we only loop the object’s own as seen 7.9 a loop with looping should only iterate over own () { var person = { name: result = []; for (var prop in person) { if expected = "name", From the Library of purchase PDF on to remove this Objects and 125 This test passes because we now filter out added to the chain. There are two things to keep in mind when dealing with Some browsers, such as early versions of Safari don’t support it. • Objects are used as hashes; there is a risk of shadowed by another guard our code against the latter case, we can implement a custom accepts an object and a property and returns true if the property is one of the object’s own even when the object’s method or otherwise Listing 7.10 shows the method. Add it to file from Chapter 6, Applied Functions and 7.10 Sandboxed = (function () { var hasOwn = (typeof hasOwn == { return function (object, property) { return else { // Provide an emulation if you can live with browsers that do not support this method we can emulate it, but it is to provide a fully Chances are that lack this method will present other issues as well, so failing to provide may not be your main problem. We will learn to deal with such cases in Chapter 10, Feature are always when added by globals make it hard for scripts to co-exist, it is widely accepted should be left alone. should also with care, if you are on arrays. Although such generally be avoided for arrays, they can be useful when dealing with arrays. From the Library of purchase PDF on to remove this Objects and in mind that although you may decide to avoid extending native may not be so nice. Filtering for-in loops with you are not modifying and keep your code running as expected, of whether code such as ad, or analytics related code decide to do Property defines four that may be set for any given property. It is important to note that these are set for by the but code you write has no way of setting these ReadOnly and cannot be inspected but we can deduce their values. ECMA-262 specifies the method, which could be used to get the value however, it does not check the prototype chain and is not across a property has the ReadOnly attribute set, it is not possible to write to the to do so will pass by silently, but the property attempted to not change. Note that if any object on the prototype chain has a property with the attribute set, writing to the property will fail. ReadOnly does not imply that the value is constant and may change its value a property has the attribute set, it is not possible to delete it using the delete operator. Much like writing to with the deleting with the attribute will fail silently. will return false if the object either didn’t have the given property, or if the property existed and had a causes to not appear in for-in loops, as shown 7.9. The DontEnum attribute is the most important property because it is the one that is most likely to affect your code. 7.7 we saw an example of how may trip up for-in loops. The DontEnum attribute is the internal mechanism whether or not a property is From the Library of purchase PDF on to remove this Objects and Explorer version 8) has a peculiar bug property on an object that has a property by the same name anywhere on its prototype chain that has DontEnum will act as though it as well (even though it should be to have DontEnum on object). This means that if you create an object and shadow any of on neither of these will show up in a for-in loop in Internet Explorer. If you create an object of any of the native and host types, all the on the prototype chains with magically disappear from a for-in loop, as seen in Listing 7.11 with { "test should enumerate shadowed object () { var object = { // with DontEnum on result = []; for (var property in object) should enumerate shadowed function () { var object = function () {}; // with DontEnum = = = From the Library of purchase PDF on to remove this Objects and result = []; for (var property in object) of these tests fail in all versions of Internet Explorer, including is 0. We can solve this issue by making a special case for the on as well as if the object in question inherits from it. The in Listing 7.12 can be used to loop of for Internet bug. When defined, the method loop the of an object that shadows all the on as well as a function that shadows on Any property that does not show up in the loop is and looped inside the each 7.12 Looping with a each = (function () { // Returns an array of that are not in a for-in loop on the provided { var length = (var i = 0; i < length; i++) = = (var prop in object) { if prop)) -= = From the Library of purchase PDF on to remove this Objects and needsFix = []; for (i = 0; i < length; i++) { if oFixes = fFixes = () {}, ["call", "apply", (fFixes && oFixes) { fFixes = needsFix = { "object": oFixes, fFixes function (object, callback) { if (typeof callback != { throw new is not a Normal loop, should expose all in (var prop in object) { if prop)) Loop in fixes = (fixes) { var From the Library of purchase PDF on to remove this Objects and (var i = 0, l = i < l; i++) = we change the for-in loops in the tests in Listing 7.11 to use the method, the tests will run, even on Internet Explorer. the method smoothes over a similar bug in Chrome in which function property is not when Creating Objects with functions have the ability to act as when invoked with the new operator, i.e., new There is nothing that of a regular function and one that objects. In fact, every function with a prototype object in case it is used with the When the function is used as a to create new objects, property will be a reference to this the absence of language level checks on functions vs. names are usually to indicate their intended use. you use or not in your own code, you should honor this idiom by not names of functions and objects that are not prototype and word is used to describe two concepts. First, a has a public prototype property. When the is used to create new those objects will have an internal property that is a to the prototype property. Second, the has that the prototype of the that cre- ated it, most commonly All objects have an internal property; only function objects have the From the Library of purchase PDF on to remove this Creating Objects with Creating Objects with new When a function is called with the new operator, a new object is function is then called using the newly created object as the this value any arguments that were passed in the original call. In Listing 7.13 we see how creating objects with compares with the object literal we’ve been using so 7.13 Creating objects with = Create a circ = new Create a circ2 = { radius: 6 }; The two objects share the same radius property along inherited from Although both objects circ2 does so directly (i.e., its is a reference to whereas circ does so We can use the operator to the between objects. we can use the to inspect their origin, as seen in Listing 7.14 { "test inspect objects": function () { var circ = new circ2 = { radius: 6 From the Library of purchase PDF on to remove this Objects and a b will return true whenever the property of a, or one of the objects on its prototype chain, is the same object as are always assigned a prototype property, which will be set as the property of objects created by the function when used as a The assigned prototype object’s prototype is in it defines a single property, which is a reference to the itself. Because the new operator may be used with any in a we can use this property to create new the same type as a known object. In Listing 7.15 we use the of a circle object to create a new circle 7.15 Creating objects of the same should create another object of same () { var circle = new circle2 = new Adding to the can give our new circle objects new by the property of the much like we extended the behavior of in Section 7.1.3, Extending Objects through the Prototype Chain. Listing 7.16 adds three methods for circle objects to 7.16 Adding to = function () { return * = function () { return * From the Library of purchase PDF on to remove this