原型是什么
原型:每个函数都有prototype(原型)属性,这个属性是一个指针,指向一个对象,这个对象的用途是包含特定类型的所有实例共享的属性和方法,这个对象(原型对象)是用来给实例共享属性和方法的。
在原型上添加属性和方法的好处
通过原型继承的方法并不是自身的,我们要在原型链上一层一层的查找,这样创建的好处是只在内存中创建一次,实例化的对象都会指向这个 prototype 对象。
怎么找到原型对象
function Player(color) {
this.color = color;
}
Player.prototype.start = function () {
console.log(color + "下棋");
};
const whitePlayer = new Player("white");
const blackPlayer = new Player("black");
console.log(blackPlayer.__proto__); // Player {}
console.log(Object.getPrototypeOf(blackPlayer)); // Player {},可以通过Object.getPrototypeOf来获取__proto__
console.log(Player.prototype); // Player {}
console.log(Player.__proto__); // [Function]
关系图可以为下图
image.png
new关键字做了什么
- 一个继承自 Player.prototype 的新对象 whitePlayer 被创建
- whitePlayer.proto[图片上传失败...(image-c4e084-1618934433846)]
指向 Player.prototype,即 whitePlayer._proto _= Player.prototype - 将 this 指向新创建的对象 whitePlayer
- 返回新对象
4.1 如果构造函数没有显式返回值,则返回 this
4.2 如果构造函数有显式返回值,是基本类型,比如 number,string,boolean, 那么还是返回 this
4.3 如果构造函数有显式返回值,是对象类型,比如{ a: 1 }, 则返回这个对象{ a: 1 }
下面看一下怎么手写实现 new 函数
// 1. 用new Object() 的方式新建了一个对象 obj
// 2. 取出第一个参数,就是我们要传入的构造函数。此外因为 shift 会修改原数组,所以 arguments 会被去除第一个参数
// 3. 将 obj 的原型指向构造函数,这样 obj 就可以访问到构造函数原型中的属性
// 4. 使用 apply,改变构造函数 this 的指向到新建的对象,这样 obj 就可以访问到构造函数中的属性
// 5. 返回 obj
function objectFactory() {
let obj = new Object();
let Constructor = [].shift.call(arguments);
obj.__proto__ = Constructor.prototype;
let ret = Constructor.apply(obj, arguments);
return typeof ret === "object" ? ret : obj;
}
原型链是什么
我们都知道当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性,如果还查不到,就去找原型的原型,一直找到最顶层为止。
举个例子
function Player() {}
Player.prototype.name = "Kevin";
var p1 = new Player();
p1.name = "Daisy";
// 查找p1对象中的name属性,因为上面添加了name,所以会输出“Daisy”
console.log(p1.name); // Daisy
delete p1.name;
// 删除了p1.name,然后查找p1发现没有name属性,就会从p1的原型p1.__proto__中去找,也就是Player.prototype,然后找到了name,输出"Kevin"
console.log(p1.name); // Kevin
鸡生蛋问题
我们看下面这段代码
Object instanceof Function // true
Function instanceof Object // true
Object instanceof Object // true
Function instanceof Function // true
于是可以得出
// Object instanceof Function 即
Object.__proto__ === Function.prototype // true
// Function instanceof Object 即
Function.__proto__.__proto__ === Object.prototype // true
// Object instanceof Object 即
Object.__proto__.__proto__ === Object.prototype // true
// Function instanceof Function 即
Function.__proto__ === Function.prototype // true
Function._proto_ === Function.prototype 这一现象有 2 种解释,争论点在于 Function 对象是不是由 Function 构造函数创建的一个实例?
解释 1、YES:按照 JavaScript 中“实例”的定义,a 是 b 的实例即 a instanceof b 为 true,默认判断条件就是 b.prototype 在 a 的原型链上。而 Function instanceof Function 为 true,本质上即 Object.getPrototypeOf(Function) === Function.prototype,正符合此定义。
解释 2、NO:Function 是 built-in 的对象,也就是并不存在“Function对象由Function构造函数创建”这样显然会造成鸡生蛋蛋生鸡的问题。实际上,当你直接写一个函数时(如 function f() {} 或 x => x),也不存在调用 Function 构造器,只有在显式调用 Function 构造器时(如 new Function('x', 'return x') )才有。
我个人偏向于第二种解释,即先有 Function.prototype 然后有的 function Function() ,所以就不存在鸡生蛋蛋生鸡问题了,把 Function._proto_ 指向 Function.prototype 是为了保证原型链的完整,让 Function 可以获取定义在 Object.prototype 上的方法。
----- the end
参考
原型和原型链
网友评论