原型及原型链

2021-04-21  本文已影响0人  晨曦_f83d
原型是什么

原型:每个函数都有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关键字做了什么
  1. 一个继承自 Player.prototype 的新对象 whitePlayer 被创建
  2. whitePlayer.proto[图片上传失败...(image-c4e084-1618934433846)]
    指向 Player.prototype,即 whitePlayer._proto _= Player.prototype
  3. 将 this 指向新创建的对象 whitePlayer
  4. 返回新对象
    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

参考
原型和原型链

上一篇下一篇

猜你喜欢

热点阅读