前端的面试中常常会遇到这个问题,本身也是一直似懂非懂,趁这个机会整理一下
1994年,网景公司(Netscape)发布了Navigator浏览器0.9版,可是刚开始的Js没有继承机制,更别提像同时期兴盛的C++和Java这样拥有面向对象的概念。在实际的开发过程当中,工程师们发现没有继承机制很难解决一些问题,必须有一种机制能将全部的对象关联起来。javascript
Brendan Eich鉴于以上状况,但不想把Js设计得过为复杂,因而引入了new关键词 和 constructor构造函数来简化对象的设计,引入了prototype函数对象来包含全部实例对象的构造函数的属性和方法,引入了proto和原型链的概念解决继承的问题。html
var o1 = {}; var o2 =new Object(); var o3 = new f1(); function f1(){}; var f2 = function(){}; var f3 = new Function('str','console.log(str)');
凡是直接或者间接经过
new Function()
建立的对象都是函数对象,其余的都是普通对象。
在上面的程序中,o一、o二、o3都是普通对象,f一、f二、f3都是函数对象。前端
function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.sayName = function() { alert(this.name) } } var person1 = new Person('Zaxlct', 28, 'Software Engineer'); var person2 = new Person('Mick', 23, 'Doctor');
person1
和person2
都是 构造函数 Person 的实例。
实例的构造函数属性(constructor)指向构造函数。
Person.prototype = { name: 'Zaxlct', age: 28, job: 'Software Engineer', sayName: function() { alert(this.name); } }
每一个函数对象都有一个 prototype
属性,这个属性就是函数的原型对象。java
每一个对象都有 proto 属性,但只有函数对象才有 prototype 属性
在默认状况下,全部的原型对象都会自动得到一个 constructor(构造函数)属性,这个属性(是一个指针)指向 prototype 属性所在的函数(Person)面试
Person.prototype.constructor == Person
而在Person这个对象进行实例化的时候,其实是建立了一个它的实例对象并赋值给它的 prototype,因此得出如下结论:浏览器
原型对象(Person.prototype)是 构造函数(Person)的一个实例。
JS 在建立对象(不管是普通对象仍是函数对象)的时候,都有一个叫作__proto__ 的内置属性,用于指向建立它的构造函数的原型对象,也就是prototype。网络
Person.prototype.constructor == Person; person1.__proto__ == Person.prototype; person1.constructor == Person;
看下面一段代码app
Person.prototype.__proto__ === Object.prototype;
Person.prototype
是一个普通对象,咱们无需关注它有哪些属性,只要记住它是一个普通对象。
由于一个 普通对象的构造函数 === Object
因此 Person.prototype.__proto__ === Object.prototype
ide
原型链的造成是真正是靠__proto__ 而非prototype。
person1.__proto__ === Person.prototype; Person.prototype.__proto__ === Object.prototype; Object.prototype.__proto__ === null; //Object.prototype.__proto__ === null,保证原型链可以正常结束。 Person.__proto__ === Function.prototype; Object.__proto__ === Function.prototype; Function.prototype.__proto__ === Object.prototype;
前面三项已经造成了一个原型链,那么后面代码中Person和Object的__proto__都是Function.prototype
呢?函数
全部函数对象的proto都指向Function.prototype,它是一个空函数(Empty function)
Number.__proto__ === Function.prototype // true Number.constructor == Function //true Boolean.__proto__ === Function.prototype // true Boolean.constructor == Function //true String.__proto__ === Function.prototype // true String.constructor == Function //true Object.__proto__ === Function.prototype // true Object.constructor == Function // true Function.__proto__ === Function.prototype // true Function.constructor == Function //true Array.__proto__ === Function.prototype // true Array.constructor == Function //true RegExp.__proto__ === Function.prototype // true RegExp.constructor == Function //true Error.__proto__ === Function.prototype // true Error.constructor == Function //true Date.__proto__ === Function.prototype // true Date.constructor == Function //true
全部的构造器都来自于 Function.prototype,甚至包括根构造器Object及Function自身。全部构造器都继承了·Function.prototype·的属性及方法。如length、call、apply、bind
因此咱们再举一个原型链的例子
let num = new Number(); num.__proto__ === Number.prototype; Number.prototype.__proto__ === Function.prototype; Funtion.prototype.__proto__ === Object.prototype; Object.prototype.__proto__ === null;
也可看下图的实现,更直观。
ok, 因此明白为何Number、String、Array这样的对象实例能继承到Object的属性以及原型链是怎么一回事了吧。
参考文章
最详尽的 JS 原型与原型链终极详解,没有「多是」。
三张图搞懂JavaScript的原型对象与原型链 - 水乙 - 博客园
Javascript继承机制的设计思想 - 阮一峰的网络日志