javascript - क्या जावास्क्रिप्ट के "साथ" कथन के लिए वैध उपयोग हैं?




language-features with-statement (20)

बयान के बारे में मेरे जवाब के जवाब में एलन तूफान की टिप्पणियां मुझे सोचने लगीं। मुझे शायद ही कभी इस विशेष भाषा सुविधा का उपयोग करने का कोई कारण मिला है, और कभी भी इस बारे में ज्यादा सोचा नहीं था कि इससे परेशानी कैसे हो सकती है। अब, मैं उत्सुक हूं कि इसके नुकसान से बचने के दौरान मैं इसका प्रभावी उपयोग कैसे कर सकता हूं।

आपने कथन के with उपयोगी कहां पाया है?


"साथ" का उपयोग करके आपका कोड अधिक सूखा हो सकता है।

निम्नलिखित कोड पर विचार करें:

var photo = document.getElementById('photo');
photo.style.position = 'absolute';
photo.style.left = '10px';
photo.style.top = '10px';

आप इसे निम्नलिखित में सूख सकते हैं:

with(document.getElementById('photo').style) {
  position = 'absolute';
  left = '10px';
  top = '10px';
}

मुझे लगता है कि यह निर्भर करता है कि क्या आपके पास सुवार्ता या अभिव्यक्ति की प्राथमिकता है या नहीं।

पहला उदाहरण अधिक सुगम है और शायद अधिकांश कोड के लिए अनुशंसित है। लेकिन ज्यादातर कोड वैसे भी सुंदर है। दूसरा एक और अधिक अस्पष्ट है लेकिन कोड आकार और अनावश्यक चर पर कटौती करने के लिए भाषा की अभिव्यक्तित्मक प्रकृति का उपयोग करता है।

मुझे लगता है कि जावा या सी # पसंद करने वाले लोगों को पहला तरीका (ऑब्जेक्ट.मेम्बर) चुनना होगा और रूबी या पायथन पसंद करने वाले लोग बाद वाले को चुनेंगे।


अस्पष्टता के बिना लाभ प्रदान करने के लिए आप एक छोटे से सहायक समारोह को परिभाषित कर सकते हैं:

var with_ = function (obj, func) { func (obj); };

with_ (object_name_here, function (_)
{
    _.a = "foo";
    _.b = "bar";
});

आप किसी ऑब्जेक्ट की सामग्री को ब्लॉक में स्थानीय चर के रूप में पेश करने के with उपयोग कर सकते हैं, जैसे कि यह इस छोटे टेम्पलेट इंजन के साथ किया जा रहा है।


कथन के साथ कोड आकार को कम करने या निजी वर्ग के सदस्यों के लिए उपयोग किया जा सकता है, उदाहरण:

// demo class framework
var Class= function(name, o) {
   var c=function(){};
   if( o.hasOwnProperty("constructor") ) {
       c= o.constructor;
   }
   delete o["constructor"];
   delete o["prototype"];
   c.prototype= {};
   for( var k in o ) c.prototype[k]= o[k];
   c.scope= Class.scope;
   c.scope.Class= c;
   c.Name= name;
   return c;
}
Class.newScope= function() {
    Class.scope= {};
    Class.scope.Scope= Class.scope;
    return Class.scope;
}

// create a new class
with( Class.newScope() ) {
   window.Foo= Class("Foo",{
      test: function() {
          alert( Class.Name );
      }
   });
}
(new Foo()).test();

अगर आप दायरे को संशोधित करना चाहते हैं, तो कथन के साथ बहुत उपयोगी है, अपने खुद के वैश्विक दायरे के लिए जरूरी है कि आप रनटाइम पर हेरफेर कर सकें। आप इस पर स्थिरांक डाल सकते हैं या कुछ सहायक सहायक कार्यों जैसे अक्सर "toUpper", "toLower" या "isNumber", "clipNumber" aso ..

