js 原型链的个人理解记录

前两天有同事问我:"__proto__, prototype, constructor 在原型链里是什么作用?"
今天就在稍微梳理一下

根据某翻译工具翻译
proto: 原始; 原型;
prototype: 原型; 雏形; 最初形态;
constructor: 建造者, 制造者, 建造商;

根据字面意思,粗略可以这样理解
__proto__: 指向原型的指针
prototype: 函数才有的属性, 实例化对象的原型(简称实例化原型), 因为 Fun.key 这样的写法不能继承给后代, 所以要用 Fun.prototype.key 的写法
constructor: 建造者,构造器; prototype.constructor 指向 该 prototype 对应的构造函数

__proto__

对象都有 __proto__ 属性, 用于指明自己的原型
当原型又有原型, 原型的原型又有原型时, 就是靠 __proto__ 一层一层地往上找到最上层的原型
在这个无限套娃的过程中, 一个接一个的原型组成的, 就叫做原型链
顺带一提, Object 提供一个方法 getPrototypeOf(), 用于取对象的 __proto__

Object.getPrototypeOf(son) === son.__proto__

prototype

声明一个函数时, 函数就有 prototype 属性, 用于保存被继承的方法和属性
与对象不同, 函数作为构造函数时, 实例化对象不会把所有内容继承过来, 只有构造函数的原型 prototype 里的内容, 才能被后代继承
也就是说, 构造函数的 prototype === 实例化对象的 __proto__
题外话, 箭头函数没有 prototype

var son = new Father()
Father.prototype === son.__proto__; //true

constructor

prototype 中有一个属性叫 constructor, 它的作用就是指向 prototype 对应的构造函数

Father.prototype.constructor === Father; //true

new 具体做了什么

  1. 创建一个新对象
  2. 将构造函数的原型 (prototype) 赋给新对象的 __proto__
  3. 在新对象的 this 环境中执行构造函数中的代码(一次性)
var son = new Father("1")
// 相当于
var son = {}
son.__proto__ = Father.prototype;
Father.call(son, "1");

基于以上几个步骤, 无论多复杂的对象, 都可以准确找到其原型






自定义的对象归根到底就是上面说的那几点, 而原生对象的关系却复杂得多

<html>

	<head>
		<title>JavaScript原型链的个人理解记录</title>
	</head>

	<body>

		<script type="text/javascript">
			var Father = function Father(name) {
				this.name = name
			}
			Father.prototype.p1 = "实例化前 Father.prototype"
			var son = new Father("1")
			var son2 = new Father("2")
			Father.prototype.p2 = "实例化后 Father.prototype"
			son.__proto__.p3 = "实例化后 son.__proto__"

			/* *** */

			// 这两个不是计算结果, 而是工作原理
			console.log(Father.prototype === son.__proto__); //true
			console.log(Father.prototype.constructor === Father); //true

			// Father 本质是 Function new 出来的对象, 就像 son 由 Father new 出来一样
			console.log(Function.prototype === Father.__proto__); //true

			// son 本身没有 constructor, 没有就会沿原型链向上找, 也就是 son.__proto__[.__proto__ ...], 直到 "找到" 或 "没有更多的.__proto__"
			// 本例 son.constructor 实际上是沿原型链往上一层的 son.__proto__.constructor
			console.log(son === son.__proto__); //false
			console.log(son.constructor === son.__proto__.constructor); //true
			console.log(son.constructor === son.__proto__.__proto__.constructor); //false
			console.log(Object === son.__proto__.__proto__.constructor); //true

			console.log(Object.prototype.toString.call(Father) === "[object Function]"); //true
			console.log(Object.prototype.toString.call(Function) === "[object Function]"); //true
			console.log(Object.prototype.toString.call(Object) === "[object Function]"); //true

			// 根据以上条件, 结合简单的换算可以发现以下及更多全等关系
			console.log(Father.prototype === son.constructor.prototype); //true
			console.log(Father.prototype.constructor === son.__proto__.constructor); //true

			console.log(Object.__proto__ === Function.__proto__); //true
			console.log(Object.__proto__ === Father.__proto__); //true
			console.log(Object.__proto__ === Function.prototype); //true

			/* *** */

			console.log(Father.prototype.p1); // 实例化前 Father.prototype
			console.log(son.__proto__.p1); // 实例化前 Father.prototype
			console.log(son2.__proto__.p1); // 实例化前 Father.prototype

			console.log(Father.prototype.p2); // 实例化后 Father.prototype
			console.log(son.__proto__.p2); // 实例化后 Father.prototype
			console.log(son2.__proto__.p2); // 实例化后 Father.prototype

			console.log(Father.prototype.p3); // 实例化后 son.__proto__
			console.log(son.__proto__.p3); // 实例化后 son.__proto__
			console.log(son2.__proto__.p3); // 实例化后 son.__proto__

			// end
		</script>
	</body>

</html>

上例显示无论实例化前或后, 还是通过 prototype(实例化原型) 或 __proto__(实例化对象) 的修改, 实例化原型和所有实例化对象, 都能取到相同的结果

这就是上面所说的 “构造函数的 prototype === 实例化对象的 __proto__”


通过 demo 可以看到, __proto__, prototype, constructor 三个大佬间, 是有来有往, 战况可谓一片混乱
这几个对象的大概结构如下

son = {
	name: "1",
	__proto__: {
		p1: "实例化前 Father.prototype",
		p2: "实例化后 Father.prototype",
		p3: "实例化后 son.__proto__",
		constructor: Father,
		__proto__: {
			constructor: Object,
		}
	}
};

Father = {
	prototype: {
		p1: "实例化前 Father.prototype",
		p2: "实例化后 Father.prototype",
		p3: "实例化后 son.__proto__",
		constructor: Father,
		__proto__: {
			constructor: Object,
		}
	},
	__proto__: {
		constructor: Function,
		__proto__: {
			constructor: Object,
		}
	}
};

Function = {
	prototype: {
		constructor: Function,
		__proto__: {
			constructor: Object,
		}
	},
	__proto__: {
		constructor: Function,
		__proto__: {
			constructor: Object,
		}
	}
};

Object = {
	prototype: {
		constructor: Object,
	},
	__proto__: {
		constructor: Function,
		__proto__: {
			constructor: Object,
		}
	}
};

把它们的关系画成图,就如下图
关于JavaScript原型链
综上所述我们可以知道 Function 的原型是 Object, 但 Object 本身又是 Function

end

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值