आप जावास्क्रिप्ट में [] ऑपरेटर को ओवरलोड कैसे करेंगे


85

मैं जावास्क्रिप्ट में [] ऑपरेटर को ओवरलोड करने का तरीका खोजने के लिए प्रतीत नहीं कर सकता। बाहर किसी को पता है?

मैं की तर्ज पर सोच रहा था ...

MyClass.operator.lookup(index)
{
     return myArray[index];
}

या मैं सही चीजों को नहीं देख रहा हूं।


3
यहाँ उत्तर गलत हैं, JS में Arrays केवल ऐसी वस्तुएं हैं जिनकी कुंजी uint32 (- 1) मानों के लिए ज़बरदस्त हैं और उनके प्रोटोटाइप पर अतिरिक्त तरीके हैं
बेंजामिन ग्रुएनबाम

बस अपनी MyClassवस्तु को एक सरणी बनाएं । आप myArrayअपनी var myObj = new MyClass()वस्तु से कुंजियों और मूल्यों को कॉपी कर सकते हैं ।
jpaugh

हे, मैं चाहूंगा कि ओवरलोड {} ऑपरेटर, कोई विचार?
डेनियल अस्सैग

जवाबों:


81

आप ऑपरेटरों को जावास्क्रिप्ट में अधिभार नहीं दे सकते।

इसे ECMAScript 4 के लिए प्रस्तावित किया गया था लेकिन इसे अस्वीकार कर दिया गया था।

मुझे नहीं लगता कि आप इसे कभी भी देख पाएंगे।


4
यह पहले से ही कुछ ब्राउज़रों में परदे के पीछे के साथ संभव हो सकता है, और कुछ बिंदुओं पर सभी ब्राउज़रों के लिए आ जाएगा। देखें github.com/DavidBruant/ProxyArray
ट्रिस्टन

फिर jQuery कैसे आप [] या .eq () का उपयोग करते हैं उस पर निर्भर अलग-अलग चीजें वापस आती हैं? stackoverflow.com/a/6993901/829305
रिक्की

2
आप इसे अब एक प्रॉक्सी के साथ कर सकते हैं।
ईयाल

हालाँकि आप उनमें प्रतीकों के साथ तरीकों को तब तक परिभाषित कर सकते हैं जब तक आप उन्हें "" के बजाय एक सरणी के रूप में एक्सेस करते हैं। इस तरह से SmallTalk नक्शे चीजों की तरह Object arg1: a arg2: b arg3: cहै Object["arg1:arg2:arg3:"](a,b,c)। तो आपके पास हो सकता है myObject["[]"](1024): P
दिमित्री