खराब प्रदर्शन के बारे में मैंने अक्सर पढ़ा है: किसी फ़ंक्शन को स्कॉइंग करने पर प्रदर्शन पर कोई असर नहीं पड़ेगा, वास्तव में मेरे एफएफ में एक स्कॉप्ड फ़ंक्शन तेजी से चलता है तो एक अनकॉप्ड:

var o={x: 5},r, fnRAW= function(a,b){ return a*b; }, fnScoped, s, e, i;
with( o ) {
    fnScoped= function(a,b){ return a*b; };
}

s= Date.now();
r= 0;
for( i=0; i < 1000000; i++ ) {
    r+= fnRAW(i,i);
}
e= Date.now();
console.log( (e-s)+"ms" );

s= Date.now();
r= 0;
for( i=0; i < 1000000; i++ ) {
    r+= fnScoped(i,i);
}
e= Date.now();
console.log( (e-s)+"ms" );

तो उपरोक्त उल्लिखित तरीके से प्रदर्शन के साथ प्रदर्शन के पर कोई नकारात्मक प्रभाव नहीं पड़ता है, लेकिन एक अच्छा है क्योंकि यह कोड आकार को धोखा देता है, मोबाइल उपकरणों पर स्मृति उपयोग पर क्या प्रभाव पड़ता है।


डेल्फी के साथ अनुभव करने के बाद, मैं कहूंगा कि इसका उपयोग अंतिम-रिज़ॉर्ट आकार अनुकूलन होना चाहिए, संभवतः किसी प्रकार के जावास्क्रिप्ट मिनीमाइज़र एल्गोरिदम द्वारा इसकी सुरक्षा को सत्यापित करने के लिए स्थिर कोड विश्लेषण तक पहुंच के साथ किया जाना चाहिए।

स्कोपिंग समस्याएं जो आप कथन के उदार उपयोग के साथ प्राप्त कर सकते हैं, ** में शाही दर्द हो सकता है और मैं नहीं चाहता कि किसी को डिबगिंग सत्र का अनुभव करना पड़े, यह पता लगाने के लिए कि वह आपके कोड में क्या चल रहा है , केवल यह पता लगाने के लिए कि आपने अपने वैश्विक या बाहरी स्कोप वेरिएबल के बजाय किसी ऑब्जेक्ट सदस्य या गलत स्थानीय चर को कैप्चर किया है।

कथन के साथ वीबी बेहतर है, इसमें स्कॉइंग को असंबद्ध करने के लिए डॉट्स की आवश्यकता होती है, लेकिन बयान के साथ डेल्फी बालों के साथ एक भारित बंदूक है, और यह मुझे लगता है कि जावास्क्रिप्ट एक ही चेतावनी के लिए पर्याप्त है।


मुझे लगता है कि इसके with उपयोगिता इस बात पर निर्भर हो सकती है कि आपका कोड कितना अच्छा लिखा गया है। उदाहरण के लिए, यदि आप ऐसा कोड लिख रहे हैं जो इस तरह दिखाई देता है:

var sHeader = object.data.header.toString();
var sContent = object.data.content.toString();
var sFooter = object.data.footer.toString();

तो आप तर्क दे सकते हैं कि यह करने से कोड की पठनीयता में सुधार होगा:

var sHeader = null, sContent = null, sFooter = null;
with(object.data) {
    sHeader = header.toString();
    sContent = content.toString();
    sFooter = content.toString();
}

इसके विपरीत, यह तर्क दिया जा सकता है कि आप डेमेटर के कानून का उल्लंघन कर रहे हैं, लेकिन फिर, शायद नहीं। मैं digress =)।

सब से ऊपर, पता है कि डगलस क्रॉकफोर्ड के with उपयोग करने की सिफारिश नहीं है । मैं आपको आग्रह करता हूं कि आप yuiblog.com/blog/2006/04/11/with-statement-considered-harmful और इसके विकल्पों के बारे में अपने ब्लॉग पोस्ट को देखें।


