前两天有同事问我:"__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 具体做了什么
- 创建一个新对象
- 将构造函数的原型 (prototype) 赋给新对象的 __proto__
- 在新对象的 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,
}
}
};
把它们的关系画成图,就如下图
综上所述我们可以知道 Function 的原型是 Object, 但 Object 本身又是 Function
end