9

Javascript スタックトレース ライブラリを作成しています。ライブラリは、特定のオブジェクトまたは関数がプログラマーによって作成されたのか、それとも環境の一部としてそこにあったのか (組み込みオブジェクトを含む) を検出する必要があります。ホスト オブジェクトは、予測できない動作のために少し問題になりつつあるため、Javascript の特定のオブジェクトがホスト オブジェクトであるかどうかを判断する環境にとらわれない方法を探しています ( ECMAScript 3 - 4.3.8 を参照)。ただし、ホスト オブジェクトをネイティブ オブジェクトやプリミティブ値と区別することは、他のプロジェクトのプログラマーにとって、特にブラウザーのない環境で役立つので、ホスト オブジェクトが私のライブラリーで引き起こす問題やプログラマーを区別することではなく、その点に焦点を当てたいと思います。 -作成されたオブジェクト。

これまでのところ、javascript コードを実行している環境に依存するソリューションしか思いつきませんでした。例えば:

// IE Only: does not implement valueOf() in Host Objects
var isHost = (typeof obj === 'object' && typeof obj.valueOf === 'undefined');

// Firefox Only: Host objects have own constructor
var isHost = (obj.constructor && obj.hasOwnProperty('constructor'));

jQuery 独自のisPlainObject()メソッドも環境に依存しており、ロジックがかなり複雑であることに気付きました。

おそらくこれは、ホスト オブジェクトを持つ獣の性質によるものです (その動作は環境によって定義されるため)。ソリューションの準備ができています。

そう。ホスト オブジェクトをテストするための、プラットフォームに依存しないシンプルなソリューションを知っている人はいますか? また、Node や Rhino などのブラウザレス環​​境で実行すると、さらに効果的です。

可能なアプローチ(うまくいかないかもしれません):

  • ホスト オブジェクトの特性をテストすることは、その動作に関する仕様がないことを考えると、失われた原因のように思えますが、オブジェクトが ES3 仕様の一部であるかどうかをテストすることは可能かもしれません。
  • 非常に具体的に定義されていることを考慮して使用してみObject.prototype.toString()ましたが、一部の環境 (IE など) がネイティブ オブジェクトとホスト オブジェクトに対して同じ値を返すことを選択するため、結果は決定的ではありません。
  • これはconstructor、プロトタイプ チェーンを介したオブジェクトのアルティメットが本当にinstanceof Function.
4

5 に答える 5

5

ホストオブジェクトの定義を見ると、「ECMAScriptの実行環境を完成させるためにホスト環境から提供されるオブジェクト」です。— オブジェクトがホストかネイティブかを判断する簡単な方法がないことは明らかです。

ネイティブ オブジェクトとは異なり、ホスト オブジェクトは実装固有の方法で内部プロパティ ([[Prototype]]、[[Class]] など) を定義します。それは、仕様がこれを可能にしているからです。ただし、ホスト オブジェクトが実装固有の方法で内部動作を実装するための "MUST" 要件はありません。これは「5 月」タイプの要件です。したがって、これに頼ることはできません。これらのオブジェクトは、「奇妙な」動作をする場合としない場合があります。言う方法はありません。

過去にホスト オブジェクトを検出する試みはほとんどありませんでしたが、それらはすべて特定の環境 (MSHTML DOM がその 1 つです) の観察に依存していることは明らかです。に。Peter Michauxは、ほとんどの推論をここに文書化しました(「ホスト オブジェクトの機能テスト」セクションを参照してください)。悪名高いのtypeof ... == "unknown"は、MSHTML DOM とその ActiveX ベースのホスト オブジェクトです。Peter は主にブラウザ スクリプトのコンテキストでホスト オブジェクトについて話し、「これはホスト メソッドですか?」、「これはホストコレクションオブジェクトですか」などにチェックを絞り込んでいることに注意してください。

環境によっては、ホスト オブジェクトが継承されないObject.prototype(チェックが容易になる) か、エラーをスローする特定のプロパティ (IE の一部の "インターフェイス" オブジェクトの "プロトタイプ" など) を持っているか、アクセス時にそれ自体がエラーをスローすることさえあります。

オブジェクトが仕様で定義されているものの 1 つであるかどうかを確認し、そうでない場合はホストと見なすことができるように思えるかもしれません。しかし、それはあまり役に立ちません。組み込みではないオブジェクトのみを提供します。これらの非標準オブジェクトの一部は、依然としてネイティブである可能性があります (仕様で説明されている通常のセマンティクスを実装することを意味します)。

あなたの最善の策は、ホスト オブジェクトが敏感な可能性があるアプリ/スクリプトの特定の動作をテストすることです。これは常に最も安全な方法です。オブジェクトから何かにアクセスする予定はありますか? オブジェクトから何かを削除しますか? オブジェクトに何かを追加しますか? それをテストします。それが機能するかどうかを確認してください。そうでない場合は、ホスト オブジェクトを扱っている可能性があります。

于 2011-12-28T05:58:37.257 に答える
4

