对于原型和原型链的理解
AlexChiang 2020-02-14
JavaScript
本文为笔者阅读大量 JS 原型链相关网页文章与高级程序设计一书中相关章节所得见解的记录
参考: 用自己的方式(图)理解原型链
# 理解constructor
constructor 属性其实就是一个拿来保存自己构造函数引用的属性
# prototype
出现的原因
当你想要给 Person 的两个实例加上一个效果相同的方法
- 如果不使用 prototype:
- 使用 prototype:
当实例有千万个时 在 prototype 对象上存放共享的属性和方法将节省大量内存
# constructor
属性的位置
思考:
为什么 person1,person2 本身没有constructor
属性?
如果每个new Person()
构造出来的实例都有一个直接且相同的(相同指完全可以用作共享方法)constructor
属性指向创建自己的构造函数,那将会非常多余且占内存。
对象的 constructor 属性被当做共享属性放在他们的原型对象中 如下图
Person.prototype.constructor = Person
# __proto__
的作用
- 实例对象.__proto__ = 创建自己的构造函数内部的 prototype(原型对象)
- 实例对象.__proto__.constructor = 创建自己的构造函数
# 什么是原型链?
链外的函数如 Person()通过.prototype 或.__proto__方法回到链上
.prototype 回到自己的原型对象上
.__proto__ 回到自己的构造函数的原型对象上
# 原型链引出的继承方式
- prototype 对象保存着构造函数给它的实例们调用的共享属性和方法。
- 实例对象当没有某一属性时,会通过 proto 属性去找到创建它们的构造函数的 prototype 对象,并在里面找有没有相关的共享属性或方法。
继承写法: 子.prototype = new 父()
即可改变 prototype 指向
该继承方式的问题:
- constructor 不可靠 (应该通过 Son.proto.constructor 指向 Father 实际指向 GrandFather)
- 继承下来的所有属性都是共享属性
# 手写 new
首先要明确 new 构造调用的过程
- 创建新对象
- this 指向新对象
- 执行构造函数代码
- 返回新对象
- 先写一个父级函数
function Person(id) {
this.id = id || "Person"; // 没有添加在.prototype上的属于私有属性
}
1
2
3
2
3
- 创建一个空对象子级
var obj = {};
1
- 至此 父子不相认 手动连接
obj.__proto__ = Person.prototype; // 子认父
1
- 至此 父还未认子 因为子并没有父的私有属性
在实例的执行环境内调用构造函数,添加构造函数设置的私有属性/方法。
Person.apply(obj, arguments); // arguments就是参数
1
- 整合代码
// 构造函数登场
function Person(identity) {
this.identity = identity || "Person";
}
// 实例对象登场
var obj = {};
// 环节一:让obj承认自己的构造函数(爹)就是Person函数
obj.__proto__ = Person.prototype;
// 环节二:obj调用Person,拥有Person给孩子们设置的属性/方法
// 让Person函数承认这个对象就是它自己的实例(子)
Person.apply(obj, ["son"]);
// End 完成,验证
console.log(obj.constructor); // 输出结果:[Function: Person]
console.log(obj.identity); // 输出结果:son
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
- 封装
// 构造函数登场
function Person(identity) {
this.identity = identity || "Person";
}
// 封装自己的new
function _new(Fuc) {
return function() {
var obj = {
__proto__: Fuc.prototype
};
Fuc.apply(obj, arguments);
return obj;
};
}
// 封装完成,测试如下
var obj = _new(Person)("son");
console.log(obj.constructor); // 输出结果:[Function: Person]
console.log(obj.identity); // 输出结果:son
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18