简体   繁体   中英

How can I factor a closure out of an outer function to reuse said closure in different contexts?

Suppose I have this bit of code that deals with event handling.

var btn1 = document.getElementById('btn1');
var btn2 = document.getElementById('btn2');

btn.addEventListener('click', say('hello'));
btn2.addEventListener('click', shout('yo'));

function say(word) {
    var saying = word;
    var container = document.getElementById('result1');
    return function handleEvent() {
        var text = document.createTextNode(saying);
        container.innerHTML = '';
        container.appendChild(text);
    };
}

function shout(word) {
    var saying = word.toUpperCase();
    var container = document.getElementById('result2');
    return function handleEvent() {
        var text = document.createTextNode(saying);
        container.innerHTML = '';
        container.appendChild(text);
    };
}

Uppon clicking on btn1 , "hello" is displayed in result1 .

Uppon clicking on btn2 , "YO" is displayed in result2 .

Now, as you may have noticed, the two handleEvent functions are exactly the same. What I want to achieve is something like.

function say(word) {
    var saying = word;
    var container = document.getElementById('result1');
    return handleEvent;
}

function shout(word) {
    var saying = word.toUpperCase();
    var container = document.getElementById('result2');
    return handleEvent;
}

function handleEvent() {
    var text = document.createTextNode(saying);
    container.innerHTML = '';
    container.appendChild(text);
}

But that of course will not work as the handleEvent function is not declared inside the say and shout functions; thus, saying and container will not be defined.

I have tried playing around with bind() and declare saying and container on this inside the say and shout functions :

function say(word) {
    this.saying = word;
    this.container = document.getElementById('result1');
    return handleEvent.bind(this);
}

function shout(word) {
    this.saying = word.toUpperCase();
    this.container = document.getElementById('result2');
    return handleEvent.bind(this);
}

function handleEvent() {
    var text = document.createTextNode(this.saying);
    this.container.innerHTML = '';
    this.container.appendChild(text);
}

But that didn't work either.


How can i factor the handleEvent function out of the say and shout functions?

Thank you!

JsBin setup

You cannot reuse a closure in a different context, because by definition in javascript a closure is an inner function which is bound to the context of its parent.

You can however extract your function body in order to reuse it in different contexts:

var btn1 = document.getElementById('btn1');
var btn2 = document.getElementById('btn2');

btn.addEventListener('click', say('hello'));
btn2.addEventListener('click', shout('yo'));

function saySomething(context, saying) {
    var text = document.createTextNode(saying);
    container.innerHTML = '';
    container.appendChild(text);
};

function say(word) {
    var saying = word;
    var container = document.getElementById('result1');
    return function handleEvent() {
        saySomething(context, saying);
    };
}

function shout(word) {
    var saying = word.toUpperCase();
    var container = document.getElementById('result2');
    return function handleEvent() {
        saySomething(context, saying);
    };
}

Or if you want to use bind:

var btn1 = document.getElementById('btn1');
var btn2 = document.getElementById('btn2');

btn.addEventListener('click', say('hello'));
btn2.addEventListener('click', shout('yo'));

function saySomething(context, saying) {
    var text = document.createTextNode(saying);
    container.innerHTML = '';
    container.appendChild(text);
};

function say(word) {
    var saying = word;
    var container = document.getElementById('result1');
    return saySomething.bind(undefined, context, saying);
}

function shout(word) {
    var saying = word.toUpperCase();
    var container = document.getElementById('result2');
    return saySomething.bind(undefined, context, saying);
}

Binding should have worked if you had created new objects to be bound to, instead of using this :

function say(word) {
    return handleEvent.bind({
        saying: word,
        container: document.getElementById('result1')
    });
}

function shout(word) {
    return handleEvent.bind({
        saying: word.toUpperCase(),
        container: document.getElementById('result2')
    });
}

However, you still can solve this with a normal closure:

function say(word) {
    return makeHandleEvent(word, 'result1');
}

function shout(word) {
    return makeHandleEvent(word.toUpperCase(), 'result2');
}


function makeHandleEvent(saying, id) {
    var container = document.getElementById(id);
    return function handleEvent() {
        var text = document.createTextNode(saying);
        container.innerHTML = '';
        container.appendChild(text);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM