JavaScript高级|3. 继承与原型

1. 对象和函数的原型

js
复制代码
// 1. 什么是原型 /* 我们创建的每个构造函数都有一个 prototype(原型) 属性,该属性是一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。 当我们通过构造函数创建对象时,在这个对象中有一个指针,这个指针指向构造函数的prototype。我们将这个指向构造函数的prototype的指针称为原型。 */ // 2. 对象的原型 var obj = { name: "zgc", age: 18, }; console.log(obj); // 获取对象的原型 console.log(obj.__proto__); // 隐式原型 console.log(Object.getPrototypeOf(obj)); console.log(Object.getPrototypeOf(obj) === obj.__proto__); // true // 对象原型的作用 // 当在自身查找不到属性和方法时, 可以去原型查找 // 3. 函数的原型 function Foo() {} // 获取函数的原型 console.log(Foo.prototype); // 显式原型, 对象没有prototype属性 var f1 = new Foo(); var f2 = new Foo(); console.log(f1.__proto__ === Foo.prototype); // true console.log(Foo.prototype === f2.__proto__); // true console.log(f1.__proto__ === f2.__proto__); // true // 创建三个对象 function Foo(name, age) { this.name = name; this.age = age; // 方式1: 这样每次都会创建一个新对象, 每个对象都会定义一遍函数, 造成浪费 // this.bar = function () { // console.log(this.name, "bar"); // }; // this.baz = function () { // console.log(this.name, "baz"); // }; } // 方式2: 这将每一个方法定义在原型上面, 这样每个对象调用的都是同一个函数 /* 即当我们多个对象拥有共同属性时, 我们可以将它放到构造函数的显式原型对象上面, 由构造函数创建出来的所有对象都会共享这些属性 */ Foo.prototype.bar = function () { console.log(this.name, "bar"); }; Foo.prototype.baz = function () { console.log(this.name, "baz"); }; var foo1 = new Foo("zgc", 18); var foo2 = new Foo("wf", 24); var foo3 = new Foo("cx", 20); // console.log(foo1.bar === foo2.bar); // flse console.log(foo1.bar === foo2.bar); // true foo3.baz(); // cx baz // 函数原型的作用 // 在用new操作符创建对象时, 将自身的prototype属性赋给对象的隐式原型

2. 函数原型上的constructor属性

js
复制代码
// 显式原型上面的constructor属性指向了函数对象 function Foo() {} console.log(Foo.prototype.constructor === Foo); // true console.log(Foo.name); // Foo console.log(Foo.prototype.constructor.name); // Foo console.log(Foo.name === Foo.prototype.constructor.name); // true var f = new Foo(); console.log(f.__proto__.constructor === Foo.prototype.constructor); // true console.log(f.__proto__ === Foo.prototype); // true

3. 重写函数原型对象

js
复制代码
function Person() {} // console.log(Person.prototype); // 像原型对象上直接添加属性和方法 Person.prototype.msg = "Hello World"; Person.prototype.info = { name: "zgc", age: 18 }; Person.prototype.run = function () {}; // console.log(Person.prototype); // 如果我们需要在原型上添加过多的属性, 通常我们可以重写整个原型对象 Person.prototype = { msg: "Hello Person", info: { name: "wf", age: 24 }, run: function () {}, // constructor: Person, }; // constructor默认是无法枚举的,所以直接添加constructor属性不太适合 Object.defineProperty(Person.prototype, "constructor", { value: Person, writable: true, configurable: true, enumerable: false, }); var p1 = new Person(); console.log(p1.msg); // Hello Person

4. 默认原型链和自定义原型链

js
复制代码
/* 当我们访问一个对象的属性时,如果这个对象内部不存在这个属性,那么它就会去它的原型对象里找这个属性; 这个原型对象又会有自己的原型,这样层层上溯,就形成了一个类似链表的结构,这就是原型链; 原型链的尽头一般来说都是Object.prototype所以这就是我们新建的对象为什么能够使用toString()等方法的原因。; */ var info = {}; // 等价于 var info = new Object(); console.log(info.__proto__ === Object.prototype); // true var obj = { name: "zgc", age: 18, }; // 查找顺序 // 1. 在obj上查找' // 2. 在obj的原型上查找(obj.__proto__) // 3. 在obj的原型的原型上查找上查找(obj.__proto__.__proto__) // 3. 直到某个原型指向null则意味着到原型链尽头,停止查找 返回undefined console.log(obj.msg); // undefined // 自定义原型链 obj.__proto__ = { // msg: "a", }; obj.__proto__.__proto__ = { msg: "b", }; obj.__proto__.__proto__.__proto__ = { msg: "c", }; console.log(obj.msg); // b console.log( obj.__proto__.__proto__.__proto__.__proto__ === Object.prototype ); // true console.log(Object.prototype.__proto__); // null

5. 继承

https://www.codefather.cn/post/1628296538633613314

0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
下载 APP