通过构造函数生成的实例化对象,无法共享属性或方法(即每个实例化对象上都有构造函数中的属性和方法);造成了一定的资源浪费
 function Obj(name,age){
this.name=name;
this.age=age;
this.func=function(){
return 'this is a test function';
};
}
var o1=new Obj('小明',10);
var o2=new Obj('小白',12);
console.log(o1.func===o2.func);

运行结果:

我们可以看出所有实例化对象(即o1,o2)中的func()都相同。但是每个对象都新建了func()方法,显得多余且浪费资源
 
为了解决上述问题,就要用到JavaScript的prototype对象:起到共享某些相同属性和方法的作用!
JavaScript的对象都继承自"原型"对象(与java、c++中类相似的作用);除了null,null没有自己的原型
JavaScript原型设计机制:原型上面的属性和方法,都能够被子对象共享
 function Obj1(name,age){
this.name=name;
this.age=age;
}
Obj1.prototype.address='beijing';//原型上的属性被所有实例化对象共享
Obj1.prototype.begin=function(){//原型上的方法被所有实例化对象共享
return 'this is a begin function';
};
var o3=new Obj1('晓明',100);
var o4=new Obj1('你猜',90);
console.log(o3,o4);
console.log(o3.address,o4.address);
console.log(o3.begin(),o4.begin());

运行结果:

 
但是实例化对象的属性或方法可能覆盖原型对象上的属性或方法
 o3.address='shanghai';
o3.begin=function(){
return 'this is a begin function for o3';
};
console.log(o3.address,o3.begin());

运行结果:

实际上所有函数都有自己的原型对象;因为函数在广义上可以认为是对象
对象能作为其他对象的原型对象,也能作为原型对象的实例化对象,由此形成了prototype chain原型链
所有的对象的原型对象如果一层层往上“回溯”,最后可以回溯到Object.prototype;而Object.prototype的原型对象是null,null没有自己的原型对象
 console.log(Object.getPrototypeOf(Object.prototype));

运行结果:

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

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

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

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

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

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

运行结果:

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

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

运行结果:

 
所以使用constructor属性,能确定对象使用的构造函数同时我们可以根据constructor属性,间接调用构造函数
 var a3=new a2.constructor();
console.log(a3.constructor===a1.constructor);//true

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

 function Abc(){};
Abc.prototype.constructor=Abc;
Abc.prototype.f=function(){
console.log('hello');
};
var a4=new Abc();
console.log(a4.constructor===Abc.prototype.constructor);
Abc.prototype={
f1:function(){
console.log('hi');
}
};//此时,定义Abc.prototype为一个新对象,原先定义在prototype上面的属性和方法均失效
//所以调用原先prototype的f方法,报错
var a5=new Abc();
//a5.f();//a5.f is not a function
//但是能调用新定义的属性或方法
a5.f1();//'hi'

运行结果:

name属性,可以查看构造函数的名字
 function Obj2(){};
var o7=new Obj2();
console.log(o7.constructor.name);//Obj2

运行结果:

总结起来:1. 构造函数生成对象;构造函数的原型(prototype)属性上面定义的方法或属性被所有实例化对象共享;构造函数的原型属性是实例对象的原型对象。
2.  constructor属性时定义在构造函数的prototype属性(原型对象),被所有实例化对象共享;所以实例化的对象能够直接调用constructor属性
3.  原型对象=构造函数的prototype属性

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

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

运行结果:

Object.getPrototypeOf():获取一个对象的原型对象
 //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():第一个参数是现有对象,第二个是原型对象;返回一个新对象

 var a={
name:'apple',
f:function(){
console.log('this is a test function');
}
};
var o9=Object.setPrototypeOf({},a);
//o9本身是空对象,它的原型对象是a;o9能调用a的属性和方法
console.log(o9);
console.log(o9.name);//apple
o9.f();

运行结果:

 
所以我们通过构造函数生成实例化对象本质其实就是将构造函数的property属性赋值给实例对象的原型对象
 function F(){};
var f1=new F();
console.log(f1);
/**
* 相当于
* var f1=Object.setPrototypeOf({},F.prototype);
* F.call(f1);//调用F函数,this指向f1
*/
Object.create()使用场景:
有时候,我们不能拿到对象的构造函数,只能取到实例对象;比如如下生成的:
 var o10={
name:'chrome',
speed:'fast'
};
o10是一个实例对象,但是并不是很容易找到它的构造函数
那么如何以o10为原型,另外生成一个实例对象?使用Object.create()
 var o11=Object.create(o10);
console.log(o11);
console.log(o11.name,o11.speed);
console.log(Object.getPrototypeOf(o11)===o10);//true;说明o11的原型对象等于o10

运行结果:

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

运行结果:

__proto__:内部属性,不应该对使用者显露;尽量减少使用这个属性
__pro__:设置对象的原型对象;其实一般都可以用Object.getPrototypeOf()和Object.setPrototypeOf()替代
 var o12={
name:'apple',
age:100
};
var o13={
sex:'male',
email:'qq@com'
};
o12.__proto__=o13;
console.log(Object.getPrototypeOf(o12)===o13);
//即__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标准参考教程

