1 オブジェクトと関数のプロトタイプ
2 新規、コンストラクター
3. プロトタイプチェーンの検索順序
4 プロトタイプチェーンによる継承実装
5 コンストラクターの継承の借用
6 寄生結合による継承の実装
関数によって作成された名前が大文字で始まる場合、作成されるのは関数ではなくクラスです。
区別に注目してください!この章にはそのようなクラスがたくさんあります。
インスタンスには暗黙的なプロトタイプがあり、宣言された関数またはオブジェクトには明示的および暗黙的なプロトタイプがあります。明示的なプロトタイプには、コンストラクターと暗黙的なプロトタイプが含まれます。各オブジェクトの関係は以下のとおりです。
普通の物のプロトタイプ
オブジェクトのプロトタイプは下の図の Prototype で、通常は非表示になっています。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <script> var obj = { name: "why", age: 18 } console.log(obj) var info = {} // 获取对象的原型 console.log(obj.name, obj.age) console.log(obj.__proto__) console.log(Object.getPrototypeOf(obj)) console.log(obj.__proto__ === Object.getPrototypeOf(obj)) // true // 疑问: 这个原型有什么用呢? // 当我们通过[[get]]方式获取一个属性对应的value时 // 1> 它会优先在自己的对象中查找, 如果找到直接返回 // 2> 如果没有找到, 那么会在原型对象中查找 console.log(obj.name) // 在原型中添加一个参数,开发时候不建议这样子用 obj.__proto__.message = "Hello World" console.log(obj.message) </script> </body> </html>
関数オブジェクトのプロトタイプ
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <script> var obj = {} function foo() {} // 1.将函数看成是一个普通的对象时, 它是具备__proto__(隐式原型) // 作用: 查找key对应的value时, 会找到原型身上 // console.log(obj.__proto__) // console.log(foo.__proto__) // 2.将函数看成是一个函数时, 它是具备prototype(显式原型) // 作用: 用来构建对象时, 给对象设置隐式原型的 console.log(foo.prototype) // console.log(obj.prototype) 对象是没有prototype </script> </body> </html>
新しい操作プロトタイプの割り当て
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <script> function Foo() { // 1.创建空的对象 // 2.将Foo的prototype原型(显式隐式)赋值给空的对象的__proto__(隐式原型) } console.log(Foo.prototype) var f1 = new Foo() var f2 = new Foo() var f3 = new Foo() var f4 = new Foo() var f5 = new Foo() console.log(f1.__proto__) console.log(f1.__proto__ === Foo.prototype) // true console.log(f3.__proto__ === f5.__proto__) // true </script> </body> </html>
プロトタイプにメソッドを配置する
新しいオペレーションの役割は非常に重要です。注: 定義された関数内に複数の関数があり、その関数が new によって作成される場合、作成される関数の数 * 定義された関数内の関数の数が作成されます。これらの機能の多くは繰り返され、スペースを無駄にしているため、プロトタイプ手法が解決策として使用されます。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <script> /* 1.什么是函数的显式原型 * 区分和对象原型区别 2.函数的原型的作用 * 在通过new操作创建对象时, 将这个显式原型赋值给创建出来对象的隐式原型 3.案例Person, 将所有的函数定义放到了显式原型上 */ function Student(name, age, sno) { this.name = name this.age = age this.sno = sno // 1.方式一: 编写函数, 会创建很多个函数对象 // this.running = function() { // console.log(this.name + " running") // } // this.eating = function() { // console.log(this.name + " eating") // } // this.studying = function() { // console.log(this.name + " studying") // } } // 当我们多个对象拥有共同的值时, 我们可以将它放到构造函数对象的显式原型 // 由构造函数创建出来的所有对象, 都会共享这些属性 Student.prototype.running = function() { console.log(this.name + " running") } Student.prototype.eating = function() { console.log(this.name + " eating") } // 1.创建三个学生 var stu1 = new Student("why", 18, 111) var stu2 = new Student("kobe", 30, 112) var stu3 = new Student("james", 18, 111) // 隐式原型的作用 // 1> stu1的隐式原型是谁? Student.prototype对象 // 2> stu1.running查找: // * 先在自己身上查找, 没有找到 // * 去原型去查找 stu1.running() stu2.eating() </script> </body> </html>
明示的なプロトタイプのプロパティ
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <script> // 非常重要的属性: constructor, 指向Person函数对象 function Person() { } // 1.对constructor在prototype上的验证 var PersonPrototype = Person.prototype console.log(PersonPrototype) console.log(PersonPrototype.constructor) console.log(PersonPrototype.constructor === Person) console.log(Person.name) console.log(PersonPrototype.constructor.name) // 2.实例对象p var p = new Person() console.log(p.__proto__.constructor) console.log(p.__proto__.constructor.name) </script> </body> </html>
オブジェクトプロセスメモリを作成します。具体的なメモリ図については ppt を参照してください。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <script> function Person(name, age) { this.name = name this.age = age } Person.prototype.running = function() { console.log("running~") } var p1 = new Person("why", 18) var p2 = new Person("kobe", 30) // 进行操作 console.log(p1.name) console.log(p2.name) p1.running() p2.running() // 新增属性 Person.prototype.address = "中国" p1.__proto__.info = "中国很美丽!" p1.height = 1.88 p2.isAdmin = true // 获取属性 console.log(p1.address) console.log(p2.isAdmin) console.log(p1.isAdmin) console.log(p2.info) // 修改address p1.address = "广州市" console.log(p2.address) </script> </body> </html>
オーバーライド関数プロトタイプ オブジェクト
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <script> function Person() { } console.log(Person.prototype) // 在原有的原型对象上添加新的属性 // Person.prototype.message = "Hello Person" // Person.prototype.info = { name: "哈哈哈", age: 30 } // Person.prototype.running = function() {} // Person.prototype.eating = function() {} // console.log(Person.prototype) // console.log(Object.keys(Person.prototype)) // 直接赋值一个新的原型对象,把原来的替换掉 Person.prototype = { message: "Hello Person", info: { name: "哈哈哈", age: 30 }, running: function() {}, eating: function() {}, // 加上constructor就和原本的原型差不多一样了 // 但是原来的constructor是不可枚举的Object.keys枚举不到,所以还得改不可枚举 // constructor: Person } // 通过下面对象属性描述符来给原型添加对应的constructor Object.defineProperty(Person.prototype, "constructor", { enumerable: false, configurable: true, writable: true, value: Person }) console.log(Object.keys(Person.prototype)) // 新建实例对象 var p1 = new Person() console.log(p1.message) </script> </body> </html>
重要 - オブジェクトのプロトタイプ チェーン
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <script> // 1.{}的本质 // var info = {} // 相当于 // var info = new Object() // console.log(info.__proto__ === Object.prototype) // 2.原型链 var obj = { name: "why", age: 18 } // 查找顺序 // 1.obj上面查找 // 2.obj.__proto__上面查找 // 3.obj.__proto__.__proto__ -> null 上面查找(undefined) // console.log(obj.message) // 3.对现有代码进行改造 obj.__proto__ = { // message: "Hello aaa" } obj.__proto__.__proto__ = { message: "Hello bbbb" } obj.__proto__.__proto__.__proto__ = { message: "Hello ccc" } console.log(obj.message) </script> </body> </html>
プロトタイプチェーン実装メソッドの継承
ここで私が話しているのは、学生関数をそれ自体のプロパティやプロトタイプに見つからないようにする方法です。person 関数のプロトタイプで必要な属性を見つけるように生徒に指示する方法を見つけてください。
間違った方法 1: 親クラスのプロトタイプを子クラスに直接与える (これは行わないでください)
方法 2: 問題が多く、親関数の属性に依存し、複数のインスタンスで作成された属性が共有されるため、お勧めしません。
方法 3 では、コンストラクター属性の継承を借用し、次の見出しのコードを調べます。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <script> // 定义Person构造函数(类) function Person(name, age, height, address) { this.name = name this.age = age this.height = height this.address = address } Person.prototype.running = function() { console.log("running~") } Person.prototype.eating = function() { console.log("eating~") } // 定义学生类 function Student(name, age, height, address, sno, score) { this.name = name this.age = age this.height = height this.address = address this.sno = sno this.score = score } // 方式一: 父类的原型直接赋值给子类的原型 // 缺点: 父类和子类共享通一个原型对象, 修改了任意一个, 另外一个也被修改 // Student.prototype = Person.prototype // 方式二: 创建一个父类的实例对象(new Person()), 用这个实例对象来作为子类的原型对象 var p = new Person("why", 18) Student.prototype = p // Student.prototype.running = function() { // console.log("running~") // } // Student.prototype.eating = function() { // console.log("eating~") // } Student.prototype.studying = function() { console.log("studying~") } // 创建学生 var stu1 = new Student("kobe", 30, 111, 100) var stu2 = new Student("james", 25, 111, 100) stu1.running() stu1.studying() console.log(stu1.name, stu1.age) console.log(stu1) console.log(stu2.name, stu2.age) </script> </body> </html>
コンストラクターのプロパティ継承の借用 -- 重要
まだまだ欠点はありますが、
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <script> // 定义Person构造函数(类) function Person(name, age, height, address) { this.name = name this.age = age this.height = height this.address = address } Person.prototype.running = function() { console.log("running~") } Person.prototype.eating = function() { console.log("eating~") } // 定义学生类 function Student(name, age, height, address, sno, score) { // 重点: 借用构造函数 Person.call(this, name, age, height, address) // this.name = name // this.age = age // this.height = height // this.address = address this.sno = sno this.score = score } // 方式一: 父类的原型直接赋值给子类的原型 // 缺点: 父类和子类共享通一个原型对象, 修改了任意一个, 另外一个也被修改 // Student.prototype = Person.prototype // 方式二: 创建一个父类的实例对象(new Person()), 用这个实例对象来作为子类的原型对象 var p = new Person("why", 18) Student.prototype = p // Student.prototype.running = function() { // console.log("running~") // } // Student.prototype.eating = function() { // console.log("eating~") // } Student.prototype.studying = function() { console.log("studying~") } // 创建学生 var stu1 = new Student("kobe", 30, 111, 100) var stu2 = new Student("james", 25, 111, 100) stu1.running() stu1.studying() console.log(stu1.name, stu1.age) console.log(stu1) console.log(stu2.name, stu2.age) </script> </body> </html>
プロトタイプオブジェクトの作成方法
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <script> // 工具函数 // 创建对象的过程 function createObject(o) { function F() {} F.prototype = o return new F() } // 将Subtype和Supertype联系在一起 // 寄生式函数 function inherit(Subtype, Supertype) { Subtype.prototype = createObject(Supertype.prototype) Object.defineProperty(Subtype.prototype, "constructor", { enumerable: false, configurable: true, writable: true, value: Subtype }) } /* 满足什么条件: 1.必须创建出来一个对象 2.这个对象的隐式原型必须指向父类的显式原型 3.将这个对象赋值给子类的显式原型 */ function Person(name, age, height) {} function Student() {} inherit(Student, Person) // 1.之前的做法: 但是不想要这种做法 // var p = new Person() // Student.prototype = p // 2.方案一: var obj = {} // obj.__proto__ = Person.prototype Object.setPrototypeOf(obj, Person.prototype) Student.prototype = Person.prototype // 3.方案二: // function F() {} // F.prototype = Person.prototype // Student.prototype = new F() // 4.方案三: var obj = Object.create(Person.prototype) console.log(obj.__proto__ === Person.prototype) Student.prototype = obj </script> </body> </html>
最終的な相続計画書の書き方
次の内容を含むツールの継承_utils.js ファイルを作成します。
// 创建对象的过程 function createObject(o) { function F() {} F.prototype = o return new F() } // 将Subtype和Supertype联系在一起 // 寄生式函数 function inherit(Subtype, Supertype) { // Subtype.prototype.__proto__ = Supertype.prototype // Object.setPrototypeOf(Subtype.prototype, Subtype.prototype) Subtype.prototype = createObject(Supertype.prototype) Object.defineProperty(Subtype.prototype, "constructor", { enumerable: false, configurable: true, writable: true, value: Subtype }) Object.setPrototypeOf(Subtype, Supertype) // Subtype.__proto__ = Supertype }
実際に使用されているツールを紹介します。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <script src="./js/inherit_utils.js"></script> <script> // 寄生组合式继承 // 原型链/借用/原型式(对象之间)/寄生式函数 function Person(name, age, height) { this.name = name this.age = age this.height = height } Person.prototype.running = function() { console.log("running~") } Person.prototype.eating = function() { console.log("eating~") } function Student(name, age, height, sno, score) { Person.call(this, name, age, height) this.sno = sno this.score = score } inherit(Student, Person) Student.prototype.studying = function() { console.log("studying") } // 创建实例对象 var stu1 = new Student("why", 18, 1.88, 111, 100) </script> </body> </html>
ES5-プロトタイプ-寄生継承スキーム
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <script> // 对象之间的继承 // 默认的对象 var obj = { name: "why", age: 18 } // 原型式继承 function createObject(o) { function F() {} F.prototype = o return new F() } // 寄生式函数 function createInfo(o, name, age, height, address) { var newObj = createObject(o) newObj.name = name newObj.age = age newObj.height = height newObj.address = address return newObj } // 创建另外一个对象, 这个对象可以继承自obj // var info1 = createObject(obj) // info1.height = 1.88 // info1.address = "广州市" // var info2 = createObject(obj) // info1.height = 1.88 // info1.address = "广州市" // var info3 = createObject(obj) // info1.height = 1.88 // info1.address = "广州市" // 创建一系列对象 var info1 = createInfo(obj, "why", 18, 1.88, "广州市") var info2 = createInfo(obj, "kobe", 30, 1.98, "洛杉矶市") </script> </body> </html>
ES5-Object は他のクラスの親クラスです
つまり、オブジェクト型であれば、オブジェクト内の関数を呼び出すことができます。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <script src="./js/inherit_utils.js"></script> <script> function Person() {} function Student() {} function Teacher() {} inherit(Student, Person) console.log(Person.prototype.__proto__ === Object.prototype) // 在Object的原型上添加属性 Object.prototype.message = "coderwhy" var stu = new Student() console.log(stu.message) // Object原型上本来就已经存放一些方法 console.log(Object.prototype) console.log(stu.toString()) // 函数对象也是最终继承自Object function foo() {} console.log(foo.message) </script> </body> </html>
ES5-オブジェクト判定方法補足
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <script src="./js/inherit_utils.js"></script> <script> var obj = { name: "why", age: 18 } var info = createObject(obj) info.address = "中国" info.intro = "中国大好河山" console.log(info.name, info.address) console.log(info) // 1.hasOwnProperty // console.log(info.hasOwnProperty("name")) // false // console.log(info.hasOwnProperty("address")) // true // 2.in操作符 console.log("name" in info) console.log("address" in info) // 注意: for in遍历不仅仅是自己对象上的内容, 也包括原型对象上的内容 for (var key in info) { console.log(key) } // 3.instanceof // instanceof用于判断对象和类(构造函数)之间的关系 function Person() {} function Student() {} inherit(Student, Person) // stu实例(instance)对象 var stu = new Student() console.log(stu instanceof Student) console.log(stu instanceof Person) console.log(stu instanceof Object) console.log(stu instanceof Array) // 4.isPrototypeOf console.log(Student.prototype.isPrototypeOf(stu)) console.log(Person.prototype.isPrototypeOf(stu)) // 可以用于判断对象之间的继承 console.log(obj.isPrototypeOf(info)) </script> </body> </html>
ES5 - コンストラクターのクラスメソッド
プロトタイプに追加されたメソッドはインスタンス メソッドと呼ばれ、クラスに追加されたメソッドはクラス メソッドと呼ばれます。
インスタンスメソッドはインスタンスを作成しないと使用できませんが、クラスメソッドは使用できます。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <script> // var obj = { // friend: { // play: function() { // console.log("play~") // } // } // } // obj.play() function Person(name, age) { this.name = name this.age = age } Person.totalCounter = "70亿" // 添加Person原型上的方法也成为 实例方法 Person.prototype.running = function() { console.log(this.name + "running~") } Person.prototype.eating = function() { console.log("eating~") } // 添加Person对象本身的方法成为 类方法 var names = ["abc", "cba", "nba", "mba"] Person.randomPerson = function() { var randomName = names[Math.floor(Math.random() * names.length)] return new Person(randomName, Math.floor(Math.random() * 100)) } // 实例对象 var p1 = new Person("why", 18) var p2 = new Person("kobe", 30) p1.running() // 没有实例对象的情况下, 能不能调用函数? 不可以调用 var p = Person.randomPerson() console.log(p) </script> </body> </html>