سؤال

كنت أقرأ فقط هذا السؤال وأردت تجربة طريقة الاسم المستعار بدلاً من طريقة تغليف الوظائف، لكن يبدو أنني لم أتمكن من تشغيلها في Firefox 3 أو 3.5beta4 أو Google Chrome، سواء في نوافذ تصحيح الأخطاء أو في صفحة ويب اختبارية.

فايربوج:

>>> window.myAlias = document.getElementById
function()
>>> myAlias('item1')
>>> window.myAlias('item1')
>>> document.getElementById('item1')
<div id="item1">

إذا قمت بوضعه في صفحة ويب، فإن استدعاء myAlias ​​يعطيني هذا الخطأ:

uncaught exception: [Exception... "Illegal operation on WrappedNative prototype object" nsresult: "0x8057000c (NS_ERROR_XPC_BAD_OP_ON_WN_PROTO)" location: "JS frame :: file:///[...snip...]/test.html :: <TOP_LEVEL> :: line 7" data: no]

Chrome (مع إدراج >>> للتوضيح):

>>> window.myAlias = document.getElementById
function getElementById() { [native code] }
>>> window.myAlias('item1')
TypeError: Illegal invocation
>>> document.getElementById('item1')
<div id=?"item1">?

وفي صفحة الاختبار، أحصل على نفس "الاستدعاء غير القانوني".

أفعل شيئا خاطئا؟هل يمكن لأي شخص آخر إعادة إنتاج هذا؟

ومن الغريب أيضًا أنني حاولت للتو وهو يعمل في IE8.

هل كانت مفيدة؟

المحلول

يجب عليك ربط هذه الطريقة بكائن المستند.ينظر:

>>> $ = document.getElementById
getElementById()
>>> $('bn_home')
[Exception... "Cannot modify properties of a WrappedNative" ... anonymous :: line 72 data: no]
>>> $.call(document, 'bn_home')
<body id="bn_home" onload="init();">

عندما تقوم بإنشاء اسم مستعار بسيط، يتم استدعاء الوظيفة على الكائن العام، وليس على كائن المستند.استخدم تقنية تسمى عمليات الإغلاق لإصلاح ذلك:

function makeAlias(object, name) {
    var fn = object ? object[name] : null;
    if (typeof fn == 'undefined') return function () {}
    return function () {
        return fn.apply(object, arguments)
    }
}
$ = makeAlias(document, 'getElementById');

>>> $('bn_home')
<body id="bn_home" onload="init();">

بهذه الطريقة لا تفقد الإشارة إلى الكائن الأصلي.

وفي عام 2012، هناك الجديد bind طريقة من ES5 تتيح لنا القيام بذلك بطريقة أكثر روعة:

>>> $ = document.getElementById.bind(document)
>>> $('bn_home')
<body id="bn_home" onload="init();">

نصائح أخرى

لقد بحثت بعمق لفهم هذا السلوك بالذات وأعتقد أنني وجدت تفسيرًا جيدًا.

قبل أن أتطرق إلى سبب عدم قدرتك على استخدام الاسم المستعار document.getElementById, سأحاول شرح كيفية عمل وظائف/كائنات JavaScript.

عندما تقوم باستدعاء وظيفة JavaScript، يحدد مترجم JavaScript نطاقًا ويمرره إلى الوظيفة.

النظر في الوظيفة التالية:

function sum(a, b)
{
    return a + b;
}

sum(10, 20); // returns 30;

يتم الإعلان عن هذه الوظيفة في نطاق النافذة وعند استدعائها تكون قيمتها this داخل الدالة المبلغ ستكون العالمية Window هدف.

بالنسبة للدالة "المجموع"، لا يهم قيمة "هذا" لأنها لا تستخدمها.


النظر في الوظيفة التالية:

function Person(birthDate)
{
    this.birthDate = birthDate;    
    this.getAge = function() { return new Date().getFullYear() - this.birthDate.getFullYear(); };
}

var dave = new Person(new Date(1909, 1, 1)); 
dave.getAge(); //returns 100.

