js 原型链的那些事儿

前言

通常谈到js中的继承的时候,必定会遇到原型,原型链的问题,原型里面又有prototype,__proto__,constructor属性,讲到这儿,不少同窗是否是都一头雾水,傻傻分不清楚,由于工做中用到的地方是少之又少,再加上es6又出了extends语法糖,更加不用理会之,可是对于理解继承,原型和原型链是很重要的,理解函数和对象,理解prototype和__proto__,construct之间的关系尤其重要,不过本文对继承不予以深究,另起一篇文章写之,今天咱们只讨论js中的原型和原型链。javascript

首先,容在下提出一个问题。
到底prototype和__proto__是否是指同一个东西呢?
答案天然非也。java

还有一个问题,就是ie8,9下是没有__proto__的概念的,如何解决这个问题?
这个问题在这篇文章结束以前会说明。es6

如今咱们先来分析js中的对象。
js中对象是很重要的,所谓万物皆对象,可是js中对象分为两种,普通对象和函数对象函数

普通对象和函数对象

先来看几个例子this

function f1(){};
 var f2 = function(){};
 var f3 = new Function('str','console.log(str)');

 var o3 = new f1();
 var o1 = {};
 var o2 = new Object();

 console.log(typeof Object); //function
 console.log(typeof Function); //function
 console.log(typeof o1); //object
 console.log(typeof o2); //object
 console.log(typeof o3); //object
 console.log(typeof f1); //function
 console.log(typeof f2); //function
 console.log(typeof f3); //function

在上面的例子中,o1 o2 o3 为普通对象,f1 f2 f3 为函数对象。
那么怎么区分普通对象和函数对象呢?
其实很简单,凡是经过new Function()建立的对象都是函数对象,其余的都是普通对象。f1,f2,归根结底都是经过 new Function()的方式进行建立的。Function Object 也都是经过 New Function()建立的。.net

原型对象

接下来先说一下原型对象
在js中,每当定义一个对象的时候,对象中都会包含一些预约义的属性。其中函数对象的一个属性就是原型对象prototype
普通对象没有prototype,只有__proto__属性,看下面的例子prototype

function f1(){};
 console.log(f1.prototype) //{constructor:ƒ f1(),__proto__:Object}
 console.log(typeof f1.prototype) //Object
 console.log(typeof Function.prototype) // Function,这个特殊,由于Function是经过new Function建立的
 console.log(typeof Object.prototype) // Object
 console.log(typeof Function.prototype.prototype) //undefined

从console.log(f1.prototype) //{constructor:ƒ f1(),__proto__:Object}能够看出f1.prototype就是f1的一个实例对象,就是在建立f1的时候,建立了一个它的实例对象,并把它赋给了prototype原型对象。代码以下设计

const temp = new f1();
f1.prototype = temp;

那么看到这儿,你们确定会说,为何要有原型对象?这个原型对象有什么用?
刚开始我就提到了,继承里会用到。看下下面的代码:code

function Cat(name){
    this.name = name;
}
Cat.prototype.getName = function(){
    alert(this.name);
}
const qqq = new Cat('qqq');
qqq.getName();//qqq

从上面的代码中能够看出,经过给Cat的原型对象添加属性方法,那么Cat的实例都会拥有这个方法并能够调用之。有同窗可能会有疑问,为何在原型对象上添加了属性方法,它的实例就也能够拥有这个方法呢?这就牵扯到接下来讲到的原型链了。对象

原型链

首先,js的对象(普通对象和函数对象)都会有__proto__属性,指向建立它的构造函数的原型对象,好比上面的例子

qqq.__proto__ === Cat.prototype;//true
Cat.prototype.__proto__ === Object.prototype;//true
Object.prototype.__proto__//null

这就造成了原型链,会一直查找原型对象的__proto__属性,直到为null。
有几个比较特殊的例子,来看一下
1.Object.__proto__ === Function.prototype // true
Object是函数对象,是经过new Function()建立,因此Object.__proto__指向Function.prototype。
2.Function.__proto__ === Function.prototype // true
Function 也是对象函数,也是经过new Function()建立,因此Function.__proto__指向Function.prototype。
3.Function.prototype.__proto__ === Object.prototype //true
Function.prototype是个函数对象,理论上他的__proto__应该指向 Function.prototype,就是他本身,本身指向本身,没有意义。
JS一直强调万物皆对象,函数对象也是对象,给他认个祖宗,指向Object.prototype。Object.prototype.__proto__ === null,保证原型链可以正常结束。

constructor

constructor是这么定义的。
在 Javascript 语言中,constructor 属性是专门为 function 而设计的,它存在于每个 function 的prototype 属性中。这个 constructor 保存了指向 function 的一个引用。

Cat.prototype.constructor === Cat //true
 Function.prototype.constructor === Function //true
 Object.prototype.constructor === Object //true

这里也有要注意的
1.注意Object.constructor===Function;//true 自己Object就是Function函数构造出来的
2.如何查找一个对象的constructor,就是在该对象的原型链上寻找碰到的第一个constructor属性所指向的对象

总结

1.原型和原型链是实现继承的一种方式
2.原型链真正的继承是靠__proto__,而不是prototype,且看如下这个例子

var animal = function(name){
   this.name = name;
}
var cat = function(){};
animal.say = 'lalala';
cat.prototype = animal;
var ca = new cat();
console.log(cat.say);//undefined
console.log(ca.say);//lalala

从输出结果能够看出,虽然cat的prototype指向了animal,可是读取say属性的时候并不会根据prototype找,ca自己虽然也没有say属性,可是看下面这段代码

ca.__proto__ = cat.prototype
cat.prototype = animal

因此ca.say输出lalala
3.以前遗留的问题,关于兼容ie的__proto__
ie9有Object.getPrototypeof()方法

function a(){console.log("aaa")};
   const b = new a();
   Object.getPrototypeof(b) === b.__proto__//true

ie8不支持Object.getPrototypeof方法,能够结合constructor和prototype

function a(){console.log("aaa")};
   const b = new a();
   b.constructor.prototype === b.__proto__//true

最后再思考下new()过程都作了些什么?(好比new A())

  1. 建立新对象var obj = {};
  2. 将新对象的construct属性指向构造函数,__proto__属性指向构造函数的prototype
  3. 执行构造函数,A.call(obj),将this指向obj
  4. 返回新对象(注意:若是构造函数返回this,基本类型或者不返回,就是返回新对象,若是返回引用类型,就是返回引用类型)

好了,今天就先写这么多,明天结合今天的原型和原型链总结下继承以及每一个继承的优缺点~~~

参考资料

相关文章
相关标签/搜索