JS中原型与原型链

Posted RainyBear

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JS中原型与原型链相关的知识,希望对你有一定的参考价值。

一. 普通对象与函数对象

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
在上面的例子中 o1 o2 o3 为普通对象,f1 f2 f3 为函数对象。怎么区分,其实很简单,凡是通过 new Function() 创建的对象都是函数对象(Function),其他的都是普通对象(Object)。f1,f2,归根结底都是通过 new Function()的方式进行创建的。Function Object也都是通过 New Function()创建的

二. 原型对象

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



以上是关于JS中原型与原型链的主要内容,如果未能解决你的问题,请参考以下文章

图解Javascript原型链

浅谈js原型与原型链

JS原型与原型链终极讲解

js原型链和继承的理解

js 原型与原型链

js中原型和原型链理解