عند استدعاء وظيفة dave.getAge، يرى مترجم JavaScript أنك تستدعي وظيفة getAge على dave كائن، لذلك يحدد this ل dave ويدعو getAge وظيفة. getAge() سوف يعود بشكل صحيح 100.


ربما تعلم أنه في JavaScript يمكنك تحديد النطاق باستخدام ملف apply طريقة.دعونا نحاول ذلك.

var dave = new Person(new Date(1909, 1, 1)); //Age 100 in 2009
var bob = new Person(new Date(1809, 1, 1)); //Age 200 in 2009

dave.getAge.apply(bob); //returns 200.

في السطر أعلاه، بدلاً من السماح لـ JavaScript بتحديد النطاق، فإنك تقوم بتمرير النطاق يدويًا باعتباره bob هدف. getAge سوف يعود الآن 200 على الرغم من أنك "اعتقدت" أنك اتصلت getAge على ال dave هدف.


ما الهدف من كل ما سبق؟يتم ربط الوظائف "بشكل فضفاض" بكائنات JavaScript الخاصة بك.على سبيل المثاليمكنك ان تفعل

var dave = new Person(new Date(1909, 1, 1));
var bob = new Person(new Date(1809, 1, 1));

bob.getAge = function() { return -1; };

bob.getAge(); //returns -1
dave.getAge(); //returns 100

لنأخذ الخطوة التالية.

var dave = new Person(new Date(1909, 1, 1));
var ageMethod = dave.getAge;

dave.getAge(); //returns 100;
ageMethod(); //returns ?????

ageMethod التنفيذ يلقي خطأ!ماذا حدث؟

إذا قرأت نقاطي أعلاه بعناية، ستلاحظ ذلك dave.getAge تم استدعاء الطريقة مع dave مثل this الكائن بينما لم يتمكن JavaScript من تحديد "النطاق" الخاص به ageMethod تنفيذ.لذلك تم تمرير "النافذة" العالمية كـ "هذا".لم يكن window لا يملك birthDate ملكية، ageMethod سوف يفشل التنفيذ.

كيف يمكن اصلاح هذا؟بسيط،

ageMethod.apply(dave); //returns 100.

هل كل ما سبق منطقي؟إذا كان الأمر كذلك، فستتمكن من شرح سبب عدم قدرتك على استخدام الاسم المستعار document.getElementById:

var $ = document.getElementById;

$('someElement'); 

$ يسمى مع window مثل this و إذا getElementById ومن المتوقع التنفيذ this يكون document, ، سوف تفشل.

مرة أخرى لإصلاح هذا، يمكنك القيام به

$.apply(document, ['someElement']);

فلماذا يعمل في Internet Explorer؟

لا أعرف التنفيذ الداخلي لـ getElementById في IE، ولكن تعليق في مصدر jQuery (inArray تنفيذ الطريقة) يقول أنه في IE، window == document.إذا كان هذا هو الحال، ثم التعرج document.getElementById يجب أن تعمل في IE.

ولتوضيح ذلك بشكل أكبر، قمت بإنشاء مثال مفصل.الق نظرة على Person وظيفة أدناه.

function Person(birthDate)
{
    var self = this;

    this.birthDate = birthDate;

    this.getAge = function()
    {
        //Let's make sure that getAge method was invoked 
        //with an object which was constructed from our Person function.
        if(this.constructor == Person)
            return new Date().getFullYear() - this.birthDate.getFullYear();
        else
            return -1;
    };

    //Smarter version of getAge function, it will always refer to the object
    //it was created with.
    this.getAgeSmarter = function()
    {
        return self.getAge();
    };

    //Smartest version of getAge function.
    //It will try to use the most appropriate scope.
    this.getAgeSmartest = function()
    {
        var scope = this.constructor == Person ? this : self;
        return scope.getAge();
    };

}

ل Person وظيفة أعلاه، وهنا كيفية عمل مختلف getAge سوف تتصرف الأساليب.

لنقم بإنشاء كائنين باستخدام Person وظيفة.