लिंक मर चुका है :(
Gp2mv3

69

आप ईएस 6 प्रॉक्सी ( सभी आधुनिक ब्राउज़रों में उपलब्ध ) के साथ ऐसा कर सकते हैं

var handler = {
    get: function(target, name) {
        return "Hello, " + name;
    }
};
var proxy = new Proxy({}, handler);

console.log(proxy.world); // output: Hello, world

एमडीएन पर विवरण देखें ।


2
इंडेक्स एक्सेसर के साथ अपनी कक्षा बनाने के लिए हम इसका उपयोग कैसे करेंगे? यानी मैं अपने खुद के कंस्ट्रक्टर का उपयोग करना चाहता हूं, मैं प्रॉक्सी का निर्माण नहीं करना चाहता।
एमपीएन

1
यह सही ओवरलोडिंग नहीं है। ऑब्जेक्ट पर कॉल करने के तरीकों के बजाय, अब आप प्रॉक्सी के तरीकों को कॉल कर रहे हैं।
पचेरियर

@ स्पेसर आप गेट्टर target[name]में वापस आ सकते हैं , ओपी केवल उदाहरण दिखा रहा है
वैलेन

1
[]ऑपरेटर के साथ ही काम करता है , btw:var key = 'world'; console.log(proxy[key]);
Klesun

15

इसका सरल उत्तर यह है कि जावास्क्रिप्ट, वर्ग कोष्ठक के माध्यम से किसी वस्तु के बच्चों तक पहुँचने की अनुमति देता है।

तो आप अपनी कक्षा को परिभाषित कर सकते हैं:

MyClass = function(){
    // Set some defaults that belong to the class via dot syntax or array syntax.
    this.some_property = 'my value is a string';
    this['another_property'] = 'i am also a string';
    this[0] = 1;
};

फिर आप अपने वर्ग के किसी भी उदाहरण पर सदस्यों को सिंटैक्स के साथ एक्सेस कर पाएंगे।

foo = new MyClass();
foo.some_property;  // Returns 'my value is a string'
foo['some_property'];  // Returns 'my value is a string'
foo.another_property;  // Returns  'i am also a string'
foo['another_property'];  // Also returns 'i am also a string'
foo.0;  // Syntax Error
foo[0];  // Returns 1
foo['0'];  // Returns 1

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

4
यह वह नहीं है जो सवाल चाहता है। सवाल यह है कि foo['random']आपके कोड को करने में सक्षम नहीं है जो पकड़ने के लिए एक रास्ता पूछ रहा है।
पचेरियर

9

एक प्रॉक्सी का उपयोग करें। इसका जवाब कहीं और दिया गया था, लेकिन मुझे लगता है कि यह एक बेहतर उदाहरण है:

var handler = {
    get: function(target, name) {
        if (name in target) {
            return target[name];
        }
        if (name == 'length') {
            return Infinity;
        }
        return name * name;
    }
};
var p = new Proxy({}, handler);

p[4]; //returns 16, which is the square of 4.

संभवतः यह उल्लेख करने योग्य है कि परदे के पीछे एक ES6 सुविधा है और इसलिए एक अधिक सीमित ब्राउज़र समर्थन है (और बैबेल उन्हें या तो नकली नहीं कर सकता )।
jdehesa

8

जैसा कि ब्रैकेट्स ऑपरेटर वास्तव में प्रॉपर्टी एक्सेस ऑपरेटर है, आप गेटर्स और सेटर्स के साथ इस पर हुक लगा सकते हैं। IE के लिए आपको इसके बजाय Object.defineProperty () का उपयोग करना होगा। उदाहरण:

var obj = {
    get attr() { alert("Getter called!"); return 1; },
    set attr(value) { alert("Setter called!"); return value; }
};

obj.attr = 123;

IE8 + के लिए समान:

Object.defineProperty("attr", {
    get: function() { alert("Getter called!"); return 1; },
    set: function(value) { alert("Setter called!"); return value; }
});

IE5-7 के लिए onpropertychangeकेवल ईवेंट है, जो DOM तत्वों के लिए काम करता है, लेकिन अन्य वस्तुओं के लिए नहीं।

विधि का दोष यह है कि आप केवल गुणों के पूर्वनिर्धारित सेट के अनुरोध पर हुक कर सकते हैं, किसी पूर्वनिर्धारित नाम के बिना मनमानी संपत्ति पर नहीं।


क्या आप कृपया jsfiddle.net पर अपने दृष्टिकोण का प्रदर्शन कर सकते हैं ? मुझे लगता है कि समाधान को अभिव्यक्ति में किसी भी कुंजी के लिए काम करना चाहिए, obj['any_key'] = 123;लेकिन मुझे आपके कोड में जो भी दिख रहा है, उसे किसी भी (अभी तक ज्ञात नहीं) कुंजी के लिए सेटर / गेट्टर को परिभाषित करने की आवश्यकता है। वह असंभव है।
dma_k

3
प्लस 1 माइनस 1 को ऑफसेट करने के लिए क्योंकि यह केवल IE नहीं है।
परिक्रमा

क्या यह एक क्लास फंक्शन के लिए किया जा सकता है? मैं अपने दम पर इसके लिए वाक्यविन्यास खोजने के लिए संघर्ष कर रहा हूं।
माइकल हॉफमैन

7

आपको बताए अनुसार प्रॉक्सी का उपयोग करने की आवश्यकता है, लेकिन इसे अंततः एक क्लास कंस्ट्रक्टर में एकीकृत किया जा सकता है

return new Proxy(this, {
    set: function( target, name, value ) {
...}};

इसके साथ'। तब सेट और मिलता है (भी deleteProperty) कार्यों आग जाएगा। हालाँकि आपको एक प्रॉक्सी ऑब्जेक्ट मिलता है जो कि अधिकांश भाग के लिए अलग-अलग लगता है, तुलना करने के लिए काम करता है (target.constructor === MyClass) यह वर्ग प्रकार आदि है [भले ही यह एक फ़ंक्शन है जहाँ target.constructor.name वर्ग का नाम है पाठ (केवल उन चीजों का एक उदाहरण देख रहा है जो थोड़ा अलग काम करते हैं।)]


1
यह बैकबोन संग्रह पर [] को ओवरलोड करने के लिए अच्छी तरह से काम करता है ताकि व्यक्तिगत मॉडल वस्तुओं को अन्य सभी गुणों के लिए पास-थ्रू के साथ उपयोग किया जा सके।
justkt

5

तो आप कुछ भी करने की उम्मीद कर रहे हैं जैसे var = MyClassInstance [4]; ? यदि हां, तो सरल उत्तर यह है कि जावास्क्रिप्ट वर्तमान में ऑपरेटर ओवरलोडिंग का समर्थन नहीं करता है।


1
तो jQuery कैसे काम करता है आप $ ('। Foo') जैसे jQuery ऑब्जेक्ट पर एक विधि कह सकते हैं। html () या $ ('। Foo') की तरह पहला मेलिंग डोम तत्व प्राप्त करें [0]
kagronick

1
jQuery एक फ़ंक्शन है, आप $ फ़ंक्शन के लिए एक पैरामीटर पास कर रहे हैं। इसलिए () कोष्ठक, नहीं []
जेम्स वेस्टगेट

5

ऐसा करने का एक आसान तरीका है भाषा का विस्तार करना।

चरण 1

एक कस्टम इंडेक्सिंग कन्वेंशन को परिभाषित करते हैं, चलो इसे "[]" कहते हैं।

var MyClass = function MyClass(n) {
    this.myArray = Array.from(Array(n).keys()).map(a => 0);
};
Object.defineProperty(MyClass.prototype, "[]", {
    value: function(index) {
        return this.myArray[index];
    }
});

...

var foo = new MyClass(1024);
console.log(foo["[]"](0));

चरण 2

एक नया निष्कासन कार्यान्वयन परिभाषित करें। (इस तरह मत करो, लेकिन यह अवधारणा का प्रमाण है)।

var MyClass = function MyClass(length, defaultValue) {
    this.myArray = Array.from(Array(length).keys()).map(a => defaultValue);
};
Object.defineProperty(MyClass.prototype, "[]", {
    value: function(index) {
        return this.myArray[index];
    }
});

var foo = new MyClass(1024, 1337);
console.log(foo["[]"](0));

var mini_eval = function(program) {
    var esprima = require("esprima");
    var tokens = esprima.tokenize(program);
    
    if (tokens.length == 4) {    
        var types = tokens.map(a => a.type);
        var values = tokens.map(a => a.value);
        if (types.join(';').match(/Identifier;Punctuator;[^;]+;Punctuator/)) {
            if (values[1] == '[' && values[3] == ']') {
                var target = eval(values[0]);
                var i = eval(values[2]);
                // higher priority than []                
                if (target.hasOwnProperty('[]')) {
                    return target['[]'](i);
                } else {
                    return target[i];
                }
                return eval(values[0])();
            } else {
                return undefined;
            }
        } else {
            return undefined;
        }
    } else {
        return undefined;
    }    
};

mini_eval("foo[33]");

उपरोक्त अधिक जटिल अनुक्रमित के लिए काम नहीं करेगा, लेकिन यह मजबूत पार्सिंग के साथ हो सकता है।

विकल्प:

अपनी स्वयं की सुपरसेट भाषा बनाने का सहारा लेने के बजाय, आप मौजूदा भाषा में अपने अंकन को संकलित कर सकते हैं, फिर उसे निकाल सकते हैं। यह पहली बार उपयोग करने के बाद देशी से पार्सिंग ओवरहेड को कम कर देता है।

var compile = function(program) {
    var esprima = require("esprima");
    var tokens = esprima.tokenize(program);
    
    if (tokens.length == 4) {    
        var types = tokens.map(a => a.type);
        var values = tokens.map(a => a.value);
        if (types.join(';').match(/Identifier;Punctuator;[^;]+;Punctuator/)) {
            if (values[1] == '[' && values[3] == ']') {
                var target = values[0];
                var i = values[2];
                // higher priority than []                
                return `
                    (${target}['[]']) 
                        ? ${target}['[]'](${i}) 
                        : ${target}[${i}]`
            } else {
                return 'undefined';
            }
        } else {
            return 'undefined';
        }
    } else {
        return 'undefined';
    }    
};

var result = compile("foo[0]");
console.log(result);
console.log(eval(result));

1
आप कुटिल +1
जूस

पूरी तरह घिनौना। मुझे यह पसंद है।
स्लाइसपाइ

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

3

हम प्रॉक्सी प्राप्त कर सकते हैं | सीधे तरीके सेट करें। से प्रेरित होकर इस

class Foo {
    constructor(v) {
        this.data = v
        return new Proxy(this, {
            get: (obj, key) => {
                if (typeof(key) === 'string' && (Number.isInteger(Number(key)))) // key is an index
                    return obj.data[key]
                else 
                    return obj[key]
            },
            set: (obj, key, value) => {
                if (typeof(key) === 'string' && (Number.isInteger(Number(key)))) // key is an index
                    return obj.data[key] = value
                else 
                    return obj[key] = value
            }
        })
    }
}

var foo = new Foo([])

foo.data = [0, 0, 0]
foo[0] = 1
console.log(foo[0]) // 1
console.log(foo.data) // [1, 0, 0]
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.