专栏首页Micro_awake webJavaScript OOP(三):prototype原型对象(即构造函数的prototype属性)

JavaScript OOP(三):prototype原型对象(即构造函数的prototype属性)

通过构造函数生成的实例化对象,无法共享属性或方法(即每个实例化对象上都有构造函数中的属性和方法);造成了一定的资源浪费

 1 function Obj(name,age){
 2     this.name=name;
 3     this.age=age;
 4     this.func=function(){
 5         return 'this is a test function';
 6     };
 7 }
 8 var o1=new Obj('小明',10);
 9 var o2=new Obj('小白',12);
10 console.log(o1.func===o2.func);

运行结果:

我们可以看出所有实例化对象(即o1,o2)中的func()都相同。但是每个对象都新建了func()方法,显得多余且浪费资源

为了解决上述问题,就要用到JavaScript的prototype对象:起到共享某些相同属性和方法的作用!

JavaScript的对象都继承自"原型"对象(与java、c++中类相似的作用);除了null,null没有自己的原型

JavaScript原型设计机制:原型上面的属性和方法,都能够被子对象共享

 1 function Obj1(name,age){
 2     this.name=name;
 3     this.age=age;
 4 }
 5 Obj1.prototype.address='beijing';//原型上的属性被所有实例化对象共享
 6 Obj1.prototype.begin=function(){//原型上的方法被所有实例化对象共享
 7     return 'this is a begin function';
 8 };
 9 var o3=new Obj1('晓明',100);
10 var o4=new Obj1('你猜',90);
11 console.log(o3,o4);
12 console.log(o3.address,o4.address);
13 console.log(o3.begin(),o4.begin());

运行结果:

但是实例化对象的属性或方法可能覆盖原型对象上的属性或方法

1 o3.address='shanghai';
2 o3.begin=function(){
3     return 'this is a begin function for o3';
4 };
5 console.log(o3.address,o3.begin());

运行结果:

实际上所有函数都有自己的原型对象;因为函数在广义上可以认为是对象

对象能作为其他对象的原型对象,也能作为原型对象的实例化对象,由此形成了prototype chain原型链

所有的对象的原型对象如果一层层往上“回溯”,最后可以回溯到Object.prototype;而Object.prototype的原型对象是null,null没有自己的原型对象

1 console.log(Object.getPrototypeOf(Object.prototype));

运行结果:

表明Object.prototype的原型对象是null

如果尝试获取null或undefined的原型对象

1 console.log(Object.getPrototypeOf(null));//报错 Cannot convert undefined or null to object

注意:读取对象的某个属性时,js引擎会先在对象本身属性上寻找,如果找不到,那么去原型对象上找,一层一层往上"回溯",直至null.所以如果找寻某个不存在的属性,会将完整的原型链遍历一变,对性能影响较大。

constructor属性prototype原型对象有一个constructor属性,默认指向prototype所在的构造函数

1 var O5=function(){};
2 O5.prototype=new Array();//O5的prototype属性等于new Array();那么O5的实例化对象共享着Array对象的所有属性和方法,并且O5.prototype的constructor也与Array.prototype的constructor一样
3 // O5.prototype.constructor=O5;
4 var o6=new O5();
5 o6.push(1,2,3);
6 console.log(o6);
7 console.log(O5.prototype.constructor===Array.prototype.constructor);//true

运行结果:

 因为constructor属性定义在prototype上,所以所有实例对象都能访问

1 var a1=new O6();
2 var a2=new O6();
3 console.log(a1.constructor===a2.constructor && a1.constructor===O6.prototype.constructor);

运行结果:

所以使用constructor属性,能确定对象使用的构造函数同时我们可以根据constructor属性,间接调用构造函数

1 var a3=new a2.constructor();
2 console.log(a3.constructor===a1.constructor);//true

注意原型对象被覆盖可能出现的问题:

 1 function Abc(){};
 2 Abc.prototype.constructor=Abc;
 3 Abc.prototype.f=function(){
 4     console.log('hello');
 5 };
 6 var a4=new Abc();
 7 console.log(a4.constructor===Abc.prototype.constructor);
 8 Abc.prototype={
 9     f1:function(){
10         console.log('hi');
11     }
12 };//此时,定义Abc.prototype为一个新对象,原先定义在prototype上面的属性和方法均失效
13 //所以调用原先prototype的f方法,报错
14 var a5=new Abc();
15 //a5.f();//a5.f is not a function
16 //但是能调用新定义的属性或方法
17 a5.f1();//'hi'

