原型链与继承
说明
原型、原型链、原型链实现继承笔记总结
# 前言
感谢掘金用户 月光也会跟着我 月光也会跟着我 的个人主页 - 动态 - 掘金 (juejin.cn) (opens new window) 让我对原型和原型链有了更加透彻的理解
# 构造函数
因为JS中没有类(Class)这个概念,所以JS的设计者使用了构造函数来实现继承机制。
// 构造函数
function Person(name, age) {
this.name = name;
this.age = age;
}
// 生成实例
const p = new Person('zhangsan', 18);
2
3
4
5
6
7
8
JS通过构造函数来生成实例。但是又出现了一个新的问题,在构造函数中通过this
赋值的属性或者方法,是每个实例的实例属性以及实例方法,无法共享公共属性。所以又设计出了一个原型对象,来存储这个构造函数的公共属性以及方法
# 原型对象
每个函数都有一个 prototype 属性,这个属性指向一个对象,这个对象就是此函数的原型对象。该原型对象中有个属性为constructor
,指向该函数。这样原型对象和它的函数之间就产生了联系。
什么是原型呢?每一个JavaScript对象(null除外)在创建的时候就会与之关联另一个对象,这个对象就是我们所说的原型,每一个对象都会从原型"继承"属性。
let Person = {}
Person.prototype = {
message: "Hello Person",
info: { name: "哈哈哈", age: 30 },
running: function () {},
eating: function () {},
}
//将 Person 的原型对象中的 constructor 属性设置为 Person 构造函数本身,保证了在创建实例对象时,constructor 属性指向正确的构造函数
Object.defineProperty(Person.prototype, "constructor", {
value: Person
})
console.log(Object.keys(Person.prototype)); // ['message', 'info', 'running', 'eating']
---------------------------------------------------
// Object.prototype.hasOwnProperty()
// 对象是否有某一个属于自己的属性(不包括在原型上的属性)
function Person() {
this.name = 5
this.age = 10
}
Person.prototype = {
id:5
}
let p = new Person()
console.log(p.hasOwnProperty('name')); // true
console.log(p.hasOwnProperty('id')); // false
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 原型链
hasOwnProperty()
Object.keys()
处理属性并且不会遍历原型链的方法
// 构造函数
function Preson(name, age) {
this.name = name;
this.age = age;
}
// 所有实例共享的公共方法
Preson.prototype.say = function (word) {
console.log(`${this.name}说:${word}`);
}
const p1 = new Preson('张三', 18); // 创建一个Person实例对象
p1.hasOwnProperty('say') // false 说明不是定义在其本身上的
p1.say('hello world'); // 调用公共方法 打印:张三说:hello world
2
3
4
5
6
7
8
9
10
11
12
13
14
我们构造的p1
这个实例对象
,它可以调用到Person
这个构造函数
的原型对象
上的方法是因为每个通过构造函数
创建出来的实例对象
,其本身有个属性__proto__
,这个属性会指向该实例对象
的构造函数
的原型对象
当访问一个对象
的某个属性时,会先在这个对象本身属性上查找,如果没有找到,则会通过它的__proto__
隐式属性,找到它的构造函数
的原型对象
,如果还没有找到就会再在其构造函数
的prototype
的__proto__
中查找,这样一层一层向上查找就会形成一个链式结构,我们称为原型链
。
![image-20230519015456585](/assets/img/image-20230519015456585.76781db1.png)
function fn() {}
let obj = {}
Function.prototype.age1 = 20
Object.prototype.age1 = 200
console.log(Object.age1); // 20
console.log(obj.age1); // 200
const n = 3;
n.constructor === Number; // true
const o1 = {};
o1.constructor === Object; // true
const a1 = [];
a1.constructor === Array; // true
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# new的过程
构造函数创建一个实例的过程:
- 创建一个新对象
- 这个对象内部的
__prototype__
属性会被赋值为该实例构造函数的prototype属性; - 将构造函数的作用域赋值给新对象(这样this就指向了新对象)
- 执行构造函数中的代码(为新对象添加实例属性和实例方法)
- 返回新对象
// 手写new
function myNew(fn, ...args) {
if (Object.prototype.toString.call(fn) !== '[object Function]') {
throw 'Error in params';
}
let obj = {};
// obj.__proto__ = Fn.prototype 但是__proto__ 属性是一个有争议不建议使用的属性
obj = Object.create(fn.prototype);
// 新创建的对象作为this的上下文并传入参数
let ret = fn.call(obj, ...args);
return ret instanceof Object ? ret : obj;
}
// 验证
function Person(name, age) {
this.name = name;
this.age = age;
}
let person1 = myNew(Person, '张三', 15);
let person2 = new Person('李四', 15);
------------------------------------------
// Object.create() 方法用于创建一个新对象,使用现有的对象来作为新创建对象的原型
Object.myCreate = function (proto, propertyObject = undefined) {
if (propertyObject === null) {
// 这里没有判断propertyObject是否是原始包装对象
throw 'TypeError'
} else {
function Fn () {}
Fn.prototype = proto
const obj = new Fn()
if (propertyObject !== undefined) {
Object.defineProperties(obj, propertyObject)
}
if (proto === null) {
// 创建一个没有原型对象的对象,Object.create(null)
obj.__proto__ = null
}
return obj
}
}
// 简易手写create
function create(obj) {
function F() {}
F.prototype = obj
F.prototype.constructor = F
return new F()
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# 原型链实现-方法继承
通过实例化一个新的函数,子类的原型指向了父类的实例,子类就可以调用其父类原型对象上的私有属性和公有方法。
缺点:原型链继承实质上是重写原型对象。所以,如果在继承前就在子类的 prototype 上定义了一些方法和属性,那么继承后,子类的这些属性和方法将会被覆盖。所有新实例都会共享父类实例的属性,创建实例时无法向Parent传参
function father(name,age) {
this.name = name
this.age = age
this.des = 'dad'
}
father.prototype.show = function () {
console.log('dad');
}
function son(name,age,sex) {
this.name = name
this.age = age
this.sex = sex
this.des = 'son'
}
let f1 = new father();
son.prototype = f1
let s1 = new son('rio',20,'man');
s1.show() // dad
console.log(s1); // {name: 'rio', age: 20, sex: 'man', des: 'son'}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 借用构造函数-属性继承
利用 call
或者 apply
把父类中通过 this
指定的属性和方法复制(借用)到子类创建的实例中。
function father(name,age) {
this.name = name
this.age = age
}
function son(name,age,sex) {
father.call(this,name,age)
this.sex = sex
}
let s1 = new son('rio',20,'man');
console.log(s1);
2
3
4
5
6
7
8
9
10
缺点:只能继承父类构造函数的属性。
# 组合寄生继承
即通过借用构造函数来继承属性,通过原型链的混成形式来继承方法
思路是:不必为了子类型的原型而调用父类型的构造函数
function Parent(name,age) {
this.name = name;
this.age = age;
}
Parent.prototype.getAge = function(){
return this.age
}
function Child(name,age,id) {
Parent.call(this, name,age);
this.id = id
}
// Object.create 创建一个对象使其隐式原型指向括号内该目标
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
// 测试结果
let c1 = new Child("c1")
var c2 = new Child('c2');
console.log(c1 instanceof Child); // true
console.log(c1 instanceof Parent); // true
console.log(c1.constructor); // Child
console.log(Child.prototype.__proto__ === Parent.prototype); // true
console.log(Parent.prototype.__proto__ === Object.prototype); // true
-----------------------------------------------------
function Person(name) {
this.name = name
this.sum = function () {
alert(this.name)
}
}
Person.prototype.age = 10;
function content(obj) {
function F() {}
F.prototype = obj;
return new F();
}
var con = content(Person.prototype);
function Per(name) {
Person.call(this,name)
}
Per.prototype = con;
con.constructor = Per;
let p = new Per('rio');
console.log(p.name);
console.log(p.age);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# 参考
一文搞懂JS原型与原型链(超详细,建议收藏) - 掘金 (juejin.cn) (opens new window)
JavaScript 继承的八种方式 - 腾讯云开发者社区-腾讯云 (tencent.com) (opens new window)
现代 JavaScript 教程 (opens new window)
MDN Web Docs (mozilla.org) (opens new window)
JavaScript高级程序设计(第4版)