isNativeこれは、スタック トレース ライブラリの問題を解決しますが、ここtoStringに投稿された質問には十分に答えません。後者でこのアプローチが失敗するのは、ホスト オブジェクト自体ではない 、 、 、 などObjectDateすべてStringの組み込み型定義を除外することです。Mathまた、この解決策は、環境がネイティブ/組み込み関数定義をどのように出力するかに依存します (関数が機能するには、「[ネイティブ コード]」が含まれている必要があります)。関数の動作が異なるため、名前が変更されましisUserObjectた。

// USER OBJECT DETECTION

function isUserObject(obj) {

    // Should be an instance of an Object
    if (!(obj instanceof Object)) return false;

    // Should have a constructor that is an instance of Function
    if (typeof obj.constructor === 'undefined') return false;
    if (!(obj.constructor instanceof Function)) return false;

    // Avoid built-in functions and type definitions
    if (obj instanceof Function && 
      Function.prototype.toString.call(obj).indexOf('[native code]') > -1) 
          return false;

    return true;
}

// CHECK IF AN OBJECT IS USER-CREATED OR NOT

if (typeof myObject === 'object' || typeof myObject === 'function')
   alert(isUserObject(myObject) ? 'User Object' : 'Non-user Object'); 

これは、さまざまなブラウザーでこれをテストするために使用できるJsFiddle テストのリストです。

// ASSERT HELPER FUNCTION

var n = 0;
function assert(condition, message) {
    n++;
    if (condition !== true) {
       document.write(n + '. FAILS: ' + (message || '(no message)') + '<br/>');
    } else {
       document.write(n + '. PASS: ' + (message || '(no message)') + '<br/>');
    }
}

// USER CREATED OBJECTS

assert(isUserObject({}), '{} -- Plain object');
assert(isUserObject(function() {}), 'function() {} -- Plain function');
assert(isUserObject([]), '[] -- Plain array');

assert(isUserObject(/regex/), '/regex/ - Native regex');
assert(isUserObject(new Date()), 'new Date() - Native date object through instantiation');

assert(isUserObject(new String('string')), 'new String("string") - Native string object through instantiation');
assert(isUserObject(new Number(1)), 'new Number(1) - Native number object through instantiation');
assert(isUserObject(new Boolean(true)), 'new Boolean(true) - Native boolean object through instantiation');
assert(isUserObject(new Array()), 'new Array() - Native array object through instantiation');
assert(isUserObject(new Object()), '{} -- new Object() - Native object through instantiation');
assert(isUserObject(new Function('alert(1)')), '{} -- Native function through instantiation');

// USER OBJECT INSTANTIATION AND INHERITANCE

var Animal = function() {};
var animal = new Animal();

var Dog = function() {};
Dog.prototype = animal;
var dog = new Dog();

assert(isUserObject(Animal), 'Animal -- User defined type');
assert(isUserObject(animal), 'animal -- Instance of User defined type');

assert(isUserObject(Dog), 'Dog -- User defined inherited type');
assert(isUserObject(dog), 'dog -- Instance of User defined inherited type');

// BUILT IN OBJECTS

assert(!isUserObject(Object), 'Object -- Built in');
assert(!isUserObject(Array), 'Array -- Built in');
assert(!isUserObject(Date), 'Date -- Built in');
assert(!isUserObject(Boolean), 'Boolean -- Built in');
assert(!isUserObject(String), 'String -- Built in');
assert(!isUserObject(Function), 'Function -- Built in');

// PRIMITIVE TYPES 

assert(!isUserObject('string'), '"string" - Primitive string');
assert(!isUserObject(1), '1 - Primitive number');
assert(!isUserObject(true), 'true - Primitive boolean');
assert(!isUserObject(null), 'null - Primitive null');
assert(!isUserObject(NaN), 'NaN - Primitive number NotANumber');
assert(!isUserObject(Infinity), 'Infinity - Primitive number Infinity');
assert(!isUserObject(undefined), 'undefined - Primitive value undefined');

// HOST OBJECTS

assert(!isUserObject(window), 'window -- Host object');
assert(!isUserObject(alert), 'alert -- Host function');
assert(!isUserObject(document), 'document -- Host object');
assert(!isUserObject(location), 'location -- Host object');
assert(!isUserObject(navigator), 'navigator -- Host object');
assert(!isUserObject(parent), 'parent -- Host object');
assert(!isUserObject(frames), 'frames -- Host object');​
于 2012-02-15T02:27:40.460 に答える
2

ほぼ解決

ほぼこれを機能させることができました。

このソリューションは、ホスト オブジェクトがネイティブ オブジェクトと区別できない場合があるという点で不十分です。以下のコードは、Webkit エンジンが (今のところ) ネイティブのものと同じように見える関数をisNative(window.alert)定義しているため、Chrome でテストすると失敗します。alert

ES3 に従ってプレーンな JavaScript を使用し、オブジェクトがネイティブであることのテストに基づいています (ホスト オブジェクトではなく)。ただし、ホスト オブジェクトの ES3 定義によると、「ネイティブでないオブジェクトはすべてホスト オブジェクトです。」この関数は、ホスト オブジェクトを検出するために使用できます。

