一. 普通对象与函数对象
javascript 中,万物皆对象!但对象也是有区别的。分为普通对象和函数对象,Object 、Function等 是 JS 自带的函数对象。下面举例说明。
var o1 = {}; var o2 =new Object(); var o3 = new f1(); function f1(){}; var f2 = function(){}; var f3 = new Function(‘str‘,‘console.log(str)‘); console.log(typeof Object); //function console.log(typeof Function); //function console.log(typeof f1); //function console.log(typeof f2); //function console.log(typeof f3); //function console.log(typeof o1); //object console.log(typeof o2); //object console.log(typeof o3); //object
二. 原型对象
function Person(){} Person.prototype.name = ‘test‘; Person.prototype.age = 28; Person.prototype.job = ‘Software Engineer‘; Person.prototype.sayName = function() { alert(this.name); }
var person = new Person();
在 JavaScript 中,每当定义一个对象(函数也是对象)时候,对象中都会包含一些预定义的属性。其中每个函数对象都有一个prototype属性,这个属性指向函数的原型对象。而原型对象他本身就是一个普通对象(没有通过 new Function() 创建的对象都是普通对象),即原型对象就是Person.prototype,如果你还是害怕它,那就把它想想成一个字母 A:var A = Person.prototype。这里要强调一点,只有函数对象才会拥有prototype属性,但是每个对象都拥有__proto__属性(null除外)。
在上面我们给A添加了四个属性:name、age、job、sayName。其实它还有一个默认的属性:constructor。在默认情况下,所有的原型对象都会自动获得一个 constructor(构造函数)属性,这个属性(是一个指针)指向 prototype属性所在的函数(Person)。即:Person.prototype.constructor == Person。当我们创建对象var person = new Person()时,person可以继承原型对象Person.prototype的constructor属性,因此person.constructor == Person。
person.constructor == Person
Person.prototype.constructor == Person
从这一角度,我们可以将Person.prototype理解为Person的一个实例。(但其实原型对象(Person.prototype)并不是构造函数(Person)的实例,而是构造函数的属性,而且是预定义添加的)。
var A = new Person(); Person.prototype = A;
但是有一个非常特别的原型对象:Function.prototype,它并不是普通对象,而是函数对象,而这个函数对象却没有prototype属性(前面所说的“每个函数对象都有一个prototype属性,这个属性指向函数的原型对象”,对Function.prototype并不适用)。
function Person(){}; console.log(typeof Person.prototype) //Object console.log(typeof Function.prototype) // Function,这个特殊 console.log(typeof Object.prototype) // Object console.log(typeof Function.prototype.prototype) //undefined
Function.prototype为什么是函数对象呢?
var A = new Function(); Function.prototype = A;
上文提到过凡是通过凡是通过 new Function() 创建的对象都是函数对象,其他的都是普通对象。因为 A 是函数对象,所以Function.prototype是函数对象。
三. __proto__
JS 在创建对象(不论是普通对象还是函数对象)的时候,都有一个叫做__proto__的内置属性,用于指向创建它的构造函数的原型对象。对象 person有一个__proto__属性,创建它的构造函数是Person,构造函数的原型对象是Person.prototype ,所以:person.__proto__ == Person.prototype
Person.prototype.constructor == Person; person.__proto__ == Person.prototype; person.constructor == Person;
类似的,Person.__proto__ == Function.prototype;Person.prototype.__proto__ == Object.prototype;Object.__proto__ == Function.prototype; Object.prototype.__proto__ == null,按照上述理解 Object.prototype是普通对象,而普通对象的构造函数是Object,那么Object.prototype.__proto__ == Object.prototype,从而在原型链上形成死循环无法终止,因此定义Object.prototype.__proto__ == null,null是原型链的顶端。
不过,要明确的真正重要的一点就是,这个连接存在于实例(person)与构造函数(Person)的原型对象(Person.prototype)之间,而不是存在于实例(person)与构造函数(Person)之间。
var animal = function(){}; var dog = function(){}; animal.price = 2000; dog.prototype = animal; var tidy = new dog(); console.log(dog.price) //undefined console.log(tidy.price) // 2000
实例(tidy)和 原型对象(dog.prototype)存在一个连接。这个连接存在于实例(tidy)与构造函数的原型对象(dog.prototype)之间,而不是存在于实例(tidy)与构造函数(dog)之间。
四. 函数对象
所有函数对象的__proto__都是指向Function.prototype,它是一个空函数。
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 // 所有的构造器都来自于Function.prototype,甚至包括根构造器Object及Function自身 Object.__proto__ === Function.prototype // true Object.constructor == Function // true // 所有的构造器都来自于Function.prototype,甚至包括根构造器Object及Function自身 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的属性及方法。Function.__proto__ == Function.prototype,而前面说过,Function.prototype它不是普通对象,而是函数对象,那么Function.prototype.__proto__ == ?,按照上述,Function.prototype.__proto__ == Function.prototype,但又出现了原型链上的死循环,JS一直强调万物皆对象,函数对象也是对象,给他认个祖宗,指向 Object.prototype,Object.prototype.__proto__ == null,保证原型链能够正常结束。
特别的,Math,JSON是以普通对象形式存在的。
Math.__proto__ === Object.prototype // true Math.construrctor == Object // true JSON.__proto__ === Object.prototype // true JSON.construrctor == Object //true
四. 总结
原型和原型链是JS实现继承的一种模型。
原型链的形成是真正是靠__proto__而非prototype。
参考资料:https://www.jianshu.com/p/dee9f8b14771