💡JS-深入原型链

438 阅读4分钟

上篇文章深入探讨了原型的概念,以及构造函数,实例对象,原型对象三者之间的关系。并且简单提到了原型链。在 JavaScript 中,原型链是通过 proto 属性连接起来的一系列对象,对象查找属性就是沿着原型链查找的。

这篇文章深入聊聊原型链

即是函数也是对象

function Fn(name) {
    this.name = name;
}

const obj = new Fn('zenos');

Fn 是一个很简单的构造函数,但它也是一个对象!其实上篇文章已经表现出它的对象的一面了:

console.log(Fn.prototype === obj.__proto__); // true

Fn.prototype这个表达式,不就是典型的访问对象属性的方式吗?

既然 Fn 是一个对象,那我们就可以对这个对象的属性进行“增删改查”了

function Fn(){
};

Fn.helloMsg = 'i am Fn function';

Fn.hello = function(){
  return this.helloMsg
}

Fn.hello();
console.log(Object.keys(Fn)); // ['helloMsg', 'hello']

再看一段代码:

// 创建一个函数
const newFn = new Function('a','b', 'return a+b;');
// 创建一个对象
const newObj = new Object();

上面用不同的构造函数,分别创建了一个函数和一个对象。

还有,Function 和 Object 在这里是被作为构造函数的,所以 newFn 是 Function 的实例对象,newObj 是 Object 的实例对象,所以有:

console.log(newFn.__proto__ === Function.prototype);
console.log(newObj.__proto__ === Object.prototype);

那 Function 和 Object 在这里表现为函数,那就有 prototype 属性,也就意味着:

console.log(Function.prototype !== undefined);
console.log(Object.prototype !== undefined);

而Function 和 Object 本身是对象,所以 Function 和 Object 有它们自己 proto 属性,所以有:

console.log(Function.__prot__ !== undefined);
console.log(Object.__proto__ !== undefined);

看到这里你是不是有点晕了,下面我画个图,帮助你理解:

画板

深入了解 Funtion 和 Object

Function

所有的函数都是来自Function,且都是 Function 的实例( 可以理解为都是由 Function 创建的),包括 Object!!所以有:

console.log((function(){}).__proto__ === Function.prototype); // true
console.log((()=>{}).__proto__ === Function.prototype); //true
console.log(Object.__proto__ === Function.prototype); // true

**注:**判断一个对象是由谁创建的,就看一个对象的是不是某个构造函数的实例对象。

function Fn(){}

const fnObj = new Fn();

这里的 fnObj 是 Fn 创建的,即是 Fn 的实例对象。它们满足下面的关系:

console.log(fnObj.__proto__ === Fn.prototype); //true

也就意味着,我们就看这个对象的__proto__是不是和某个构造函数的prototype指向同一个对象,就能判断这个对象是不是由该构造函数创建的了


既然所有的函数都是来自 Function, 那 Function 自己是被谁创建出来的呢?


**答:**是被 Function 自己创建的

对的,“自己创建了自己”

所以有:

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

Object

在 JavaScript 中,所有对象的原型链最终会追溯到 Object.prototype

什么意思呢?当你在对象上查找属性的时候,如果一直没找到,那么最终一定会找到Object.prototype上!

包括 FunctionObject 这两个对象原型链。

下面的对象的原型链都会追溯到Object.prototype

  • Function.proto
  • Function.prototype
  • Fn.proto
  • {}
  • new Object()

也就有:

console.log(Function.__proto__.__proto__ === Object.prototype);
console.log(Function.prototype.__proto__ === Object.prototype);
console.log(Fn.__proto__.__proto__ === Object.prototype);
console.log(({}).__proto__ === Object.prototype);
console.log((new Object()).__proto__ === Object.prototype);

控制台的打印:

现在可以得到这样一个结论: 在 JavaScript 中,所有对象都继承自 Object。Object 是所有对象的“父类”

如果你之前学过 java,对上面这句话是否似曾相识呢?🤭

那么

Object.prototype 也是对象,它的原型对象是谁呢?即Object.protype.__proto__ === ?

直接问控制台吧!

可以看出 Object.prototype 并没有原型对象,原型链到这里就终止了!这里是所有原型链的终点!

图解原型链

怎么样,看到这里是什么感觉呢?有没有一点小震撼呢,除此之外,是否还有点理不清的感觉呢?不急,这个小节用几张图片,将上面讲过的内容串一串,相信你很快就能理清了

函数和 prototype 的关系

画板

所有的函数都是 Function 的实例

画板

Object.prototype 是原型链的终点

画板

合并

画板

总结

这篇文章深入讲述了原型链的概念,从普通的函数,到特殊的函数 Function、Object,并且从函数和对象的角度来分析讲解 Function、Object,得到这些结论:

  • 在 JavaScript 中,原型链是通过 proto 属性连接起来的一系列对象,对象查找属性就是沿着原型链查找的
  • 所有的函数都是来自Function,且都是 Function 的实例
  • 在 JavaScript 中,所有对象的原型链最终会追溯到 Object.prototypeObject.prototype是所有原型链的终点!

最后贴一张网上久负盛名的原型链图,有点难读,但看完上面的内容,相信对你来说是小菜一碟:

one more thing

instanceof

在 JavaScript 中,instanceof 是一个 运算符,用于测试一个对象是否是某个构造函数的实例 。

语法:

实例对象 instanceof 构造函数

工作原理:

instanceof 检查的是对象的原型链,而不是构造函数本身。其判断逻辑如下:

  1. 获取 constructor.prototype
  2. 沿着 object 的原型链逐层查找。
  3. 如果在原型链中找到与 constructor.prototype 相同的对象,则返回 true;否则返回 false

举个例子,现在有一个对象 obj,一个函数 Fn,instanceof 的工作原理,简单来说,就是看obj.__proto__ ==== Fn.prototype是否成立?如果不成立,继续沿着原型链链往下找,即:

obj.__proto__ === Fn.prototype
obj.__proto__.__proto__ === Fn.prototype
obj.__proto__.__proto__.__proto__ === Fn.prototype
...
// 直到找到原型链的终点

一段代码

最后带你看一段代码:

console.log(Function instanceof Function);
console.log(Object instanceof Object);
console.log(Function instanceof Object);
console.log(Object instanceof Function);

控制台打印结果:

大家可以自行思考为什么🤭