运行结果:

name属性,可以查看构造函数的名字

1 function Obj2(){};
2 var o7=new Obj2();
3 console.log(o7.constructor.name);//Obj2

运行结果:

总结起来:1. 构造函数生成对象;构造函数的原型(prototype)属性上面定义的方法或属性被所有实例化对象共享;构造函数的原型属性是实例对象的原型对象。

2.  constructor属性时定义在构造函数的prototype属性(原型对象),被所有实例化对象共享;所以实例化的对象能够直接调用constructor属性

3.  原型对象=构造函数的prototype属性

instanceof 关键字判断对象是否为构造函数的实例

 1 function Obj3(){};
 2 var o8=new Obj3();
 3 console.log(o8 instanceof Obj3);
 4 console.log(o8 instanceof Object);
 5 //instanceof对整个原型链上对象均有效
 6 var n1=new Number(10);//var n1=new Object(10);
 7 console.log(n1 instanceof Number,n1 instanceof Object);//true true
 8 
 9 // o8 instanceof Obj3等同于Obj3.prototype.isPrototypeOf
10 console.log(Obj3.prototype.isPrototypeOf(o8));//true
11 //可以翻译成Obj3构造函数的prototype(原型)属性是o8的原型对象
12 
13 //null,undefined instanceof Object均为false

运行结果:

Object.getPrototypeOf():获取一个对象的原型对象

1 //console.log(Object.getPrototypeOf(null),Object.getPrototypeOf(undefined));//Cannot convert undefined or null to object

不能获取null或者undefined的原型对象

function test1(){}
console.log(Object.getPrototypeOf(test1));
console.log(Object.getPrototypeOf(test1)===Function.prototype)
function Test(){}
var t1=new Test();
console.log(Object.getPrototypeOf(t1));
console.log(Object.getPrototypeOf(t1)===Test.prototype);

运行结果:

 Object.setPrototypeOf():第一个参数是现有对象,第二个是原型对象;返回一个新对象

 1 var a={
 2     name:'apple',
 3     f:function(){
 4         console.log('this is a test function');
 5     }
 6 };
 7 var o9=Object.setPrototypeOf({},a);
 8 //o9本身是空对象,它的原型对象是a;o9能调用a的属性和方法
 9 console.log(o9);
10 console.log(o9.name);//apple
11 o9.f();

运行结果:

所以我们通过构造函数生成实例化对象本质其实就是将构造函数的property属性赋值给实例对象的原型对

1 function F(){};
2 var f1=new F();
3 console.log(f1);
4 /**
5  * 相当于
6  * var f1=Object.setPrototypeOf({},F.prototype);
7  * F.call(f1);//调用F函数,this指向f1
8  */

Object.create()使用场景:

有时候,我们不能拿到对象的构造函数,只能取到实例对象;比如如下生成的:

1 var o10={
2     name:'chrome',
3     speed:'fast'
4 };

o10是一个实例对象,但是并不是很容易找到它的构造函数

那么如何以o10为原型,另外生成一个实例对象?使用Object.create()

1 var o11=Object.create(o10);
2 console.log(o11);
3 console.log(o11.name,o11.speed);
4 console.log(Object.getPrototypeOf(o11)===o10);//true;说明o11的原型对象等于o10

运行结果:

Object.prototype.isPrototypeOf:判断是否是某个对象的原型对象

1 console.log(o10.isPrototypeOf(o11));//true;o10是o11的原型对象
2 console.log(o11.isPrototypeOf(o10));
3 console.log(Object.prototype.isPrototypeOf(null));//除了null或者Object.create(null)生成对象,其它对象的原型对象往原型链回溯一定可以回溯到Object.prototype
4 console.log(Object.prototype.isPrototypeOf(Array));

运行结果:

__proto__:内部属性,不应该对使用者显露;尽量减少使用这个属性

