9

次のようなコードでJavaScriptの「new」演算子をシミュレートしようとしました。

Function.method('new', function ( ) {
    var objPrototype = Object.create(this.prototype);
    var instance = this.apply(objPrototype, arguments);

    return instance;
});

ただし、すべてのケースをカバーするには、returnステートメントは次のようになります。

return (typeof instance === 'object' && instance ) || objPrototype;

今度はテストのために:

var SomeClass = function (param1, param2) {
    this.param1 = param1;
    this.param2 = param2;
};

var test1 = String.new('test1'); //in this case, the "instance" variable is an object
var test2 = SomeClass.new('test1', 'test2'); // in this case, the "instance" variable is undefined

これはまさに「新しい」演算子が行うことですか?カバーするケースは残っていますか?

4

4 に答える 4

9

仕様から:

11.2.2新しいオペレーター Ⓣ</a> Ⓡ</a> Ⓖ</a>

プロダクションNewExpressionnew NewExpressionは次のように評価されます。

  1. NewExpressionを評価した結果をrefとします
  2. コンストラクターGetValueref)とします。
  3. Typeコンストラクター)がObjectでない場合は、 TypeError例外をスローします。
  4. コンストラクターが[[Construct]]内部メソッドを実装していない場合は、 TypeError例外をスローします。
  5. コンストラクターで[[Construct]]内部メソッドを呼び出した結果を返します。引数は指定しません(つまり、引数の空のリスト)。

プロダクションMemberExpressionnew MemberExpression引数 は、次のように評価されます。

  1. MemberExpressionを評価した結果をrefとします
  2. コンストラクターGetValueref)とします。
  3. argListを引数の評価の結果とし、引数値の内部リストを生成します(11.2.4
  4. Typeコンストラクター)がObjectでない場合は、 TypeError例外をスローします。
  5. コンストラクターが[[Construct]]内部メソッドを実装していない場合は、 TypeError例外をスローします。
  6. コンストラクターで[[Construct]]内部メソッドを呼び出した結果を返し、引数値としてリストargListを指定します。

いずれの場合も、すべての手順が正しく実行されます。

var objPrototype = Object.create(this.prototype);    // 1-4 1-5
var instance = this.apply(objPrototype, arguments);  // 5   6

関心のあるポイントは2です。州
の仕様:[[construct]]

関数オブジェクトFの[[Construct]]内部メソッドが、空の可能性のある引数のリストを使用して呼び出されると、次の手順が実行されます。

  • objを新しく作成されたネイティブECMAScriptオブジェクトとします。
    . . .
  • 結果をFの[[Call]]内部プロパティを呼び出し、この値としてobjを提供し、[[Construct]]に渡された引数リストをargsとして提供した結果とします。
  • Typeresult)がObjectの場合、 resultを返します。
  • objを返します。

typeof objはオブジェクトではありませんが"object"nullを返します。nullただし、nullは偽の値であるため、コードも意図したとおりに機能します。

return (typeof instance === 'object' && instance ) || objPrototype;
于 2012-05-03T10:07:54.060 に答える
8

new演算子は関数Farguments:を取りますnew F(arguments...)。それは3つの簡単なステップを実行します:

  1. クラスのインスタンスを作成します。__proto__これは、プロパティがに設定された空のオブジェクト F.prototypeです。インスタンスを初期化します。

  2. 関数Fは、渡された引数とインスタンスとして設定された状態で呼び出されます。

  3. インスタンスを返す

new演算子の機能を理解したので、Javascriptで実装できます。

    function New (f) {
/*1*/  var n = { '__proto__': f.prototype };
       return function () {
/*2*/    f.apply(n, arguments);
/*3*/    return n;
       };
     }

そして、それが機能することを確認するための小さなテストです。

function Point(x, y) {
  this.x = x;
  this.y = y;
}
Point.prototype = {
  print: function () { console.log(this.x, this.y); }
};

var p1 = new Point(10, 20);
p1.print(); // 10 20
console.log(p1 instanceof Point); // true

var p2 = New (Point)(10, 20);
p2.print(); // 10 20
console.log(p2 instanceof Point); // true
于 2012-05-03T09:58:09.610 に答える
2

ここでの答えはすべて、作成時にすべて存在していた標準のES5に有効ですが、すべてのES6コンテキストで機能する一般的なソリューションではないため、それらを拡張したいと思います。簡単な答えは、質問からのコードです:

Function.method('new', function ( ) {
  var objPrototype = Object.create(this.prototype);
  var instance = this.apply(objPrototype, arguments);

  return instance;
});

標準のES6環境では、次のように実装する方が適切です。

Function.method('new', function ( ) {
  return Reflect.construct(this, arguments);
});

これは間違いなく物事を単純化します。

Reflect.constructシステムの一部としてES6で導入されましたが、Proxyこのような一般的な用途があります。

これが現在推奨されている方法である理由は、.applyすべてのタイプの関数で機能しなくなった単純な理由です。前の答えnewは、引数を初期化するために内部言語関数を呼び出すことを説明して[[Construct]]います。を使用するアプローチは、基本的に、オブジェクトを手動で作成してから、の代わりにその内部メソッドを使用する関数を呼び出すことによって.apply処理される自動オブジェクト作成および呼び出しロジックを置き換えます。[[Construct]][[Call]][[Construct]]

関数の呼び出しは、ES6で変更されたものの一部です。ES5では、構築するのは通常のfunction Foo(){}値だけなので、それについて推測することができます。ES6では、class Foo {}構文が導入され、クラス構文によって作成されたコンストラクター関数にはより多くの制限が設定されているため、ES5についての仮定は適用されません。最も重要なことは、ES6クラスの使用が明示的に禁止されていること[[Call]]です。次の手順を実行すると、例外がスローされます。

class Foo {}
Foo();

.callこれはとと同じ問題.applyです。それらは関数構築関数ではなく、関数呼び出し関数です。したがって、ES6クラスでそれらを使用しようとすると、例外がスローされます。

Reflect.constructではなく実際に呼び出すことでこれらの問題を回避しますが、なしで使用できるAPIを介して公開します。[[Construct]][[Call]]new

于 2017-08-03T22:04:01.890 に答える
0

これは、このアプローチを使用する代わりの__proto__方法です。OPが最初に始まった方法と一致しています...

function New(fn) {
    var newObj = Object.create(fn.prototype);
    return function() {
        fn.apply(newObj, arguments);
        return newObj;
    };
}

これははるかにクリーンな方法であり、プロトタイプチェーンテストにも合格します。

于 2014-12-02T05:30:55.013 に答える