简体   繁体   中英

How to reuse closure with different variables

I want to reuse the function sayMyName but with different variables. Please let me know if I'm structuring this the wrong way and what is the best practice what what I'm trying to do.

 var sayMyName = function(myName) { console.log(myName) }; var name1 = function() { // myName should not be a global variable // because there may be more variables/functions // that I'd want to closed inside sayMyName(). // Declaring all of them to the global scope is not ideal. var myName = 'Walter'; sayMyName(); // I don't want to pass in myName as argument like this: // sayMyName(myName); // I want myName to be implicitly included in sayMyName() // I want to pass in everything that is declared in name1 to sayMyName() implicitly. }; var name2 = function() { var myName = 'White'; sayMyName(); } name1(); // should give me 'Walter' name2(); // should give me 'White' 

I'm not sure why you specifically want a closure, but by looking at your example it seems that a bind would be more appropriate than a closure.

 var sayMyName = function(myName) { console.log(myName) }; var name1 = sayMyName.bind(undefined, 'Walter'); var name2 = sayMyName.bind(undefined, 'White'); name1(); // log 'Walter' name2(); // log 'White' 

Move your variable myName to the outermost scope:

 var myName; var sayMyName = function() { console.log(myName) }; var name1 = function() { myName = 'Walter'; sayMyName(); }; var name2 = function() { myName = 'White'; sayMyName(); } name1(); // should give me 'Walter' name2(); // should give me 'White' 

Update: Thinking about it, if you're willing to use the non-standard, Error.stack attribute, are willing to use named functions, and are willing to use a naming convention, you could hackishly achieve your goal:

 function sayMyName() { try { throw new Error(); } catch (e) { if (e.stack) { // non-standard attribute var reNames = /^\\s*at myNameIs([AZ][^(\\s]*)\\s*\\(/mg; reNames.lastIndex = 0; var buffer = []; for (var match = reNames.exec(e.stack); null !== match; match = reNames.exec(e.stack)) { buffer.push(match[1]); } console.log(buffer.join(" ")); } } } function myNameIsWalter() { sayMyName(); } function myNameIsWhite() { myNameIsWalter(); }; myNameIsWalter(); // "Walter" myNameIsWhite(); // "Walter White" 

... and if you're willing to use eval (bad !!!), then you could do fancier things like the following:

 var sayMyName = function () { try { throw new Error(); } catch (e) { if (e.stack) { // non-standard attribute var reNames = /^\\s*at ([_a-zA-Z][_a-zA-Z0-9]+(\\.[_a-zA-Z][_a-zA-Z0-9]+)*)/mg; reNames.lastIndex = 0; var reMyName = /\\bmyName\\s*=\\s*(?:"([^"]*)"|'([^']*)')/g; var identifier, definition, match, myName, buffer = []; while (null !== (match = reNames.exec(e.stack))) { try { identifier = match[1]; if ("sayMyName" !== identifier) { definition = eval(match[1] + '.toString()'); if (/\\bsayMyName\\(\\)/.test(definition)) { reMyName.lastIndex = 0; buffer.length = 0; while (null !== (myName = reMyName.exec(definition))) { buffer.push(myName[1]); } console.log(buffer.join(" ")); } } } catch (_) { // continue } } } } }; function name1() { var myName = "Walter"; sayMyName(); } function name2() { var myName; myName = "Walter"; myName = "White"; sayMyName(); } name1(); // "Walter" name2(); // "Walter White" 

You could even use the non-standard, Function.caller attribute, which is probably the cleanest approach (if it works in your browser -- it's non-standard for a reason):

 function sayMyName() { var reMyName = /\\bmyName\\s*=\\s*(?:"([^"]*)"|'([^']*)')/g; var definition, buffer = []; for (var caller = sayMyName.caller; caller; caller = caller.caller) { definition = caller.toString(); if (/\\bsayMyName\\(\\)/.test(definition)) { reMyName.lastIndex = 0; buffer.length = 0; while (null !== (myName = reMyName.exec(definition))) { buffer.push(myName[1]); } console.log(buffer.join(" ")); } } }; function name1() { var myName = "Walter"; sayMyName(); } function name2() { var myName; myName = "Walter"; myName = "White"; sayMyName(); } name1(); // "Walter" name2(); // "Walter White" 

This is arguably no less code than just passing a parameter, though.

If you want closure then this is example.

function sayMyName(myName){
   return function(){
      console.log(myName); //myName is available from parent scope
      return myName; 
   }
}
var name1 = sayMyName('Walter');
var name2 = sayMyName('White');
//no console output by now
name1(); //Walter
name2(); //White
//myName is not available in global scope
console.log(myName); //undefined 

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