从三个属性的角度弄清原型链

55 阅读3分钟

神图

原型与原型链(全).png

new关键词

new关键词做了什么?

  • 创建新对象
  • 将新对象的__proto__指向函数的原型
  • 改变函数内部的this指向,使this指向新对象(这部执行了函数这也是为什么新对象会有函数的属性和方法)
  • 返回新对象
// 仿写该函数接收的第一个参数位函数
function objectFactory() {
// 创建新对象
    var obj = new Object(),
// 取出第一个参数也就是函数
    Constructor = [].shift.call(arguments);
// 将新对象的__proto__指向函数的原型
    obj.__proto__ = Constructor.prototype;
// 改变函数内部的this指向,使this指向新对象
    var ret = Constructor.apply(obj, arguments);
// 返回新对象
    return typeof ret === 'object' ? ret : obj;

};

开篇前 首先我们需要明确两点:

  1. 只有Function所构造是实例是函数对象,其他一律为普通对象
  2. prototype属性是函数对象独有的。

proto

proto 的例子,说起来比较复杂,可以说是一个历史问题。 ECMAScript 规范描述 prototype 是一个隐式引用,但之前的一些浏览器,已经私自实现了 __proto__这个属性,使得可以通过 obj.proto 这个显式的属性访问,访问到被定义为隐式属性的 prototype。 因此,情况是这样的,ECMAScript 规范说 prototype 应当是一个隐式引用:

  • 通过 Object.getPrototypeOf(obj) 间接访问指定对象的 prototype 对象
  • 通过 Object.setPrototypeOf(obj, anotherObj) 间接设置指定对象的 prototype 对象
  • 部分浏览器提前开了 proto 的口子,使得可以通过 obj.proto 直接访问原型,通过 obj.proto = anotherObj 直接设置原型
  • ECMAScript 2015 规范只好向事实低头,将 proto 属性纳入了规范的一部分

而事实上,_proto__属他只是开发者工具方便开发者查看原型的故意渲染出来的一个虚拟节点。虽然我们可以查看,但实则并不存在该对象上。 __proto__属性既不能被 for in 遍历出来,也不能被 Object.keys(obj) 查找出来。 访问对象的 obj.proto 属性,默认走的是 Object.prototype 对象上 proto 属性的 get/set 方法。

Object.defineProperty(Object.prototype,'__proto__',{
	get(){
		console.log('get')
	}
});

({}).__proto__;
console.log((new Object()).__proto__); //get

prototype

函数对象独有 在规范里,prototype 被定义为:给其它对象提供共享属性的对象。prototype 自己也是对象,只是被用以承担某个职能罢了. 所有对象,都可以作为另一个对象的 prototype 来用。 它的作用就是包含可以给特定类型的所有实例提供共享的属性和方法。它的含义就是函数的共享对象,

到这里基本的原型原型链的出行就出来了

function lookUp(){

}
var lookup = new lookUp()
// 一定要记住js万物皆对象,一切对象都是函数对象new出来的,如何理解这句话?
typeof(Object) //function 
typeof(Function) //function
// 这个很特殊Function是原型的祖宗,他是自己的构造函数
console.log(Function.__proto__  == Function.prototype)//true
// 我们前文说了new 关键词做了什么?很容易的出
console.log(lookup.__proto__ === lookUp.prototype) //true
// 要知道函数对象是 Function new出来的
console.log(lookUp.__proto__  == Function.prototype) //true
// 函数的原型本质还是普通对象,只要是普通对象就是函数对象Object的实例
console.log(Function.prototype.__proto__ == Object.prototype)//true
//Object本质上还是函数对象只要是函数对象就是Function的实例
console.log(Object.__proto__  == Function.prototype)//true

所以这个图就出来了

prototype.jpg

constructor

constructor属性也是对象所独有的,它是一个对象指向一个函数,这个函数就是该对象的构造函数。 注意,每一个对象都有其对应的构造函数,本身或者继承而来。单从constructor这个属性来讲,只有prototype对象才有。 每个函数在创建的时候,JavaScript 会同时创建一个该函数对应的prototype对象,而函数创建的对象.proto === 该函数.prototype,该函数.prototype.constructor===该函数本身,故通过函数创建的对象即使自己没有constructor属性,它也能通过__proto__找到对应的constructor, 所以任何对象最终都可以找到其对应的构造函数

一定要记住JavaScript 原型的老祖宗: Function

它是它自己的构造函数。所以Function.prototype === Function.__proto。

最后我们得到了这张图

最终图.png

参考资料

github.com/mqyqingfeng… juejin.cn/post/684490…