Which design pattern(s) take advantage of JavaScri

2019-01-30 10:28发布

Ben Cherry's excellent article explains hoisting in JavaScript adequately. My problem, however, is that I cannot conceive a use case for this notorious perpetrator of confusion. Please explain if there is a design pattern that actually takes advantage of this language feature.

Secondly, is scope hoisting unique to JavaScript?

UPDATE --- I'm adding a bounty for an answer that satisfies my curiosity: Which design pattern(s) actually take advantage of JavaScript's hoisting behavior? I understand why JavaScript supports hoisting, but I want to know how I can take advantage of this feature.

9条回答
我想做一个坏孩纸
2楼-- · 2019-01-30 11:07

I think one area where hoisting is useful is due to the fact that functions are treated as first class objects. For example:

function foo()
{
   function a()
   {
      //...
   }

   function b()
   {
      //...
   }
}

can also be written as:

function foo()
{
   var a = function ()
   {
      //...
   }

   var b = function ()
   {
      //...
   }
}

Without hoisting, the following would result in an error:

function foo()
{
   var a = function ()
   {
      b();
   }
   a(); //Error in function since b is not yet defined

   var b = function ()
   {
      //...
   }
}

I suppose they could have only hoisted function objects, but I believe that would be inconsistent with the philosophy that functions should be treated as first class citizens in the language.

查看更多
三岁会撩人
3楼-- · 2019-01-30 11:14

The first two examples in that article are just badly written. Bad code obviously leads to bugs and confusion. Let me give you the refactored versions of these examples. You will see that there is no confusion here...

Example 1 - Original code

var foo = 1;
function bar() {
    if (!foo) {
        var foo = 10;
    }
    alert(foo);
}
bar();

Example 1 - Refactored code (removed confusion)

var foo = 1;

function bar() {
    var foo;

    if ( !foo ) {
        foo = 10;
    }

    alert( foo );
}

bar();

The alert displays "10", and it's clear why. No confusion here.

Example 2 - Original code

var a = 1;
function b() {
    a = 10;
    return;
    function a() {}
}
b();
alert(a);

Example 2 - Refactored code (removed confusion)

var a = 1;

function b() {
    var a = function () {}; 
    a = 10;
    return; 
}

b();

alert( a );

The alert displays "1". Obviously. No confusion here, too.

查看更多
闹够了就滚
4楼-- · 2019-01-30 11:14

If you consider the way other languages are written (C++/Java) and how their Class patterns are used, hoisting could be taken advantage of to write a similar pattern to build prototypes .

查看更多
Bombasti
5楼-- · 2019-01-30 11:19

Here's a use for hoisting:

(function() {
    var factorial = function(n) {
        if(n == 0)
            return 1;
        return n * factorial(n - 1);
    };
})();

Without hoisting, that wouldn't compile because factorial wouldn't exist yet inside the function literal. You'd have to declare the variable separately or use a named function.

JavaScript also allows code like the following:

var test = function(b) {
    if(b) {
        var value = 2;
    } else {
        var value = 5;
    }
    console.log(value);
};

With block scoping, you'd have to add another line to declare value before the if.

To be fair, this code works because of function scope, not hoisting. And JavaScript could have had function scope without hoisting. Ruby handles this better: Ruby has method scope for variables, but the variables don't exist until you set them:

def test(b)
    # unlike JavaScript, value is not accessible here
    if b
        value = 2
    else
        value = 5
    end
    puts value
end
查看更多
Animai°情兽
6楼-- · 2019-01-30 11:24

I like the style of question, based on curiosity about the language. Obviously no one should actually use hoisting as a feature, unless they're absolutely certain that their home address cannot be discovered by those who may use it later.

I can only imagine a few trivial cases. The basic property to exploit is that the variable can be declared (but undefined) and then assigned in only one line of code, but with the events interpreted at two different distinct point.

With the declaration at the end of a loop (not .forEach as that of course sets scope) you could use this to detect the first iteration.

var notfirst = true;  // this is actually never referenced.

(function () {  
  var number, stack = [1, 2, 3, 4, 5];

  while (number = stack.pop()) {
    if (notfirst) console.log(number);
    var notfirst = true;
  }
})();

The output from emptying the stack is 4, 3, 2, 1. 5 is rejected.

Again. Don't do this!

查看更多
【Aperson】
7楼-- · 2019-01-30 11:27

Variable hoisting

One of the simplest uses of hoisting is variable hoisting. If we didn't have variable hoisting, this would throw a ReferenceError:

var bar = foo; 
var foo;

That doesn't seem immediately useful, but it allows us to do things like this:

var myCoolJS = myCoolJS || {};

This basically means what it looks like: myCoolJS is myCoolJS if it exists, or a new object if it doesn't. The second myCoolJS doesn't throw a ReferenceError if myCoolJS didn't already exist, because this variable declaration is hoisted.

This saves us from doing an awkward typeof myCoolJS != 'undefined' check.

Function hoisting

Function hoisting can be especially useful when combining multiple scripts into one. For example, I've created a lightweight build-time implementation of CommonJS modules. This provides the same module, require, and exports features that are found in node.js. I built the tool to allow required modules to be composed of multiple files. For example, require('/foo') could result in a module composed of two files, foo.js (the "body file") and foo.h.js (the "header file").

This allows the "body file" to have no knowledge of the free variables provided by the CommonJS modules environment; all of that is handled in the header. This makes code reusable and easy to test without building. However, since the headers are prepended to the body, we leverage function hoisting in the body file to allow exports in the headers. For example:

// dom.h.js

var util = require('util').util;

exports.css = css; // we can do this because "css" is hoisted from below

// ... other exports ...

...

// dom.js

function css(){}; // this would normally just be an object.

css.hasClass = function(element) { ... };
css.addClass = function(element) { ... };

// ...other code...
查看更多
登录 后发表回答