まず、以下の面接の質問を見て、結果について考えてください。
Object.prototype.test1 = function () {
console.log('test1');
};
Function.prototype.test2 = function () {
console.log('test2');
};
function Fun() {
this.a = 1;
}
var obj = new Fun();
console.log(obj.test1);
console.log(obj.test2);
回答:
obj.test1は関数を出力しますが、obj.test2は未定義を出力します。
この質問に答えるには、javascriptプロトタイプチェーンを深く理解する必要があります。
知識のポイント
jsプロトタイプチェーンとは何ですか
プロトタイプチェーンを学ぶ前に、まずコンストラクター、インスタンスオブジェクト、プロトタイプオブジェクトの3つの基本概念を理解してください。
コンストラクタ
コンストラクターも関数ですが、通常の関数よりも関数が1つ多く、newキーワードで呼び出すとオブジェクトを作成できます。
function student(){
this.name = "张三";
}
var stu1 = new student(); //创建对象
console.log(stu1.name); //输出张三
上で定義されたstudent関数はコンストラクターであり、newキーワードを使用して新しいオブジェクトを生成します。
インスタンスオブジェクト
インスタンスオブジェクトは、newキーワードを使用してコンストラクターを呼び出した後に返される新しいオブジェクトです。上記のコードのstu1に対応します。
プロトタイプオブジェクト
プロトタイプオブジェクトもオブジェクトであり、コンストラクターのプロトタイプが指すオブジェクトです。
function student(){
this.name = "张三";
}
student.prototype.run = function(){console.log("奔跑");}
var stu1 = new student(); //创建对象
stu1.run() //打印'奔跑'
student.prototypeはプロトタイプオブジェクトです。プロトタイプオブジェクトにプロパティとメソッドを追加した後、インスタンスオブジェクトはプロトタイプオブジェクトのプロパティとメソッドを呼び出すことができます。
Object、Function、Array、String、Numberなどはすべてコンストラクターであることに注意してください。newキーワードを使用すると、対応するインスタンスオブジェクトを作成し、コンストラクターのプロトタイプにプロパティとメソッドを追加できます。インスタンスオブジェクトを呼び出すことができます。
インスタンスオブジェクトはプロトタイプオブジェクトのプロパティとメソッドにアクセスでき、プロトタイプオブジェクトは別のプロトタイプオブジェクトから継承できます。このようにして、第1レベルのインスタンスオブジェクトは第2レベルのプロトタイプオブジェクトのプロパティとメソッドにアクセスできます。また、第3レベルのプロトタイプにアクセスすることもできます。オブジェクトのプロパティとメソッド。このように形成されたチェーンがプロトタイプチェーンです。次の図は、プロトタイプチェーンの構造を理解するために使用されます。
プロトタイプチェーンのアーキテクチャ
上の図の長方形はコンストラクター、楕円はプロトタイプオブジェクト、角が丸い長方形はインスタンスオブジェクトです。
上図の変数配列は一例です。配列は配列のプロトタイプオブジェクトを継承し、配列のプロトタイプオブジェクトはオブジェクトのプロトタイプオブジェクトを継承します。このとき、配列、配列のプロトタイプオブジェクト、オブジェクトのプロトタイプオブジェクトがプロトタイプを形成します。鎖。
プロトタイプチェーンの高レベルオブジェクトによって定義されたプロパティとメソッドは、低レベルオブジェクトから呼び出してアクセスできます。
Object.prototypeのステータス
プロトタイプチェーンの最上位はObject.prototypeです。すべてのオブジェクトはObject.prototypeを継承します。プロパティとメソッドがObject.prototypeで定義されている場合、すべてのオブジェクト(nullを除く)を継承して使用できます。
Object.prototype.test1 = 123;
var obj = {};
console.log(obj.test1); //输出123
obj.test1 = 456;
console.log(obj.test1); //输出456,倘若子对象定义了与原型链上的对象相同的属性和方法时,子对象会覆盖
console.log(typeof null) // 输出 "object"
console.log(null.test1) //报错,null虽然也是对象,但是它不能调用任何属性和方法
コンストラクターとプロトタイプオブジェクト
Object、Function、Array、String、RegExpはすべてコンストラクターであり、上の図の右側にあるプロトタイプオブジェクトには、.prototypeを介してアクセスできます。
上記のプロトタイプオブジェクトを印刷すると、プロトタイプオブジェクトと通常のオブジェクトに違いはないことがわかりますが、多くのメソッドが定義されています。
オブジェクトの継承
インスタンスオブジェクトであろうとプロトタイプオブジェクトであろうと、__ proto__を使用して親のプロトタイプオブジェクトを取得できます。
var array = new Array();
array.__proto__ === Array.prototype; // true,通过__proto__属性可以获取父级的原型对象
Array.prototype.__proto__ === Object.prototype //true
//Object.prototype是所有对象的最高层,它再往一层就为null了
console.log(Object.prototype.__proto__) //输出 null
Arrayはインスタンスオブジェクトであり、Arrayプロトタイプオブジェクトのすべてのプロパティとメソッドを使用できます。また、Objectプロトタイプオブジェクトのプロパティとメソッドも使用できます。
Object.prototype.test1 = 123;
Array.prototype.test2 = 456;
var array = new Array();
console.log(array.test1); //输出123
console.log(array.test2); //输出456
一般的に定義されているオブジェクトと新しいオブジェクトの違いは何ですか
多くの場合、var obj =()を直接使用するようなオブジェクトを定義し、var array = []、var fun = function()()、それらと新しいオブジェクトの違いは何ですか。
var obj1 = {};
var obj2 = new Object();
obj1.__proto__ === obj2.__proto__ // 打印 true,都是指向了Object.prototype
var array1 = [];
var array2 = new Array();
array1.__proto__ === array2.__proto__ // 打印 true,都是指向了Array.prototype
上記から、var定義とnewオブジェクトの使用に違いはないことがわかりますが、newを使用してオブジェクトを作成する方が強力な場合があります。
var fun1 = function(){
console.log(123);
}
var code = "console.log(456)";
var fun2 = new Function(code);
fun2(); // 输出456
実行するコードが文字列の場合、コンストラクターFunctionを使用して関数オブジェクトを作成すると、関数本体を動的に変更できるため、evalと同様の効果が得られます。
関数和オブジェクト
上記の説明から、Object.prototypeはすべてのオブジェクトの祖先です。Function.prototypeの親はObject.prototypeです。
Function.prototype.__proto__ === Object.prototype //输出 true
FunctionのプロトタイプオブジェクトとObjectのプロトタイプオブジェクトの関係は明らかです。コンストラクターのObjectとFunctionを見てみましょう。
Objectはコンストラクターであり、関数でもあります(関数自体はオブジェクトです)。関数である限り、Function.prototypeのプロパティとメソッドを継承します。
Function.prototype.test = "测试";
console.log(Object.test); //输出 "测试"
Function.prototypeでプロパティとメソッドを定義すると、すべての関数を継承して呼び出すことができます。
オブジェクト和Object.prototype
Object.prototype.test = "测试";
console.log(Object.test); //输出 "测试"
Object.prototypeでプロパティとメソッドを定義します。Object、Function、Array、RegExpなどだけでなく、継承して呼び出すこともできます。
Objectを例にとると、Objectはコンストラクターであり、関数オブジェクトでもあります。ObjectはFunction.prototypeを継承し、Function.prototypeはObject.prototypeを継承します。したがって、Object、Function.prototype、およびObject.prototypeはプロトタイプチェーンを構成します。
Object.__proto__.__proto__ === Object.prototype; //输出 true
総括する
-
プロトタイプチェーンに関連する問題を分析するときは、最初に、終了変数がコンストラクターとして使用されるか、コンテキストに応じて関数オブジェクトとして使用されるかを決定します。たとえば、Functionはコンストラクターまたは関数オブジェクトとして使用できます。コンストラクターとして使用されます。プロトタイプgetsプロトタイプオブジェクトは、インスタンスオブジェクトまたはプロトタイプオブジェクトとして使用される場合、__ protp__を呼び出して、親のプロトタイプオブジェクトを見つけることができます。
Function.prototype === Function.__proto__ // 输出true
コンストラクター関数としての関数は、プロトタイプを呼び出してすべての関数オブジェクトの親を取得します。通常の関数として、関数は__proto__を介してすべての関数オブジェクトの親を取得することもできます。したがって、これらは同じです。
-
プロトタイプチェーン上のオブジェクトのみが継承関係を構成します。たとえば、Array.prototype.test = "Test"; var obj = {};現時点では、objはtest属性を取得できません。
プロトタイプチェーンの適用
オブジェクトプロパティの取得
var obj1 = {
a:1
};
var obj2 = {
b:2
};
var obj3 = {
c:3
}
obj1.__proto__ = obj2;
obj2.__proto__ = obj3;
console.log(obj1.c); //输出3
3つのオブジェクトを作成し、obj1、obj2、obj3を作成し、Object.prototypeを__proto__を変更してプロトタイプチェーンを形成します。
obj1がc属性の値を取得すると、最初にobj1からその値を探し、見つからない場合は前のレイヤーobj2にジャンプし、まだ見つからない場合は次のレイヤーにジャンプし続け、最後にcobj3に出力します。
for in loop
var obj1 ={
a:1
}
var obj2 = {
b:2
};
obj1.__proto__ = obj2;
for(var key in obj1){
console.log(key); //输出 a b
}
コードはobj1のみをループしますが、for inループメカニズムは、プロトタイプチェーン全体で列挙可能なすべてのプロパティとメソッドを取得します。
オブジェクト自体によって定義されたプロパティとメソッドのみを見つけて、プロトタイプチェーンの定義を破棄する方法はありますか?(hasOwnPropertyを介して実行できます)
var obj1 ={
a:1
}
var obj2 = {
b:2
};
obj1.__proto__ = obj2;
for(var key in obj1){
if(obj1.hasOwnProperty(key)){
console.log(key); //只输出a
}
}
問題解決
Object.prototype.test1 = function () {
console.log('test1');
};
Function.prototype.test2 = function () {
console.log('test2');
};
function Fun() {
this.a = 1;
}
var obj = new Fun();
console.log(obj.test1);
console.log(obj.test2);
次に、元のインタビューの質問を見てみましょう。objは通常のオブジェクトであり、その親はFun.prototypeです。
obj、Fun.prototype、およびObject.prototypeはプロトタイプチェーンを形成するため、test1はそれを取得でき、test2は未定義です。
test2メソッドはFunction.prototypeで定義されており、すべての関数オブジェクトがtest2を取得できますが、通常のオブジェクトは取得できません。次のようになります。
console.log(Fun.test2 ); //输出 test2函数