コンストラクタとプロトタイプ
1、コンストラクタ、静的およびインスタンスメンバ
ES6のための前に、通常は特別な機能は、オブジェクトとその特性を定義して、オブジェクトを作成するコンストラクタを使用するには、コンストラクタと呼ばれます。パッケージの内部に他のオブジェクト指向言語、抽象オブジェクトの属性とメソッドのように。
function Person(uname, age) {
this.uname = uname;
this.age = age;
this.say = function() {
console.log('我叫' + this.uname + ',今年' + this.age + '岁。');
}
}
var zhangsan = new Person('张三', 18);
zhangsan.say(); //输出:我叫张三,今年18岁。
var lisi = new Person('李四', 20);
lisi.say(); //输出:我叫李四,今年20岁。
あなたがオブジェクトを作成すると、(代わりに呼び出すのでは)常に一緒に新しいコンストラクタを使用します。私たちは、このことによって、新しいオブジェクトに割り当てることができ、コードは関数本体を実行し、新しいオブジェクト(リターンを記述する必要はありません)に戻るように、新しい、新しいオブジェクトを作成し、これは、新しいオブジェクトを指します。
メンバー(プロパティ/メソッド)は、この追加機能の内部構造によってインスタンスメンバー(プロパティ/メソッドは)だけインスタンス化されたオブジェクトがアクセスすることができるように、コンストラクタはそのようなメンバーではないと呼びます。
>> Person.uname
undefined
我々はまた、静的メンバ(プロパティ/メソッド)として知られている独自のメンバー、のコンストラクタに追加できるだけコンストラクタ自体によってアクセスすることができます。
2、試作品
上記の例では、我々はコンストラクタがzhangsanとリージ2つのオブジェクトを作成し、使用し、彼らは自分の独立したプロパティとメソッドを持っています。関数が複合データ型であるため、インスタンスメソッドのために、それは専用のメモリ記憶機能を開きます。メソッドzhangsanとリージが独立しているので、また、そのそれぞれに同じことを行う、彼らがコードの同じセットであっても、二つのメモリを占有する方法と、この方法はzhangsanのリージを言うと言います。
>> zhangsan.say === lisi.say
false
ただ、インスタンスメソッド、作成した複数のオブジェクト、多くの無駄なスペースより多くのだと思います。スペースを節約するために、我々はすべてのオブジェクトが同じメソッドを呼び出したいと言います。この目的を達成するためには、プロトタイプを使用する必要があります。
また、プロトタイプオブジェクトと呼ばれるオブジェクトであり、各コンストラクタが別のオブジェクトを指して、プロトタイプの性質を有する、プロトタイプと呼ばれます。また、プロトタイプオブジェクトを指すれる__proto__プロパティのインスタンスがあり、一方(以下、混乱を作成しないために、コンストラクタは、オブジェクトのインスタンスが呼び出された作成されます)。__、一般的に呼ばれるオブジェクトを指し示すプロトタイプproto__を区別するために、プロトタイプはプロトタイプオブジェクトと呼ばれます。
そのコンストラクタからプロトタイプオブジェクト参照を記録するために、コンストラクタ自体に戻って参照しプロトタイプオブジェクトコンストラクタ性もあります。この方法では、コンストラクタ、プロトタイプおよび実施例は、三角形を構成します。
以下のように定義されるプロトタイプオブジェクトコンストラクタの導入後:
function Person(uname, age) {
this.uname = uname;
this.age = age;
}
Person.prototype.say = function() {
return '我叫' + this.uname + ',今年' + this.age + '岁。';
};
var zhangsan = new Person('张三', 18);
console.log(zhangsan.say());
var lisi = new Person('李四', 20);
console.log(lisi.say());
console.log(zhangsan.say === lisi.say); //输出:true
オブジェクトのメンバーを探していたとき、彼らは、彼らは上の__proto__ことによって、オブジェクトのプロトタイプを探しに行っていない場合は、すべての最初は、オブジェクトで自分自身を見ています。プロトタイプオブジェクトのコンストラクタ関数が定義されたとは、すべてのインスタンスで共有されます。
一般的に、我々は、コンストラクタ、例示的な方法にプロトタイプオブジェクトへのインスタンスの属性を定義します。
3、プロトタイプチェーン
プロトタイプオブジェクトは、独自のプロトタイプのObject.prototypeオブジェクトプロトタイプオブジェクトのポイントを持つオブジェクトです。
>> Person.prototype.__proto__ === Object.prototype
true
>> Person.prototype.__proto__.constructor === Object
true
言い換えれば、人のプロトタイプオブジェクトはObjectコンストラクタで作成されています。
遡及のObject.prototypeプロトタイプをダウン続行します。
>> Object.prototype.__proto__
null
最後に最後に来ます。振り返ってみると、私たちは、この例zhangsanからさかのぼって開始しました:
zhangsan .__ proto__人のプロトタイプオブジェクト。
zhangsan .__プロト__.__のObject.prototypeオブジェクトのプロトタイプオブジェクトへのproto__ポイント
zhangsan .__ので__.__ので__.__ proto__指向はnull
このチェーン構造は、プロトタイプチェーンと呼ばれます。
オブジェクトのルックアップ機構のメンバーは、プロトタイプチェーンに依存しています:オブジェクトのプロパティ(またはメソッド)にアクセスすると、まず自身がそのような性質を持っていないオブジェクトを見つけ、そのプロトタイプを見つけることがない場合、それはプロトタイプオブジェクトのプロトタイプを見つけることがない場合は、にそのようなプッシュがこれまでにnullを発見された、この時間は、undefinedを返します。__proto__プロパティは、一方向の検索メカニズムのためのコースを提供しています。
あなたはプロトタイプチェーンの概念を持っている、そして新しい再び実行時に何が行われたかを確認してください。
1.メモリ内の新しい空のオブジェクトを作成します。
2.オブジェクトポイントのプロパティ__proto__プロトタイプオブジェクトのコンストラクタ。
3.新しいオブジェクトにこのポイントのコンストラクタをしてみましょう。
4.新しいオブジェクトにメンバーを追加するために、コンストラクタコードを実行して、新しいオブジェクトを返します。
第二に、継承
ES6は前に、どのようにクラスの継承を実現するには?これは、メソッドを呼び出すために使用します。
function.call(thisArg、ARG1、ARG2、...)
thisArg:このランタイム値の関数で指定した関数。
ARG1、ARG2、...:関数の引数に渡されます。
私たちは、発信者、ウィンドウまたは異なるシナリオに従って未定義を指し、内部のこのプロパティと呼ばれる機能があることを知っています。このメソッドを呼び出す別の関数が呼び出されると指定することを可能にします。
var obj = {};
function f () {
console.log(this === window, this === obj);
}
f(); //输出:true false
f.call(obj); //输出:false true
関数f、この時点でウィンドウを呼び出して通常の方法では、呼び出すために呼び出す、これはobjに指しています。
使用を呼び出して、親下部のコンストラクタ関数を呼び出すことにより、親のコンストラクタ内のメンバーの初期化部分を完了、親クラスのインスタンスサブクラスのインスタンスで、この内側のポイントを作ります。
ビューの例としては、私たちは人のすべての属性とメソッドを持っているだけでなく、グレードのプロパティは、テストの受験に使用される方法を等級を示す、追加するだけでなく、人からStudentクラスを継承しています:
function Person(uname, age) {
this.uname = uname;
this.age = age;
}
Person.prototype.say = function() {
return '我叫' + this.uname + ',今年' + this.age + '岁。';
};
function Student(uname, age, grade) {
Person.call(this, uname, age);
this.grade = grade;
}
Student.prototype.exam = function() {
console.log('正在考试!');
};
var stu = new Student('张三', 16, '高一');
console.log(stu.uname, stu.age, stu.grade); //输出:张三 16 高一
stu.exam(); //输出:正在考试!
学生には、それがこの、この学生の内側を指すように、人の関数を呼び出し、これはunameと、この年齢層の学生を与え、最終的にはプロトタイプオブジェクトへの試験方法を追加しました。私たちの目標は、人のインスタンスを作成しないことに注意してください、これだけ唯一の一般的なコンストラクタ関数呼び出しとして、新たに追加されません。
我々はそこに継承されているかどうかを確認するために、親のコンストラクタメソッドを呼び出しましょう。
>> stu.say()
TypeError: stu.say is not a function //报错了!
>> stu.say
undefined //stu实例并没有say这个成员
ああ、Person.prototypeを入れていると言うが、それはSTU接触を生産し、変更にプロトタイプチェーンを与えませんでした。また、プロトタイプSTUが試験をハングアップしているので、直接ポイントSTU .__ proto__を変更することはできません、(それは元々のObject.prototypeを指示された)プロトタイプチェーンStudent.prototype .__ proto__ポイントを下方修正する必要がありました:
>> Student.prototype.__proto__ = Person.prototype
>> stu.say()
"我叫张三,今年16岁。" //调用成功了!
年齢、名前をプリントアウトし、メソッドの実装を言うが、私たちの学生のコンストラクタは、グレードを追加するだけでなく、プリントアウトする必要がありました。これは、メソッドのために生活が困難になることができ、我々はそれを定義するすべてのその時は人、無グレードの性質に基づいてされた後、と言います。だから我々はそれが元のメソッドが言う影響を与えずに、グレードを印刷し、Student.prototyeにリンクされているようである受験できるように、このメソッドをオーバーライドする必要があります。
>> Student.prototype.say = function() { return '我叫' + this.uname + ',今年' + this.age + '岁。' + this.grade + '学生。'; }
>> stu.say()
"我叫张三,今年16岁。高一学生。"
アップゲット!当社は、承継の問題を解決し、最後に、壁を数回押してください。
多くの材料において使用されるプロトタイプオブジェクトコンストラクタ+組み合わせであるように、彼は、「寄生合わせ継承」、アイデアや上記の分析と呼ばれます。唯一のことを除いてはなく、オリジナルのプロトタイプオブジェクトコンストラクタサブを保持し、それはObject.createによって別のオブジェクト()メソッドを指します。
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;
Object.create()メソッドは、Person.prototypeとして渡された新しいオブジェクトに新しいオブジェクト、__proto__この引数点を作成します。今、別のオブジェクトのプロトタイプとして指定され、コンストラクタは戻ってコンストラクタを参照してください。
またStudent.prototypeポイントを変更したほか、「複合遺産」と呼ばれるも頻繁に言及されている継承の別の方法は、あります:
Student.prototype = new Person(); //不用赋值,我们不关心原型里的uname和age
Student.prototype.constructor = Student;
値Student.prototypeとして親クラスのインスタンスを使用して__proto__親クラス・プロトタイプ・オブジェクト・インスタンスある点親コンストラクタため。そうすることの人の欠点は、2回の合計を呼び出すことで、Student.prototypeの存在下での資産の部未満。
さて、最後の質問:同じコード断片と親は、サブクラスのメソッドのメソッドは、この冗長化を最適化する方法を、そこに言っていると言いますか?答えは、プロトタイプの父を言うコンストラクタメソッドを呼び出すことです。
Student.prototype.say = function() {
return Person.prototype.say.call(this) + this.grade + '学生。';
};
直接呼び出しは唯一Student.prototypeは、そのサブクラスのインスタンスを変更するには、このコールを使用することを、このために、デフォルトの発信者には、未定義のプリントアウトされます。
最後に、組み合わせ寄生継承を使用して、完全なコードを添付:
function Person(uname, age) {
this.uname = uname;
this.age = age;
}
Person.prototype.say = function() {
return '我叫' + this.uname + ',今年' + this.age + '岁。';
};
function Student(uname, age, grade) {
Person.call(this, uname, age);
this.grade = grade;
}
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student; //别忘了把constructor指回来
//Student.__proto__ = Person; //这里挖个坑,后面填
Student.prototype.exam = function() {
console.log('正在考试!');
};
Student.prototype.say = function() {
return Person.prototype.say.call(this) + this.grade + '学生。';
};
var stu = new Student('张三', 16, '高一');
console.log(stu.say()); //输出:我叫张三,今年16岁。高一学生。
だから、プロトタイプチェーンはますます成長します:
継承されたアイデアを総括するには:
1.まず、娘に親コンストラクタメソッドはコンストラクタを呼び出す呼び出し、これは、親クラスの実装のインスタンスが属性を継承し、修飾を指します。
2.変更した後、サブコンストラクタのプロトタイプを作成するためにポイントし、寄生組み合わせ相続、連続または組み合わせの両方、またはプロトタイプチェーンの本質を探求する私たち自身が親ハンドルクラスのコンストラクタのプロトタイプオブジェクトにリンクされている方法を変更サブクラスを達成するために、親クラスのインスタンスを継承します。
3.サブコンストラクタのプロトタイプに連結されたサブクラスのメソッドの新しいインスタンス場合。
4.例サブクラスのメソッドは、プロトタイプのコンストラクタを呼び出すことによって、親クラスのインスタンスメソッド、親を呼び出すことが、この向きを変更する必要がある場合。
+コアは、プロトタイプオブジェクトのコンストラクタ関数です。唯一のプロトタイプオブジェクトは、親クラスのインスタンスを継承することはできませんサブクラス属性;だけコンストラクタ、およびメソッドは、プロトタイプオブジェクトを継承することはできません。しかし、2つのアプローチの後、それぞれを補完することができるようになります。不適切なアナロジー、ラオス期間トングドラゴンの救助徐朱天、天トングラオス骨折した足の移動を行うが、彼らは、特定の魔法を持っているために、徐朱かわす後に高速に実行することを学ぶが、彼は使用する方法を知りませんでした内力。彼らは最後の実行に成功しました。_(:З「∠)_
三、ES6クラスと継承
1、クラス
ES6クラスの新しい概念、クラスを定義するクラスのキーワードを使用し、文法、およびその他のオブジェクト指向言語は非常に似ています。
class Person {
constructor(uname, age) {
this.uname = uname;
this.age = age;
}
say() { //实例方法
return `我叫${this.uname},今年${this.age}岁。`; //模板字符串
}
static staticMethod() { //静态方法
console.log(`这是静态方法`);
}
}
let zhangsan = new Person('张三', 18);
console.log(zhangsan.say());
注意点:
属性の1例は、コンストラクタで定義されています。コンストラクタはデフォルトで作成されます書いてはいけません。
2.クラス・プラス・ファンクションのキーワードを必要としない、上記の方法では、方法は、カンマで区切る必要がありません。
3. staticキーワードの静的メソッドを追加する前に、インスタンスメソッドにはありません。
4.ES6静的プロパティは、クラス内で定義された、または従来のPerson.xxx人[「XXX」]を使用する必要があることはできません。
何可変リフトクラスを定義する必要がありません5.class、クラスは、オブジェクトによってインスタンス化されます。
2、継承
拡張キーワードを使用して実装の継承:
class Person {
constructor(uname, age) {
this.uname = uname;
this.age = age;
}
say() {
return `我叫${this.uname},今年${this.age}岁。`;
}
}
class Student extends Person {
constructor (uname, age, grade) {
super(uname, age);
this.grade = grade;
}
say() {
return `${super.say()}${this.grade}学生。`;
}
exam() {
console.log('正在考试!');
}
}
let stu = new Student('张三', 16, '高一');
console.log(stu.say());
stu.exam();
このコードは、ES5バージョンを継承ES6の正面の一例です。
注意点:
コンストラクタ1.サブクラスは、新しいインスタンスを作成するときに、それ以外の場合はエラーになり、スーパーメソッドを呼び出す必要があります。
両方がスーパーを使用しますが、その意義は同じではありません、後で話すだろうが、2.constructorと言います。
3、クラス自然
私は結論お話しましょう:セット内のクラス原理は基本的ES5を、しかし文言はより簡潔かつ明確です。
学生の使用クラス定義はまだコンストラクタで、プロトタイプチェーンの前にまったく同じ。
>> typeof Person //class定义出来的仍然是一个函数
"function"
>> Person.prototype === (new Person()).__proto__
true
>> Person.prototype.constructor === Person //三角关系一模一样
true
>> stu.__proto__ instanceof Person //stu的原型是Person的实例
true
>> Object.getOwnPropertyNames(stu)
[ "uname", "age", "grade" ]
>> Object.getOwnPropertyNames(stu.__proto__)
[ "constructor", "say", "exam" ] //Student的say和exam挂在原型里
Object.getOwnPropertyNames(返された配列の名前を構成している彼の体にぶら下がってのすべてのプロパティとメソッドの指定されたオブジェクトの名前を取得する)方法(プロトタイプチェーンを探して行っていません)、。彼らはプロトタイプでハングアップするので、私たちは、STUで言うと、受験訪問することができます。
他の私が直接結論に、しようとはしません。
1.class定義は、コンストラクタのまま。
プロトタイプオブジェクトにぶら下がっ方法定義2.classの例は、静的メソッドは、コンストラクタは、自分自身をぶら下げ。
3.二つの場所があり、サブクラスがスーパーを使用している、別の意味:コンストラクタでは、スーパー関数はコンストラクタスーパー(のuname、年齢)Person.call(これ、のuname、年齢)の同等の、親クラスの代表を呼び出すと見られていますさらに超に()コンストラクタでのみ使用することができる;(メソッドを言うsuper.say)オブジェクトがスーパービューである場合、それは親クラスのプロトタイプオブジェクトPerson.prototypeを指し、super.say()Person.protoypeが対応しています。 say.call(この)。
実際には、両方の関数ES6、ES5のクラスの大部分を達成することができます。もちろん、クラスの導入とは、他のオブジェクト指向プログラミング言語の構文のように、書かれたより簡潔なJSにそう広がります。だから、ES6は、クラスのシンタックスシュガーです。
4、内蔵のオブジェクトが継承します
同じことは、内蔵内のオブジェクトを継承することができます拡張します。
class MyArray extends Array {
constructor() {
super();
}
}
let a_es6 = new MyArray();
a_es6[1] = 'a';
console.log(a_es6.length); //输出:2
MyArrayというアレイのパフォーマンスとほとんど同じ。
しかし、練習ES5が必要な場合、例えば、組み合わせ継承:
function MyArray2() {
Array.call(this);
}
MyArray2.prototype = new Array();
MyArray2.prototype.constructor = MyArray2;
var a_es5 = new MyArray2();
a_es5[1] = 'a';
console.log(a_es5.length); //输出:0
我々は添字のa_es5位置が1の値を割り当てられ得、それは残念、長さまたは0です。
なぜ行動のこれら2つのクラスが全く違うのですか?相続ES5の組み合わせは、最初のサブクラスのコンストラクタにこの値を作成しているので、これのMyArray2は、新しく作成されたオブジェクトを指し、および内部アレイのメンバーはこれに追加するように、親のコンストラクタを呼び出しますが、この方法は、することはできません内部アレイのメンバー。シミュレーションの次の例を見てください:
>> let o = {}
>> Object.getOwnPropertyNames(o)
[] //空列表
>> Array.call(o)
>> Object.getOwnPropertyNames(o)
[] //仍然是空列表
我々はArray.call(O)の特性アレイのすべての空のオブジェクトoの取得作りをしようとしたが、変更されていませんどのようなO、失敗しました。でも、独自のlengthプロパティ、あまりにアレイa_es5から「継承」はありませんが、我々は長さを訪問することができ、それは、プロトタイプに掛かっているためです。
しかし、クラスES6では、これは()親クラスのインスタンスに基づいて変更サブクラス値続いて、親クラス・アレイ・ポイントのスーパー最初のインスタンスによって作成され、したがってこのES6で親クラスのインスタンスにアクセスすることができます。
第四に、関数プロトタイプ
私たちは、機能および発現定義と機能に加えて、あなたはまた、新しい機能(パラメータ1、パラメータ2、...、関数本体)を使用することができ、という方法の定義を知っています:
>> var f = new Function('a', 'b', 'console.log(a + b);')
>> f(1, 2)
3
換言すれば、全ての機能は、このインスタンスコンストラクタ関数で、オブジェクトであり、両方のプロトタイププロパティの内部機能は、プロトタイプオブジェクト目的球を指すプロトタイプオブジェクトを所有する元のポインティングを__proto__性質を有しています。私たちは関数を作成するとき(それはES5を作成するために、どのような方法であるかどうか)、新しい一般的にこれらのことを実行します。
1.メモリに空のオブジェクトを作成し、ここでFと呼びます。
2. F .__ proto__ポイントFunction.prototypeを作ります。
3.()新しいオブジェクトと別のオブジェクトを作成し、プロトと呼びます。
4.注文proto.constructorポイントF;
F.prototypeポイントので、プロト。
6.リターンF.
特に、ES6の使用は、むしろFunction.prototypeより、継承関係を表すために親クラスを指します__proto__サブクラスを継承して延びています。(それが継承寄生モジュラーコードの前に穴掘り覚えていますか?)
関数functionを見てください。またFunction.prototype、プロトタイプオブジェクトを持っています。一方、オブジェクト、独自のプロトタイプオブジェクトへの__proto__属性点のES5規定関数として、即ち.__ proto__完全等しいFunction.prototype機能します。
新しいオブジェクトによって作成されたオブジェクト、したがって、Function.prototype .__ proto__ポイントのObject.prototypeはFunction.prototypeです。
今、人々のプロトタイプチェーンの上に立ってヌル以外にあるすべてのポイントのObject.prototype、オブジェクトであるプロトタイプオブジェクトは、それの上に、のObject.prototype .__ proto__はnullです。
究極のプロトタイプチェーン図:
第五に、プロトタイプチェーンの実用化
相続や方向性を見つけるために、また、プロトタイプチェーンは、順番にも使用することができ、上記のブロック組み込みの方法は、他の人が使用する必要はありません。Bは一例を拡散させるために、我々は、ダウン最初のオープンリーディング漫画のページを漫画のいくつかのシートを保存したいです:
図2は、2枚のキャンバスです参照してください。キャンバスからの画像情報を抽出し、我々はtoDataUrlとtoBlob方法を考えました。:前者はどんな、そして最終的に画像ファイルに変換することができ、Blobオブジェクトを返していないデータのURLを、暗号化されたbase64の後に返さ
>> let c = document.getElementsByTagName('canvas')[0]
>> c.toDataUrl
undefined //没了
>> c.toBlob
undefined //这个也没了
HTMLCanvasElement、toDataUrl toBlob定義され、その上、及びプロトタイプオブジェクトへのCanvasクラスのオブジェクト。ルックアップすることにより、JSコードが実行されるこれらの二つの機能を有して直ちに10×の両方デジタルと小文字のいずれかのプレースホルダである(内部reader.xxxxxxxxxx.jsに、未定義にこのファイルをポイント属性例えば、私は今の名前reader.8d59f9bef4.jsファイル)。
……(前略) function () { try { HTMLCanvasElement.prototype.toDataURL = void 0, HTMLCanvasElement.prototype.toBlob = void 0 } catch (e) {} }(), ……(后略)
canvas要素につながるこのようなブロックはファイルブロックされたJS広告は、生成されませんが、あなたが画像をダウンロードするために他の方法を使用することができ、拡張することができない、この記事で、無細部の焦点ではありません。
ソースの1.Chromeは、直接アウトフォトギャラリーページを置きます。
キャンバスに2 Firefoxは、非標準的な方法をmozGetAsFile()を添加し、密封されていないオブジェクトをファイルに変換することができます。
爬虫類でクロール3の前と後に分析したHTTPリクエストとレスポンス、。
4. 2行をコメントすることができ、ローカル・ファイル・コース内のファイルシオマネキローカルファイルを置き換えます。
第六に、参考資料(拡張読み取り)
1. ECMAScriptの
2. プロトタイプチェーンJSについての話のオブジェクトと関数から
4. ES5継承