__pro__:设置对象的原型对象;其实一般都可以用Object.getPrototypeOf()和Object.setPrototypeOf()替代

 1 var o12={
 2     name:'apple',
 3     age:100
 4 };
 5 var o13={
 6     sex:'male',
 7     email:'qq@com'
 8 };
 9 o12.__proto__=o13;
10 console.log(Object.getPrototypeOf(o12)===o13);
11 //即__pro__是指定对象的原型对象,也就是构造函数的prototype属性

运行结果:

 总结:

  1. JavaScript的继承机制主要是基于prototype的。
  2. 构造函数生成实例化对象;构造函数的prototype属性就是实例化对象的原型对象;原型对象上的属性和方法被所有实例化对象所共享!
  3.  原型链一般往上回溯可以回溯到Object.prototype;Object.prototype的原型对象是null;而null的原型对象不存在!
  4. 原型对象上有construtor属性,等于构造函数名;因为是定义在原型对象上,所以被所有实例对象共享(由此我们也可以间接调用构造函数生成实例对象)!
  5. Object.getPrototypeOf():获取某个对象的原型;不能获取null或者undefined的原型
  6. Object.setPrototypeOf():第一个参数是现有对象,第二个是原型对象;返回一个新对象
  7. Object.create():以参数为原型对象生成新对象
  8. __proto__属性:设置对象的原型对象;尽量减少使用该属性
  9. instanceof:判断对象是否是某构造函数的实例对象

自己用Excel画的一张图:

 参考:阮一峰JavaScript标准参考教程

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 重温CSS3

    基础不牢,地动山摇!没办法,只能重温“经典”! 1.CSS3边框:border-radius;  box-shadow;  border-image borde...

    用户1149564
  • VSCode配置eslint

    在Vue.js项目中,使用的是eslint检查。 而在我写完代码后,cnpm run dev运行命令。。。然后悲剧了,一大堆报错!╮(╯▽╰)╭ 安装插件:Ve...

    用户1149564
  • 说说html 的<!DOCTYPE>声明&标准模式与兼容模式

    我们都知道<!DOCTYPE>声明位于文档的最前面,处于<html>标签之前。 <!DOCTYPE>声明不是html标签,它的作用:告知web浏览界面应该使用哪...

    用户1149564
  • 前端入门15-JavaScript进阶之原型链声明正文-原型链

    作为一个前端小白,入门跟着这几个来源学习,感谢作者的分享,在其基础上,通过自己的理解,梳理出的知识点,或许有遗漏,或许有些理解是错误的,如有发现,欢迎指点下。

    请叫我大苏
  • xfs vs ext4 性能压测对比

    最近忙着给YOUZAN的数据库服务器升级系统版本,从centos6 升级到centos7。centos/redhat 7 默认将文件系统设置为xfs。咨询了很多...

    用户1278550
  • iBatis.Net(5):Data Map(了解)

    总算,总算,能写点示例啦,呵呵,其实前面的几篇,我感觉自己写的也很生硬,没有Demo理解起来是很困难,很多名词,反正我初次接触iBatis的时候,是一点也不理解...

    小白哥哥
  • 管理后台原型设计分享- 政务管理系统

    现如今,越来越多的团队开始注重对自动化系统的使用,ERP系统(企业资源计划系统)应运而生。ERP系统是针对物资资源管理(物流)、人力资源管理(人流)、财务资源管...

    奔跑的小鹿
  • 【DDD】持久化领域对象的方法实践

    在实践领域驱动设计(DDD)的过程中,我们会根据项目的所在领域以及需求情况捕获出一定数量的领域对象。设计得足够好的领域对象便于我们更加透彻的理解业务,方便系统后...

    句幽
  • 零基础学编程035:群发邮件并不难

    我是GTD的重度用户,GTD中讲究将所有事情先收集起来再说,所以收集操作越快越好,这样才不至于把手边的工作打断。很多老牌的GTD工具软件支持发邮件实现快速收集,...

    申龙斌
  • “hdfs dfs -ls”命令的使用

    “hdfs dfs -ls”带一个参数,如果参数以“hdfs://URI”打头表示访问HDFS,否则相当于ls。 其中URI为NameNode的IP或主机...

    一见

扫码关注云+社区

领取腾讯云代金券