// ISNATIVE OBJECT DETECTION

function isNative(obj) {

    switch(typeof obj) {
        case 'number': case 'string': case 'boolean':
            // Primitive types are not native objects
            return false;
    }  

    // Should be an instance of an Object
    if (!(obj instanceof Object)) return false;

    // Should have a constructor that is an instance of Function
    if (typeof obj.constructor === 'undefined') return false;
    if (!(obj.constructor instanceof Function)) return false;

    return true;
}

// CHECK IF AN OBJECT IS HOST OR NATIVE

if (typeof myObject === 'object' || typeof myObject === 'function')
   alert(isNative(myObject) ? 'Native Object' : 'Host Object'); 

IE / Firefox / Chrome でこれをテストするために使用できるJsFiddle テストのリストを次に示します。

ブラウザ以外の環境は少し面倒なのでテストしていませんが、コードは非常に基本的なものなので、問題はないと思います。

// ASSERT HELPER FUNCTION

var n = 0;
function assert(condition, message) {
    n++;
    if (condition !== true) {
       document.write(n + '. FAILS: ' + (message || '(no message)') + '<br/>');
    } else {
       document.write(n + '. PASS: ' + (message || '(no message)') + '<br/>');
    }
}

// USER CREATED OBJECTS

assert(isNative({}), '{} -- Plain object');
assert(isNative(function() {}), 'function() {} -- Plain function');
assert(isNative([]), '[] -- Plain array');

assert(isNative(/regex/), '/regex/ - Native regex');
assert(isNative(new Date()), 'new Date() - Native date object through instantiation');

assert(isNative(new String('string')), 'new String("string") - Native string object through instantiation');
assert(isNative(new Number(1)), 'new Number(1) - Native number object through instantiation');
assert(isNative(new Boolean(true)), 'new Boolean(true) - Native boolean object through instantiation');
assert(isNative(new Array()), 'new Array() - Native array object through instantiation');
assert(isNative(new Object()), '{} -- new Object() - Native object through instantiation');
assert(isNative(new Function('alert(1)')), '{} -- Native function through instantiation');

// USER OBJECT INSTANTIATION AND INHERITANCE

var Animal = function() {};
var animal = new Animal();

var Dog = function() {};
Dog.prototype = animal;
var dog = new Dog();

assert(isNative(Animal), 'Animal -- User defined type');
assert(isNative(animal), 'animal -- Instance of User defined type');

assert(isNative(Dog), 'Dog -- User defined inherited type');
assert(isNative(dog), 'dog -- Instance of User defined inherited type');

// BUILT IN OBJECTS

assert(isNative(Object), 'Object -- Built in');
assert(isNative(Array), 'Array -- Built in');
assert(isNative(Date), 'Date -- Built in');
assert(isNative(Boolean), 'Boolean -- Built in');
assert(isNative(String), 'String -- Built in');
assert(isNative(Function), 'Function -- Built in');

// PRIMITIVE TYPES 

assert(!isNative('string'), '"string" - Primitive string');
assert(!isNative(1), '1 - Primitive number');
assert(!isNative(true), 'true - Primitive boolean');
assert(!isNative(null), 'null - Primitive null');
assert(!isNative(NaN), 'NaN - Primitive number NotANumber');
assert(!isNative(Infinity), 'Infinity - Primitive number Infinity');
assert(!isNative(undefined), 'undefined - Primitive value undefined');

// HOST OBJECTS

assert(!isNative(window), 'window -- Host object');
assert(!isNative(alert), 'alert -- Host function'); // fails on chrome
assert(!isNative(document), 'document -- Host object');
assert(!isNative(location), 'location -- Host object');
assert(!isNative(navigator), 'navigator -- Host object');
assert(!isNative(parent), 'parent -- Host object');
assert(!isNative(frames), 'frames -- Host object');
于 2012-01-04T11:33:17.383 に答える
0

私は、すべての状況に当てはまるとは限らないという考えを持っています。

JSフレームワークと同じように、スクリプトが最初に実行されるスクリプトであることを確認し、クロージャでラップします。
次に、グローバルスコープ内のすべてのオブジェクトをループし(ブラウザ以外のものを使用している場合はwindow未定義になります。したがって、スクリプトの開始時にwindow = this)を実行し、その子をループします。あなた以外のすべてのオブジェクトがホストオブジェクトになります!次に、それをローカルデータベースに追加したり、保存して、将来使用するために実行中の環境に関連付けたりすることもできます。

于 2011-12-27T15:36:46.233 に答える
0

ホスト オブジェクトの性質上、環境にとらわれずにホスト オブジェクトを検出する簡単な方法はないと私は考えています。興味がある場合は、SO に関するこのディスカッションを参照してください。

お気づきのように、jQuery プロジェクトもホスト オブジェクトを検出しようとしましたが、同様の問題が発生しました。そのバグページでの議論は非常に説得力があります。

于 2011-12-27T15:11:36.707 に答える