मुझे लगता है कि वस्तु का शाब्दिक उपयोग दिलचस्प है, जैसे बंद करने के लिए ड्रॉप-इन प्रतिस्थापन

for(var i = nodes.length; i--;)
{
       // info is namespaced in a closure the click handler can access!
       (function(info)
       {           
            nodes[i].onclick = function(){ showStuff(info) };
       })(data[i]);
}

या एक बंद के समकक्ष बयान के साथ

for(var i = nodes.length; i--;)
{
       // info is namespaced in a closure the click handler can access!
       with({info: data[i]})
       {           
            nodes[i].onclick = function(){ showStuff(info) };
       }        
}

I think the real risk is accidently minipulating variables that are not part of the with statement, which is why I like the object literal being passed into with, you can see exactly what it will be in the added context in the code.


मुझे लगता है कि स्पष्ट उपयोग एक शॉर्टकट के रूप में है। यदि आप किसी ऑब्जेक्ट को प्रारंभ करना चाहते हैं तो आप बस "ऑब्जेक्टनाम" टाइपिंग को सहेजते हैं। लिस्प के "स्लॉट्स" की तरह, जो आपको लिखने देता है

(with-slots (foo bar) objectname
   "some code that accesses foo and bar"

जो लेखन के समान है

"some code that accesses (slot-value objectname 'foo) and (slot-value objectname 'bar)""

यह और स्पष्ट है कि यह एक शॉर्टकट क्यों है जब आपकी भाषा "Objectname.foo" को अनुमति देती है लेकिन फिर भी।


मैं स्कोप्ड आयात के एक साधारण रूप के रूप में कथन के साथ उपयोग कर रहा हूं। मान लीजिए कि आपके पास किसी प्रकार का मार्कअप बिल्डर है। लिखने के बजाय:

markupbuilder.div(
  markupbuilder.p('Hi! I am a paragraph!',
    markupbuilder.span('I am a span inside a paragraph')
  )
)

आप इसके बजाय लिख सकते हैं:

with(markupbuilder){
  div(
    p('Hi! I am a paragraph!',
      span('I am a span inside a paragraph')
    )
  )
}

इस उपयोग के मामले में, मैं कोई असाइनमेंट नहीं कर रहा हूं, इसलिए मेरे पास उससे जुड़ी अस्पष्टता समस्या नहीं है।


मैंने कभी भी इसका उपयोग नहीं किया है, इसका कोई कारण नहीं दिख रहा है, और इसकी अनुशंसा नहीं करते हैं।

इसके साथ समस्या यह है कि यह कई लेक्सिकल ऑप्टिमाइज़ेशन को रोकता है जो एक ईसीएमएस्क्रिप्ट कार्यान्वयन कर सकता है। तेजी से जेआईटी-आधारित इंजनों के उदय को देखते हुए, यह मुद्दा शायद निकट भविष्य में और भी महत्वपूर्ण हो जाएगा।

ऐसा लगता है कि क्लीनर संरचनाओं (जब, कहें, एक सामान्य अज्ञात फ़ंक्शन रैपर की बजाय एक नया दायरा पेश करना या वर्बोज़ एलियासिंग को प्रतिस्थापित करना) की अनुमति हो सकती है, लेकिन यह वास्तव में इसके लायक नहीं है । एक कम प्रदर्शन के अलावा, हमेशा एक गलत वस्तु की संपत्ति को आवंटित करने का खतरा होता है (जब संपत्ति इंजेक्शन स्कोप में किसी ऑब्जेक्ट पर नहीं मिलती है) और शायद गलती से वैश्विक चर प्रस्तुत करना। आईआईआरसी, बाद का मुद्दा वह है जिसने क्रॉकफोर्ड से बचने के लिए प्रोत्साहित with


शायद आप इसके लायक हैं क्योंकि आप निम्न कार्य कर सकते हैं:

var o = incrediblyLongObjectNameThatNoOneWouldUse;
o.name = "Bob";
o.age = "50";

साथ प्रयोग करने की अनुशंसा नहीं की जाती है, और ईसीएमएस्क्रिप्ट 5 सख्त मोड में वर्जित है। अनुशंसित विकल्प उस ऑब्जेक्ट को असाइन करना है जिसका गुण आप अस्थायी चर तक पहुंचना चाहते हैं।

स्रोत: Mozilla.org


हाँ, हाँ और हाँ। एक बहुत ही वैध उपयोग है। घड़ी:

with (document.getElementById("blah").style) {
    background = "black";
    color = "blue";
    border = "1px solid green";
}

असल में किसी भी अन्य डोम या सीएसएस हुक के साथ शानदार उपयोग हैं। ऐसा नहीं है कि "क्लोन नोड" को अपरिभाषित नहीं किया जाएगा और वैश्विक दायरे पर वापस नहीं जायेगा जबतक कि आप अपने रास्ते से बाहर नहीं गए और इसे संभव बनाने का फैसला किया।

क्रॉकफोर्ड की गति शिकायत यह है कि एक नया संदर्भ इसके साथ बनाया गया है। संदर्भ आम तौर पर महंगे होते हैं। मैं सहमत हूँ। लेकिन अगर आपने अभी एक डिवी बनाया है और आपके सीएसएस को सेट करने के लिए हाथ पर कुछ ढांचा नहीं है और हाथ से 15 या तो सीएसएस गुण स्थापित करने की आवश्यकता है, तो एक संदर्भ बनाना शायद सस्ता निर्माण और 15 अवधारणाओं के बाद सस्ता होगा:

var element = document.createElement("div"),
    elementStyle = element.style;

elementStyle.fontWeight = "bold";
elementStyle.fontSize = "1.5em";
elementStyle.color = "#55d";
elementStyle.marginLeft = "2px";

आदि...


As Andy E pointed out in the comments of Shog9's answer, this potentially-unexpected behavior occurs when using with with an object literal:

for (var i = 0; i < 3; i++) {
  function toString() {
    return 'a';
  }
  with ({num: i}) {
    setTimeout(function() { console.log(num); }, 10);
    console.log(toString()); // prints "[object Object]"
  }
}

Not that unexpected behavior wasn't already a hallmark of with .

If you really still want to use this technique, at least use an object with a null prototype.

function scope(o) {
  var ret = Object.create(null);
  if (typeof o !== 'object') return ret;
  Object.keys(o).forEach(function (key) {
    ret[key] = o[key];
  });
  return ret;
}

for (var i = 0; i < 3; i++) {
  function toString() {
    return 'a';
  }
  with (scope({num: i})) {
    setTimeout(function() { console.log(num); }, 10);
    console.log(toString()); // prints "a"
  }
}

But this will only work in ES5+. Also don't use with .


For some short code pieces, I would like to use the trigonometric functions like sin , cos etc. in degree mode instead of in radiant mode. For this purpose, I use an AngularDegree object:

AngularDegree = new function() {
this.CONV = Math.PI / 180;
this.sin = function(x) { return Math.sin( x * this.CONV ) };
this.cos = function(x) { return Math.cos( x * this.CONV ) };
this.tan = function(x) { return Math.tan( x * this.CONV ) };
this.asin = function(x) { return Math.asin( x ) / this.CONV };
this.acos = function(x) { return Math.acos( x ) / this.CONV };
this.atan = function(x) { return Math.atan( x ) / this.CONV };
this.atan2 = function(x,y) { return Math.atan2(x,y) / this.CONV };
};

Then I can use the trigonometric functions in degree mode without further language noise in a with block:

function getAzimut(pol,pos) {
  ...
  var d = pos.lon - pol.lon;
  with(AngularDegree) {
    var z = atan2( sin(d), cos(pol.lat)*tan(pos.lat) - sin(pol.lat)*cos(d) );
    return z;
    }
  }

This means: I use an object as a collection of functions, which I enable in a limited code region for direct access. I find this useful.


Here's a good use for with : adding new elements to an Object Literal, based on values stored in that Object. Here's an example that I just used today:

I had a set of possible tiles (with openings facing top, bottom, left, or right) that could be used, and I wanted a quick way of adding a list of tiles which would be always placed and locked at the start of the game. I didn't want to keep typing types.tbr for each type in the list, so I just used with .

Tile.types = (function(t,l,b,r) {
  function j(a) { return a.join(' '); }
  // all possible types
  var types = { 
    br:  j(  [b,r]),
    lbr: j([l,b,r]),
    lb:  j([l,b]  ),  
    tbr: j([t,b,r]),
    tbl: j([t,b,l]),
    tlr: j([t,l,r]),
    tr:  j([t,r]  ),  
    tl:  j([t,l]  ),  
    locked: []
  };  
  // store starting (base/locked) tiles in types.locked
  with( types ) { locked = [ 
    br,  lbr, lbr, lb, 
    tbr, tbr, lbr, tbl,
    tbr, tlr, tbl, tbl,
    tr,  tlr, tlr, tl
  ] } 
  return types;
})("top","left","bottom","right");

I created a "merge" function which eliminates some of this ambiguity with the with statement:

if (typeof Object.merge !== 'function') {
    Object.merge = function (o1, o2) { // Function to merge all of the properties from one object into another
        for(var i in o2) { o1[i] = o2[i]; }
        return o1;
    };
}

I can use it similarly to with , but I can know it won't affect any scope which I don't intend for it to affect.

उपयोग:

var eDiv = document.createElement("div");
var eHeader = Object.merge(eDiv.cloneNode(false), {className: "header", onclick: function(){ alert("Click!"); }});
function NewObj() {
    Object.merge(this, {size: 4096, initDate: new Date()});
}

I just really don't see how using the with is any more readable than just typing object.member. I don't think it's any less readable, but I don't think it's any more readable either.

Like lassevk said, I can definitely see how using with would be more error prone than just using the very explicit "object.member" syntax.


My

switch(e.type) {
    case gapi.drive.realtime.ErrorType.TOKEN_REFRESH_REQUIRED: blah
    case gapi.drive.realtime.ErrorType.CLIENT_ERROR: blah
    case gapi.drive.realtime.ErrorType.NOT_FOUND: blah
}

boils down to

with(gapi.drive.realtime.ErrorType) {switch(e.type) {
    case TOKEN_REFRESH_REQUIRED: blah
    case CLIENT_ERROR: blah
    case NOT_FOUND: blah
}}

क्या आप इतने कम गुणवत्ता वाले कोड पर भरोसा कर सकते हैं? नहीं, हम देखते हैं कि यह बिल्कुल अपठनीय बना दिया गया था। यह उदाहरण निर्विवाद रूप से साबित करता है कि अगर मैं पठनीयता सही कर रहा हूं, तो कथन के लिए कोई आवश्यकता नहीं है;)


You can use with to avoid having to explicitly manage arity when using require.js:

var modules = requirejs.declare([{
    'App' : 'app/app'
}]);

require(modules.paths(), function() { with (modules.resolve(arguments)) {
    App.run();
}});

Implementation of requirejs.declare:

requirejs.declare = function(dependencyPairs) {
    var pair;
    var dependencyKeys = [];
    var dependencyValues = [];

    for (var i=0, n=dependencyPairs.length; i<n; i++) {
        pair = dependencyPairs[i];
        for (var key in dependencyPairs[i]) {
            dependencyKeys.push(key);
            dependencyValues.push(pair[key]);
            break;
        }
    };

    return {
        paths : function() {
            return dependencyValues;
        },

        resolve : function(args) {
            var modules = {};
            for (var i=0, n=args.length; i<n; i++) {
                modules[dependencyKeys[i]] = args[i];
            }
            return modules;
        }
    }   
}




with-statement