资讯专栏INFORMATION COLUMN

js 原型链的那些事儿

fuyi501 / 3259人阅读

摘要:原型链首先,的对象普通对象和函数对象都会有属性,指向创建它的构造函数的原型对象,比如上面的例子这就形成了原型链,会一直查找原型对象的属性,直到为。,保证原型链能够正常结束。

前言

一般谈到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())

创建新对象var obj = {};

将新对象的construct属性指向构造函数,__proto__属性指向构造函数的prototype

执行构造函数,A.call(obj),将this指向obj

返回新对象(注意:如果构造函数返回this,基本类型或者不返回,就是返回新对象,如果返回引用类型,就是返回引用类型)

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

参考资料

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/108282.html

相关文章

  • JS基础】原型对象的那些事(二)

    摘要:只是构造函数上的一个属性,它是一个指针,指向原型对象,并不表示就是原型对象。在上一个例子中,就是一个对象,这个对象可以说是原生构造函数的实例,所以也是一个对象,所以它也有属性,不过它的指向也就是原型链的顶端,再往上就没有了。 上一篇讲了①原型对象是什么;②__proto__、prototype、constructor的关系;③原型对象的作用;④原型对象带来的一些需要注意的问题; 没理解...

    yanbingyun1990 评论0 收藏0
  • 2019前端面试那些事儿

    摘要:虽然今年没有换工作的打算但为了跟上时代的脚步还是忍不住整理了一份最新前端知识点知识点汇总新特性,语义化浏览器的标准模式和怪异模式和的区别使用的好处标签废弃的标签,和一些定位写法放置位置和原因什么是渐进式渲染模板语言原理盒模型,新特性,伪 虽然今年没有换工作的打算 但为了跟上时代的脚步 还是忍不住整理了一份最新前端知识点 知识点汇总1.HTMLHTML5新特性,语义化浏览器的标准模式和怪...

    JeOam 评论0 收藏0
  • 2019前端面试那些事儿

    摘要:虽然今年没有换工作的打算但为了跟上时代的脚步还是忍不住整理了一份最新前端知识点知识点汇总新特性,语义化浏览器的标准模式和怪异模式和的区别使用的好处标签废弃的标签,和一些定位写法放置位置和原因什么是渐进式渲染模板语言原理盒模型,新特性,伪 虽然今年没有换工作的打算 但为了跟上时代的脚步 还是忍不住整理了一份最新前端知识点 知识点汇总1.HTMLHTML5新特性,语义化浏览器的标准模式和怪...

    QLQ 评论0 收藏0
  • 漫谈 | 从姓名巫术说起,聊聊区块链匿名那些事儿

    摘要:无独有偶,在中国古代社会,也有姓名巫术这一说。从匿名到伪匿名此时,具有匿名性的区块链走入了人们的视野,除了去中心化安全性可追溯性等特征外,区块链被提及最多的就是其匿名性。因此,区块链的完全匿名或完全实名是不可能的。 从姓名巫术到匿名文化 姓名,是一个人最基础的社会符号,是人们相互了解的一个窗口。然而,在科技落后的古代,人们对名字被人知晓讳莫如深。因为他们认为,姓名与灵魂共生。 在印第安...

    Carl 评论0 收藏0
  • [聊一聊系列]聊一聊百度移动端首页前端速度那些事儿

    摘要:要快,但是我们的服务也必须万无一失,后续我会分享百度移动端首页的前端架构设计那么这样的优化,是如何做到的呢,又如何兼顾稳定性,架构性,与速度呢别急,让我们把这些优化一一道来。百度移动端首页的很多就是这样缓存在客户端的。 欢迎大家收看聊一聊系列,这一套系列文章,可以帮助前端工程师们了解前端的方方面面(不仅仅是代码):https://segmentfault.com/blog/fronte...

    The question 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<