constructor
在js设计之初,constructor 是专门为 function 设计的,所以每一个函数的原型上都有constructor属性,并且当前函数的原型上的constructor属性指向函数自身。
let fo = function () {};
console.log(fo.prototype.constructor === fo); // true
什么是原型,原型链?
原型:每一个 JavaScript 对象(null 除外)在创建的时候就会与之关联另一个对象,这个对象就是我们所说的原型,每一个对象都会从原型"继承"属性,其实就是 prototype 对象。
原型链:由相互关联的原型组成的链状结构就是原型链。
如图所示:我们通过new的形式构造一个实例对象(person),每一个实例对象身上都有隐藏的属性
__proto__
,这是链接实例对象和原型的桥梁,(__proto__
就是一条原型链中的一环),通过这种方式我们可以访问原型对象上的属性和方法。由于原型对象本身也是一个对象,它也有__proto__
属性,同样它也可以向上查找它的原型属性和方法,这种链式的查找结构就是原型链。
函数和对象的关系
函数是对象,对象同时都是通过函数创建的(js中一切皆对象,故而存在函数对象和普通对象,函数和对象之间不是简单的包含关系,这个需要悟,不再赘述。。。。)
原型的类别
显示原型:prototype,函数(function)独有的属性
let obj = {};
let fo = function () {};
console.log(fo.prototype); // {constructor: ƒ, …}
console.log(obj.prototype); // undefined
从上面可以看出只有函数才有显示原型,对象并不才存在显示原型。
隐式原型:__ proto __,对象都具有的属性
let obj = {};
let fo = function () {};
console.log(fo.__proto__); // ƒ () { [native code] }
console.log(obj.__proto__); // {constructor: ƒ, …}
每个对象都有__proto__属性,Object、Function都是js内置的函数, 类似的还有我们常用到的Array、RegExp、Date、Boolean、Number、String
js中两个最顶层的概念
最顶层的构造器---Function(function是js中最顶层的构造器,它构造了所有的对象,包括它自己)
let fo = function () {};
console.log(fo.prototype); // ƒ () { [native code] }
console.log(fo.prototype.__proto__); // Object.prototype
console.log(fo.prototype.__proto__.__proto__); // null
每一个函数都有自己显示原型(fo.prototype),又由于显示原型本身也是一个对象,所以fo.prototype拥有自己的隐式原型指向Object的原型对象,而Object的原型对象指向null。
最顶层的对象---Object(所有的对象都继承了Object原型,Object也是function构造出来的)
let obj = {};
console.log(obj.__proto__); // {constructor: ƒ, …}
console.log(obj.__proto__.__proto__); // null
从上面可以看出普通对象的隐式原型指向Object的原型,所以可以推导出 obj.__proto__ === Object.prototype;
。又由第一幅图中的普通函数中原型对象也是指向Object的原型,同样可以推导出 fo.prototype.__proto__ = Object.prototype;
两者都和Object的原型对象存在对等关系。由此我们似乎可以得出函数和对象的某种关系,于是联想到构造函数...
在构造函数中,实例对象(即person)的隐式原型(__proto__
)等于构造该对象的函数的显示原型,(即每个对象的隐式原型都指向构建该对象的函数的显示原型)
function Person() {}
let person = new Person();
console.log(person.__proto__=== Person.prototype); //true
推导公式
1.普通对象的隐式原型和Object的显示原型相等
let obj = {}
console.log(obj.__proto__ === Object.prototype); //true
2.函数对象的隐式原型和Function的显示原型相等
function Fn() {}
console.log(Fn.__proto__ === Function.prototype); //true
3.所有的构造器都是函数对象,函数对象都是 Function 构造产生的
Object.__proto__ === Function.prototype
解释:从上面的三点可以得出,普通对象的产生是由Object构造器构造的,而函数对象的产生是由Function构造器构造的,所有构造器都是函数,所以Object是函数对象,又由于函数对象都是 Function 构造产生的。
4.原型对象(Foo.prototype)本身是一个普通对象,而普通对象的构造函数都是Object,所以普通对象的隐式原型和构造它的Object的显示原型相等。(参考1)
function Foo(){}
Foo.prototype.__proto__ = Object.prototype;
5.Object 的原型对象也有__proto__属性指向null,null是原型链的顶端
Object.prototype.__proto__ = null;
6.Function.prototype本质也是普通对象,普通对象的隐式原型指向Object的显示原型。
Function.prototype.__proto__ = Object.prototype;
7.参考constructor
Function.prototype.constructor = Function;
四、原型链的查找方式
当JavaScript引擎发现一个对象访问一个属性时,会首先查找对象的“自有属性”,如果没有找到则会在[[proto]]属性指向的原型属性中继续查找,如果还没有找到的话,你知道其实原型属性也是一个对象,所以它也有一个隐式的[[proto]]属性指向它的原型属性...,正如你所料,如果一直没有找到该属性,JavaScript引擎会一直这样找下去,直到找到最顶部构造函数Object的prototype原型属性,如果还是没有找到,会返回一个undefined值。这个不断查找的过程,有一个形象生动的名字“攀爬原型链”。