js 原型链的那些事儿

发布时间:2019-08-20 发布网站:脚本宝典
脚本宝典收集整理的这篇文章主要介绍了js 原型链的那些事儿脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。

前言

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

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

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

现在我们先来分析js中的对象。
js中对象是很重要的,所谓万物皆对象,但是js中对象分为两种,普通对象和函数对象

普通对象和函数对象

先来看几个例子

 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()创建的。

原型对象

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

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;

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

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,基本类型或者不返回,就是返回新对象,如果返回引用类型,就是返回引用类型)

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

参考资料

脚本宝典总结

以上是脚本宝典为你收集整理的js 原型链的那些事儿全部内容,希望文章能够帮你解决js 原型链的那些事儿所遇到的问题。

如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典推荐好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。