JavaScript OOP(三):prototype原型对象(即构造函数的prototype属性)的更多相关文章

  1. JavaScript学习笔记之原型对象

    本文是学习<JavaScript高级程序设计>第六章的笔记. JS中,便于批量创建对象的三种模式: 1.工厂模式:用一个函数封装创建对象的细节,传入必要的参数,在函数内部new一个对象并返 ...

  2. 理解js的prototype原型对象

    我们创建的每一个函数都有一个prototype(原型)属性.这个属性是一个指针,指向一个对象,而这个对象的用途是包括能够由特定类型的全部实例共享的属性和方法.假设依照字面意思来理解,那么prototy ...

  3. 原型和原型对象(__proto__和prototype)转

    看了之后我总算对原型继承有了更深刻的理解,做爱分享的姑娘,原文链接:理解Javascript 原型 我(个人)不喜欢的,就是讲原型时上来就拿类做比较的,所以我不会这样讲.不过我的确讲过构造器函数,在这 ...

  4. JavaScript 原型链学习(三)原型对象存在的问题 与 组合使用构造函数和原型

    原型对象也不是没有缺点.首先,它省略了为构造函数传递初始化参数这一环节, 结果所有实例在默认情况下都将取得相同的属性值.虽然这会在某种程度上带来一些不方便, 但还不是原型对象的最大问题.原型对象的最大 ...

  5. 简单理解javascript中的原型对象,实现对之间共享属性和行为

    javascript中提供了构造函数.可以方便的创建对象. 典型的构造函数例如以下: function Person(name, age) { this.name = name; this.age = ...

  6. JavaScript高级程序设计之原型对象

    构造函数.原型对象.构造器是一体的关系,同时产生: 实例中的隐藏属性__proto__指向原型对象: 原型对象是这四种关系的纽带. 原型对象是动态的,不论在何处变化,实例中可以立即体现出来. var ...

  7. javascript(三):对象

    对象(object)是javascript中很重要的数据类型.对象是“键值对”的集合,同时也是无序的.(注意:对象结尾处有分号) var ob1={ a1:'name',//a1可以加引号或者不加 a ...

  8. js之prototype 原型对象

    原型对象prototype可以这么理解,是该类的实例对象的模板,每个实例对象都是先复制一份该类的prototype,通过这个可以让类的实例拥有相同的功能   String.prototype.say= ...

  9. js学习(四)- prototype原型对象

    前言: 下面两行代码都是创建一个数组对象myArray:var myArray=[];//等价于var myArray=new Array();同样,下面的两段代码也都是创建一个函数myFunctio ...

随机推荐

  1. java笔记3(动手动脑)

    1.以下代码为何无法通过编译?哪儿出错了? 原因:已有的Foo()是带一个int型参数的构造方法,不存在无参的构造方法Foo() "构造方法" 当创建一个对象时,它的构造方法会被自 ...

  2. python自动化运维五:paramiko

    p { margin-bottom: 0.25cm; line-height: 120% } a:link { } paramiko是基于python实现的SSH2远程安全连接,支持认证以及密钥方式, ...

  3. App 组件化/模块化之路——使用SDK的思路进行模块化设计接口

    在不久之前分享一篇<App 组件化/模块化之路——如何封装网络请求框架>文章介绍了我在项目中封装网络请求框架的思路.开发一个 App 会涉及到很多网络请求 API ,例如登录注册接口.用户 ...

  4. Windows7 64位环境下Python-igraph环境配置

    本文为作者原创,转载请注明出处(http://www.cnblogs.com/mar-q/)by 负赑屃 其实配置很简单,但是网上的几个都不咋靠谱.而且我本身对这个软件挺感兴趣.igraph是一个网络 ...

  5. JS框架设计读书笔记之-选择器引擎02

    选择器引擎涉及相关概念 概念 以Sizzle的主函数声明为例,来说明引擎的相关概念. function Sizzle(selector, context, results, seed) { //... ...

  6. SE6 不定参数和默认参数详解和使用细节

    在SE5以前我们通常通过arguments类数组对象来引用不定形参,SE6则使用了一种叫做不定参数的写法,比起隐式的arguments要直观的多. 不定参数使用...参数名来指定一个不定参数,参数名指 ...

  7. 使用localstorage来存储页面信息

    今天小颖在跟着慕课网学习vue,不学不知道,一学吓一跳,学了才发现,我之前知道的只是vue的冰山一角,嘻嘻,今天把小颖跟着慕课网学习的demo,给大家分享下,希望对大家有所帮助嘻嘻. 环境搭建: 参考 ...

  8. 颜色框架Hue相关使用方法

    Hue地址 cocoapods安装Hue pod "Hue" 导入框架 import Hue 将十六进制数字变成对应的颜色值 let color = UIColor.init(he ...

  9. shell命令输入输出重定向

    Linux命令的执行过程 首先是输入:stdin输入可以从键盘,也可以从文件得到 命令执行完成:把成功结果输出到屏幕,stout默认是屏幕 命令执行有错误:把错误也输出到屏幕上面,stderr默认也是 ...

  10. Linux安装nginx代理服务器

    Nginx("engine x")是一款是由俄罗斯的程序设计师Igor Sysoev所开发高性能的 Web和 反向代理 服务器,也是一个 IMAP/POP3/SMTP 代理服务器. ...