起因
代わりに、個人的なブログ投稿、古典的な本、有名な講師からのJavaScriptシステムの知識をたくさん入力すると、少し混乱しました。私は8つの継承を流暢に、流暢にアンチシェイクスロットリング、アンダースコアライブラリにディープコピーを書くことができます。また、クロージャとは何か、イテレータとは何か、ジェネレータとは何かを知っています。しかし、問題は、クロージャーなどのJavaScriptのコアコンセプトをクラスメートに紹介し、それを自分自身に説明し、インタビュアーに話しかけるときに発生します。私が思っていた言葉は曖昧になるかもしれませんが、話すのをためらっているようでした。それは、ほんの少しの言葉か知識の一部でのみ相手に表現することができます。これは確かに、多くの時間を費やしたにもかかわらず、JavaScriptを持っていなかったという感覚を私に与えました。
目的
したがって、このシリーズが解決しようとしている問題は、他の人がJavaScriptのコアコンセプトを尋ねたり調べたりするときに、他の人にできるだけ流暢かつ明確に表現できることです。
予想
ナゲッツの優秀なフロントエンド技術者と先輩が忙しいスケジュールでこの記事を補足し、この記事をさらにレビューし、私にもっと質問をしてくれることを願っています。このシリーズが私の現在の問題を解決することを期待しています。
プロトタイプをどのように理解しているか教えてください
アーキタイプの理解に答える
まず、インタビュアーにプロトタイプの理解について話をしたいと思います。JavaScriptはプロトタイプに基づく言語です。プロトタイプは、古典的なオブジェクト指向言語のクラスに似ています。JavaScriptプロトタイプを使用して、クラスを使用できるようにすることができます。オブジェクト指向および継承テクノロジーのスタイル。コーディング。ただし、従来のオブジェクト指向言語とは異なり、JavaScriptがプロトタイプを継承してインスタンス化する場合、JavaScriptはクラスの動作を従来のオブジェクト指向言語のようにインスタンスまたはサブクラスにコピーせず、複数のオブジェクトを関連付けて実行します。
JavaScriptでの回答プロトタイプの実装
次に、JavaScriptでのプロトタイプの実装についてインタビュアーに話したいと思います。JavaScriptのすべての関数には、初期化時にプロトタイプ属性があります。この属性は、コンストラクターを呼び出して作成されたインスタンスオブジェクトのプロトタイプを指します。プロトタイプオブジェクトの使用利点は、プロパティ、メソッドを事前に定義できることです。これらのプロパティとメソッドは、インスタンスオブジェクトによって共有されます。
JavaScriptでのオブジェクトレイアウトへの回答(プロトタイピング)
最后想和面试官聊一聊 gitHub 上的一张关于 Object Layout 的神图。这张图,清晰的画出了 JavaScript 语言的原型设计。
这张图,我们可以按照显式原型和隐式原型两条线来进行全面梳理。
显式原型路线是:无论是基本类型构造函数(Number, String, BigInt, Boolean, Symbol)还是引用类型构造函数(Array, Date, Function, RegExp,除了 Object),还是其他自定义构造函数,都遵守:
构造函数.prototype.__proto__
等于Object.prototype
Object.prototype.__proto__
等于顶层原型null
。
// 基本类型 构造函数
console.log(Number.prototype.__proto__ == Object.prototype);
console.log(String.prototype.__proto__ == Object.prototype);
console.log(BigInt.prototype.__proto__ == Object.prototype);
console.log(Boolean.prototype.__proto__ == Object.prototype);
console.log(Symbol.prototype.__proto__ == Object.prototype);
// 引用类型 构造函数
console.log(Function.prototype.__proto__ == Object.prototype);
console.log(Array.prototype.__proto__ == Object.prototype);
console.log(Date.prototype.__proto__ == Object.prototype);
console.log(RegExp.prototype.__proto__ == Object.prototype);
// 自定义构造函数
console.log(Person.prototype.__proto__ == Object.prototype, "自定义");
//全部为true
// 顶层原型 null
console.log(Object.prototype.__proto__);
// null
复制代码
隐式原型路线是:无论是基本类型构造函数(Number, String, BigInt, Boolean, Symbol),还是引用类型构造函数(Array, Date, Function, RegExp,包括 Object),还是其他自定义构造函数,都遵守:
构造函数.__proto__ 等于 Function.prototype
构造函数.__proto__.__proto__ 等于 Object.prototype
// 基本类型 构造函数
console.dir(Number.__proto__.__proto__ === Object.prototype);
console.dir(String.__proto__.__proto__ === Object.prototype);
console.dir(BigInt.__proto__.__proto__ === Object.prototype);
console.dir(Boolean.__proto__.__proto__ === Object.prototype);
console.dir(Symbol.__proto__.__proto__ === Object.prototype);
// 引用类型 构造函数
console.dir(Array.__proto__.__proto__ === Object.prototype);
console.dir(Date.__proto__.__proto__ === Object.prototype);
console.dir(RegExp.__proto__.__proto__ === Object.prototype);
console.dir(Function.__proto__.__proto__ === Object.prototype);
// 自定义构造函数
console.dir(Function.__proto__.__proto__ === Object.prototype,"自定义");
// 基本类型 构造函数
console.dir(Number.__proto__ === Function.prototype);
console.dir(String.__proto__ === Function.prototype);
console.dir(BigInt.__proto__ === Function.prototype);
console.dir(Boolean.__proto__ === Function.prototype);
console.dir(Symbol.__proto__ === Function.prototype);
// 引用类型 构造函数
console.dir(Array.__proto__ === Function.prototype);
console.dir(Date.__proto__ === Function.prototype);
console.dir(RegExp.__proto__ === Function.prototype);
console.dir(Function.__proto__ === Function.prototype);
console.log(Object.__proto__.__proto__ === Object.prototype);
// 自定义构造函数
console.log(Person.__proto__ == Function.prototype, "自定义");
// 全部都是true
复制代码
练手滴滴面试题
有了上面的基础,做下面的题如同切菜一般简单了吧!
Object.prototype.name = "object";
Function.prototype.name = "function";
function Person() {}
const p = new Person();
console.log(Person.name);
console.log(p.name);
console.log(p.__proto__.__proto__.constructor.constructor.constructor.constructor.constructor);
// function
// object
// Function构造函数
复制代码
你说一下你是怎么样理解原型链的
相互关联的原型对象的链接被称为原型链, 当我们读取实例属性或者方法的时候, 会通过
[[prototype]]
查找即沿着原型链进行委托查找,一直找到最顶层为止。
其他原型知识 - 重写原型对象
- 如果我们需要在原型对象上定义很多属性和方法的时候,我们可以重写原型对象。
- 重写时需要注意,原型对象的 constructor已经丢失,我们需要重新定义具有不可遍历特征的constructor属性。
const foo.prototype = {
name: 'CIc',
age: 18,
eating() {},
drinking() {}
}
Object.defineProperty(foo.prototype, 'constructor', {
enumerable: false,
value: foo,
writable: true,
configurable: true;
})
复制代码
其他原型知识 - 检测原型的方法
in
判断属性是否在某个对象或者某个对象的原型上。
var obj = {
name: "why",
age: 18,
};
var info = Object.create(obj, {
address: {
value: "北京市",
enumerable: true,
},
});
// in 操作符: 不管在当前对象还是原型中返回的都是true
console.log("address" in info);
console.log("name" in info);
复制代码
hasOwnProperty
判断对象是否有属于自己的属性(不在原型链上找)。
var obj = {
name: "why",
age: 18,
};
var info = Object.create(obj, {
address: {
value: "北京市",
enumerable: true,
},
});
// hasOwnProperty方法判断
console.log(info.hasOwnProperty("address")); // true
console.log(info.hasOwnProperty("name")); // false
复制代码
instanceof
用于检测构造函数的原型对象是否出现在实例对象的原型链上。
function Person() {}
function Student() {}
var stu = new Student();
inheritPrototype(Student, Person);
console.log(stu instanceof Student); // true
console.log(stu instanceof Person); // true
console.log(stu instanceof Object); // true
复制代码
constructor
用于检测实例对象的是否属于某个构造函数。
function Person() {}
const p1 = new Person();
console.log(p1.constructor === Person); // true
复制代码
参考
- GitHub神图 - Hursh Jain/mollypages.org
- 《你不知道的JavaScript》
- 《JavaScript忍者秘籍》