上篇文章深入探讨了原型的概念,以及构造函数,实例对象,原型对象三者之间的关系。并且简单提到了原型链。在 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上!
包括 Function 和 Object 这两个对象原型链。
下面的对象的原型链都会追溯到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.prototype,Object.prototype是所有原型链的终点!
最后贴一张网上久负盛名的原型链图,有点难读,但看完上面的内容,相信对你来说是小菜一碟:
one more thing
instanceof
在 JavaScript 中,instanceof 是一个 运算符,用于测试一个对象是否是某个构造函数的实例 。
语法:
实例对象 instanceof 构造函数
工作原理:
instanceof 检查的是对象的原型链,而不是构造函数本身。其判断逻辑如下:
- 获取
constructor.prototype。 - 沿着
object的原型链逐层查找。 - 如果在原型链中找到与
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);
控制台打印结果:
大家可以自行思考为什么🤭