var yogi = new Person(new Date(1909, 1,1)); //Age is 100
var anotherYogi = new Person(new Date(1809, 1, 1)); //Age is 200

console.log(yogi.getAge()); //Output: 100.

بشكل مستقيم للأمام، تحصل طريقة getAge yogi كائن كما this والمخرجات 100.


var ageAlias = yogi.getAge;
console.log(ageAlias()); //Output: -1

مجموعات مترجم جافا سكريبت window كائن كما this و لنا getAge سوف تعود الطريقة -1.


console.log(ageAlias.apply(yogi)); //Output: 100

إذا قمنا بتعيين النطاق الصحيح، يمكنك استخدامه ageAlias طريقة.


console.log(ageAlias.apply(anotherYogi)); //Output: 200

إذا مررنا كائنًا لشخص آخر، فسيظل يحسب العمر بشكل صحيح.

var ageSmarterAlias = yogi.getAgeSmarter;    
console.log(ageSmarterAlias()); //Output: 100

ال ageSmarter وظيفة القبض على الأصلي this كائن لذا لا داعي للقلق الآن بشأن توفير النطاق الصحيح.


console.log(ageSmarterAlias.apply(anotherYogi)); //Output: 100 !!!

المشكلة مع ageSmarter هو أنه لا يمكنك أبدًا تعيين النطاق على كائن آخر.


var ageSmartestAlias = yogi.getAgeSmartest;
console.log(ageSmartestAlias()); //Output: 100
console.log(ageSmartestAlias.apply(document)); //Output: 100

ال ageSmartest ستستخدم الدالة النطاق الأصلي إذا تم توفير نطاق غير صالح.


console.log(ageSmartestAlias.apply(anotherYogi)); //Output: 200

سيظل بإمكانك اجتياز آخر Person يعترض على getAgeSmartest. :)

هذه إجابة قصيرة.

ما يلي يصنع نسخة من (إشارة إلى) الوظيفة.المشكلة هي أن الوظيفة الآن قيد التشغيل window كائن عندما تم تصميمه للعيش على document هدف.

window.myAlias = document.getElementById

البدائل هي

  • لاستخدام غلاف (سبق أن ذكره فابيان ميناجر)
  • أو يمكنك استخدام اسمين مستعارين.

    window.d = document // A renamed reference to the object
    window.d.myAlias = window.d.getElementById
    

إجابة قصيرة أخرى، فقط للالتفاف/التعرج console.log وطرق تسجيل مماثلة.كلهم يتوقعون أن يكونوا في console سياق.

هذا صالح للاستخدام عند التغليف console.log مع بعض الإجراءات الاحتياطية، في حال واجهت أنت أو المستخدمون مشكلة ما باستخدام متصفح لا يدعمه (دائمًا)..هذا ليس حلاً كاملاً لهذه المشكلة، لأنه يحتاج إلى عمليات فحص موسعة واحتياطية - قد يختلف عدد الأميال المقطوعة.

مثال باستخدام التحذيرات

var warn = function(){ console.warn.apply(console, arguments); }

ثم استخدمه كالمعتاد

warn("I need to debug a number and an object", 9999, { "user" : "Joel" });

إذا كنت تفضل رؤية وسيطات التسجيل ملفوفة في مصفوفة (أنا أفعل ذلك في معظم الأوقات)، فاستبدلها .apply(...) مع .call(...).

يجب أن تعمل مع console.log(), console.debug(), console.info(), console.warn(), console.error().أنظر أيضا console على MDN.

بالإضافة إلى الإجابات الرائعة الأخرى، هناك طريقة jQuery بسيطة $.proxy.

يمكنك الاسم المستعار مثل هذا:

myAlias = $.proxy(document, 'getElementById');

أو

myAlias = $.proxy(document.getElementById, document);

أنت في الواقع لا أستطيع "الاسم المستعار الخالص" دالة على كائن محدد مسبقًا.لذلك، فإن أقرب طريقة للاستعارة التي يمكنك الحصول عليها دون التفاف هي البقاء داخل نفس الكائن:

>>> document.s = document.getElementById;
>>> document.s('myid');
<div id="myid">
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top