6.23随笔

原型对象

在JavaScript 中,每当定义一个对象(函数)时候,对象中都会包含一些预定义的属性。其中函数对象的一个属性就是原型对象 prototype。注:普通对象没有prototype,但有__proto__属性。

原型对象其实就是普通对象(Function.prototype除外,它是函数对象,但它很特殊,他没有prototype属性(前面说道函数对象都有prototype属性))。看下面的例子:

function f1(){};

console.log(f1.prototype) //f1{}

console.log(typeof f1. prototype) //Object

console.log(typeof Function.prototype) // Function,这个特殊

console.log(typeof Object.prototype) // Object

console.log(typeof Function.prototype.prototype) //undefined

从这句console.log(f1.prototype) //f1 {} 的输出就结果可以看出,f1.prototype就是f1的一个实例对象。就是在f1创建的时候,创建了一个它的实例对象并赋值给它的prototype,基本过程如下:

var temp = new f1();

f1. prototype = temp;

所以,Function.prototype为什么是函数对象就迎刃而解了,上文提到凡是new Function ()产生的对象都是函数对象,所以temp1是函数对象。

var temp1 = new Function ();

Function.prototype = temp1;

那原型对象是用来做什么的呢?主要作用是用于继承。举了例子:

var person = function(name){

this.name = name

};

person.prototype.getName = function(){

return this.name;

}

var zjh = new person(‘zhangjiahao’);

zjh.getName(); //zhangjiahao

从这个例子可以看出,通过给person.prototype设置了一个函数对象的属性,那有person实例(例中:zjh)出来的普通对象就继承了这个属性。具体是怎么实现的继承,就要讲到下面的原型链了。

原型链

JS在创建对象(不论是普通对象还是函数对象)的时候,都有一个叫做__proto__的内置属性,用于指向创建它的函数对象的原型对象prototype。以上面的例子为例:

console.log(zjh.__proto__ === person.prototype) //true

同样,person.prototype对象也有__proto__属性,它指向创建它的函数对象(Object)的prototype

console.log(person.prototype.__proto__ === Object.prototype) //true

继续,Object.prototype对象也有__proto__属性,但它比较特殊,为null

console.log(Object.prototype.__proto__) //null

我们把这个有__proto__串起来的直到Object.prototype.__proto__为null的链叫做原型链。如下图:

在开始摆弄代码之前,应该搞清楚使用继承的目的和能带来什么好处。一般来说,在设计类的时候,我们希望能减少重复性的代码,并且尽量弱化类之间的耦合。而要做到这两者都兼顾是很难的,我们需要根据具体的条件和环境下决定我们应该采取什么方法。根据我们对面向对象语言中继承的了解,继承会带类直接的强耦合,但js由于其特有的灵活性,可以设计出强耦合和弱耦合,高效率和低效率的代码。而具体用什么,看情况。

下面提供js实现继承的三种方法:类式继承,原型继承,掺元类。这里先简述类式继承,后两种在往后的随便中简述,请多多关注、指导,谢谢。

类式继承。

js类式继承的实现依靠原型链来实现的。什么是原型链?js中对象有个属性prototy,这个属性返回对象类型的引用,用于提供对象的类的一组基本功能。

貌似对prototype有印象,对了,我们经常这样用代码。

var Person = function(){

this.name = "liyatang";

};

Person.prototype = {

//可以在这里提供Person的基本功能

getName : function(){

return this.name;

}

}

我们把类的基本功能放在prototype属性里,表示Person这个对象的引用有XXX功能。

在理解原型后,需要理解下什么是原型链。在访问对象的某个成员(属性或方法)时,如果这个成员未见于当前对象,那么js会在prototype属性所指的那个对象中查找它,如果还没有找到,就继续到下一级的prototype所指的对象中查找,直至找到。如果没有找到就会返回undifined。

那么原型链给我们什么提示呢?很容易联想到,原型链意味着让一个类继承另一个类,只需将子类的prototype设置为指向父类的一个实例即可。这就把父类的成员绑定到子类上了,因为在子类上查找不到某个成员时会往父类中查找。(以上这两段用词不严谨,只在用通俗易懂的言语描述)

下面我们需要个Chinese类,需要继承Person类的name和getName成员。

var Chinese = function(name, nation){

//继承,需要调用父类的构造函数,可以用call调用,this指向Chinese

//使Person在此作用域上,才可以调用Person的成员

Person.call(this,name);

this.nation = nation;

};

Chinese.prototype = Person.prototype;

//这里不可和以前一样,因为覆盖掉了prototype属性

//Chinese.prototype = {

// getNation : function(){

// return this.nation;

// }

//};

//以后的方法都需要这样加

Chinese.prototype.getNation = function(){

return this.nation;

};

继承关系就建立了,我们这样调用它

var c = new Chinese("liyatang","China");

alert(c.getName());// liyatang

于是类式继承就这样完成了。难道真的完成了嘛,用firebug在alert那里设断点,会发现原来的Person.prototype被修改了,添加了getNation方法。

这是因为在上面的代码Chinese.prototype = Person.prototype; 这是引用类型,修改Chinese同时也修改了Person。这本身就是不能容忍的,且使类之间形成强耦合性,这不是我们要的效果。

我们可以另起一个对象或实例化一个实例来弱化耦合性。

//第一种

//Chinese.prototype = new Person();

//第二种

//var F = function(){};

//F.prototype = Person.prototype;

//Chinese.prototype = F.prototype;

这两种方法有什么区别呢。在第二种中添加了一个空函数F,这样做可以避免创建父类的一个实例,因为有可能父类会比较庞大,而且父类的构造函数会有一些副作用,或者说会执行大量的计算任务。所以力荐第二种方法。

到此,完了嘛,还没有!在对象的属性prototype下面有个属性constructor,它保存了对构造特定对象实例的函数的引用。根据这个说法Chiese.prototype.constructor应该等于Chinese,实际上不是。

回忆之前在设置Chiese的原型链时,我们把Person.prototype 覆盖掉了Chiese.prototype。所以此时的Chiese.prototype.constructor是Person。我们还需要添加以下代码

//对这里的if条件不需要细究,知道Chinese.prototype.constructor = Chinese就行

if(Chinese.prototype.constructor == Object.prototype.constructor){

Chinese.prototype.constructor = Chinese;

}

整理全部代码如下

var Person = function(name){

this.name = name;

};

Person.prototype = {

getName : function(){

return this.name;

}

};

var Chinese = function(name, nation){

Person.call(this,name);

this.nation = nation;

};

var F = function(){};

F.prototype = Person.prototype;

Chinese.prototype = F.prototype;

if(Chinese.prototype.constructor == Object.prototype.constructor){

Chinese.prototype.constructor = Chinese;

}

Chinese.prototype.getNation = function(){

return this.nation;

};

var c = new Chinese("liyatang","China");

alert(c.getName());

如果可以把继承的代码放在一个函数里,方便代码复用,最后整理代码如下

function extend(subClass,superClass){

var F = function(){};

F.prototype = superClass.prototype;

subClass.prototype = new F();

subClass.prototype.constructor = subClass;

subClass.superclass = superClass.prototype; //加多了个属性指向父类本身以便调用父类函数

if(superClass.prototype.constructor == Object.prototype.constructor){

superClass.prototype.constructor = superClass;

}

}

var Person = function(name){

this.name = name;

};

Person.prototype = {

getName : function(){

return this.name;

}

};

var Chinese = function(name, nation){

Person.call(this,name);

this.nation = nation;

};

extend(Chinese, Person);

Chinese.prototype.getNation = function(){

return this.nation;

};

var c = new Chinese("liyatang","China");

alert(c.getName());

我对那个extend函数又有新的看法。之前在讨论如何设置原型链时提出了两种方法

//第一种

//Chinese.prototype = new Person();

//第二种

//var F = function(){};

//F.prototype = Person.prototype;

//Chinese.prototype = F.prototype;

虽然第二种减少了调用父类的构造函数这条路,但在设计Chinese类时用了Person.call(this,name);这里也相当于调用了父类的构造函数。

然而用第一种方法的话可以减少在Chinese中再写Person.call(this,name);,这部分代码在子类中往往会遗忘。不妨把这种功能代码放在了extend里。就只写

Chinese.prototype = new Person();也达到同样的目的:耦合不强。

但遗忘的一点是,Chinese.prototype = new Person();这样写对嘛。答案是不对!很明显 new Person()需要传一个name参数的。我们不可能在extend函数里做这部分工作,只好在Chinese类里调用父类的构造函数了。这样也符合面向对象的思路。

在开始摆弄代码之前,应该搞清楚使用继承的目的和能带来什么好处。一般来说,在设计类的时候,我们希望能减少重复性的代码,并且尽量弱化类之间的耦合。而要做到这两者都兼顾是很难的,我们需要根据具体的条件和环境下决定我们应该采取什么方法。根据我们对面向对象语言中继承的了解,继承会带类直接的强耦合,但js由于其特有的灵活性,可以设计出强耦合和弱耦合,高效率和低效率的代码。而具体用什么,看情况。

下面提供js实现继承的三种方法:类式继承,原型继承,掺元类。这里先简述类式继承,后两种在往后的随便中简述,请多多关注、指导,谢谢。

类式继承。

js类式继承的实现依靠原型链来实现的。什么是原型链?js中对象有个属性prototy,这个属性返回对象类型的引用,用于提供对象的类的一组基本功能。

貌似对prototype有印象,对了,我们经常这样用代码。

var Person = function(){

this.name = "liyatang";

};

Person.prototype = {

//可以在这里提供Person的基本功能

getName : function(){

return this.name;

}

}

我们把类的基本功能放在prototype属性里,表示Person这个对象的引用有XXX功能。

在理解原型后,需要理解下什么是原型链。在访问对象的某个成员(属性或方法)时,如果这个成员未见于当前对象,那么js会在prototype属性所指的那个对象中查找它,如果还没有找到,就继续到下一级的prototype所指的对象中查找,直至找到。如果没有找到就会返回undifined。

那么原型链给我们什么提示呢?很容易联想到,原型链意味着让一个类继承另一个类,只需将子类的prototype设置为指向父类的一个实例即可。这就把父类的成员绑定到子类上了,因为在子类上查找不到某个成员时会往父类中查找。(以上这两段用词不严谨,只在用通俗易懂的言语描述)

下面我们需要个Chinese类,需要继承Person类的name和getName成员。

var Chinese = function(name, nation){

//继承,需要调用父类的构造函数,可以用call调用,this指向Chinese

//使Person在此作用域上,才可以调用Person的成员

Person.call(this,name);

this.nation = nation;

};

Chinese.prototype = Person.prototype;

//这里不可和以前一样,因为覆盖掉了prototype属性

//Chinese.prototype = {

// getNation : function(){

// return this.nation;

// }

//};

//以后的方法都需要这样加

Chinese.prototype.getNation = function(){

return this.nation;

};

继承关系就建立了,我们这样调用它

var c = new Chinese("liyatang","China");

alert(c.getName());// liyatang

于是类式继承就这样完成了。难道真的完成了嘛,用firebug在alert那里设断点,会发现原来的Person.prototype被修改了,添加了getNation方法。

?

这是因为在上面的代码Chinese.prototype = Person.prototype; 这是引用类型,修改Chinese同时也修改了Person。这本身就是不能容忍的,且使类之间形成强耦合性,这不是我们要的效果。

我们可以另起一个对象或实例化一个实例来弱化耦合性。

//第一种

//Chinese.prototype = new Person();

//第二种

//var F = function(){};

//F.prototype = Person.prototype;

//Chinese.prototype = F.prototype;

这两种方法有什么区别呢。在第二种中添加了一个空函数F,这样做可以避免创建父类的一个实例,因为有可能父类会比较庞大,而且父类的构造函数会有一些副作用,或者说会执行大量的计算任务。所以力荐第二种方法。

到此,完了嘛,还没有!在对象的属性prototype下面有个属性constructor,它保存了对构造特定对象实例的函数的引用。根据这个说法Chiese.prototype.constructor应该等于Chinese,实际上不是。

回忆之前在设置Chiese的原型链时,我们把Person.prototype 覆盖掉了Chiese.prototype。所以此时的Chiese.prototype.constructor是Person。我们还需要添加以下代码

//对这里的if条件不需要细究,知道Chinese.prototype.constructor = Chinese就行

if(Chinese.prototype.constructor == Object.prototype.constructor){

Chinese.prototype.constructor = Chinese;

}

整理全部代码如下

var Person = function(name){

this.name = name;

};

Person.prototype = {

getName : function(){

return this.name;

}

};

var Chinese = function(name, nation){

Person.call(this,name);

this.nation = nation;

};

var F = function(){};

F.prototype = Person.prototype;

Chinese.prototype = F.prototype;

if(Chinese.prototype.constructor == Object.prototype.constructor){

Chinese.prototype.constructor = Chinese;

}

Chinese.prototype.getNation = function(){

return this.nation;

};

var c = new Chinese("liyatang","China");

alert(c.getName());

如果可以把继承的代码放在一个函数里,方便代码调出

function extend(subClass,superClass){

var F = function(){};

F.prototype = superClass.prototype;

subClass.prototype = new F();

subClass.prototype.constructor = subClass;

subClass.superclass = superClass.prototype; //加多了个属性指向父类本身以便调用父类函数

if(superClass.prototype.constructor == Object.prototype.constructor){

superClass.prototype.constructor = superClass;

}

}

var Person = function(name){

this.name = name;

};

Person.prototype = {

getName : function(){

return this.name;

}

};

var Chinese = function(name, nation){

Person.call(this,name);

this.nation = nation;

};

extend(Chinese, Person);

Chinese.prototype.getNation = function(){

return this.nation;

};

var c = new Chinese("liyatang","China");

alert(c.getName());

发表后修改:

在一楼的评论下,我对那个extend函数又有新的看法。之前在讨论如何设置原型链时提出了两种方法

//第一种

//Chinese.prototype = new Person();

//第二种

//var F = function(){};

//F.prototype = Person.prototype;

//Chinese.prototype = F.prototype;

虽然第二种减少了调用父类的构造函数这条路,但在设计Chinese类时用了Person.call(this,name);这里也相当于调用了父类的构造函数。

然而用第一种方法的话可以减少在Chinese中再写Person.call(this,name);,这部分代码在子类中往往会遗忘。不妨把这种功能代码放在了extend里。就只写

Chinese.prototype = new Person();也达到同样的目的:耦合不强。

但遗忘的一点是,Chinese.prototype = new Person();这样写对嘛。答案是不对!很明显 new Person()需要传一个name参数的。我们不可能在extend函数里做这部分工作,只好在Chinese类里调用父类的构造函数了。这样也符合面向对象的思路。

时间: 2024-10-12 03:38:31

6.23随笔的相关文章

7月23随笔

Fdisk [-l]设备名称,-l:输出后面接的设备所有的分区内容.若仅有fdisk -l时,则系统将会把整个系统内能够找到的设备的分区均列出来. Df /找出磁盘文件名. 删除磁盘分区: Fdisk/dev/sdb:先进入fdisk界面. P:先看一下分区的信息 D:如果要选择一个分区,就选1. W(or) q:按w可存储到磁盘数据表中,并离开fdisk,如果反悔了,直接按q就可以取消刚才的删除操作了. 磁盘挂载与卸载: Mount -a:依照配置文件的数据将没有挂载的磁盘全都挂载上来 -l:

2016.6.23 随笔———— AJAX

1.什么是AJAX: AJAX 即Asynchronous JavaScript And XML(异步 JavaScript和XML), 创建快速动态网页技术 通过后台与服务器进行少量数据交换,AJAX可以使网页实现异步更新. 这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新. 传统的网页(不使用AJAX)如果需要更新内容,必须重载整个网页页面. 2. AJAX核心: 一种独立于WEB服务器软件的浏览器技术. 即是在浏览器(客户端)中,存在一个AJAX引擎对AJAX块的程序进行异

2018/3/23 随笔

今天午睡前想起个问题,每个人出生都像是坐在传送带上 似乎一切都是注定的. 80%的人 都选择跟着传送带走 或快或慢 这80%中的90%的人 想过去试试其他传送带 但因为自身原因和其他某些原因 他们放弃了  还是跟着属于自己的传送带走 联想到现在的自己  我在我的传送带上挣扎着 我是80%中的10% 我希望自己是那20%中的一个 这是我对我自己活着的定义 原文地址:https://www.cnblogs.com/antble/p/8630570.html

第十周PSP

PSP 日期 类别 内容 开始时间 结束时间 间隔时间 净时间 2016.11.17  --  --  --  --  --  -- 2016.11.18  --  --  --  --  --  -- 2016.11.19  测试  找bug  18:00  19:30  10  80 2016.11.20  学习  Python语法  18:00  19:00  0  60 2016.11.21  学习  设计模式原则  18:00  19:00  10  50 2016.11.22  随笔

团队作业4——第一次项目冲刺(Alpha版本) Day2

1.Day 2 站立式会议: 2.leangoo任务分截图: 3.会议记录及任务分配: 队员 今日进展 明日安排 林燕 完善逻辑架构框架,继续学习微信开发 完成4.24随笔 王李焕 初步总结微信开发的基本思想,协助完善逻辑架构框架 开始编写签到功能 林至贤 进一步完善数据库,完成4.23随笔 根据需求进一步完善数据库 童毅南 初步完成前端界面的的雏形 编写欢迎,登录界面 代泽旭 推进项目角色合理分配,整理记录,结合目前情况进行新一轮的人员安排 开始编写签到功能 会议总结:经过初步的学习,感觉这是

jmeter随笔(23)--在csv中维护变量参数

点击标题下「蓝色微信名」可快速关注 坚持的是分享,搬运的是知识,图的是大家的进步,没有收费的培训,没有虚度的吹水,喜欢就关注.转发(免费帮助更多伙伴)等来交流,想了解的知识请留言,给你带来更多价值,是我们期待的方向,有更多兴趣的欢迎切磋,我们微信订阅号,联系方式如下: 更多书籍,敬请期待 问题: 1.我的变量表多,通过之前的csv获取的方式,或者用户变量来维护,比较麻烦 2.我想在脚本之外维护我的变量数据,脱离脚本 解决方案: 1.csv的配置如图,队列是变量名称,第二列是变量的值,第一行是标题

2019-10-23刘宸瑞计算机英语课堂随笔

硬件 1主机 2中央处理器CPu 3运算器 4控制器 5寄存器 6内存 7随机存储器RAM 8只读存储器ROM 9外部设备 10输入设备 比如键盘 鼠标 光笔 扫描仪 11输出设备 比如显示器 打印机 绘圆仪 12外部存储设备 磁带 磁盘 光盘 13通信设备 网卡 14调制解调器 15软件系统 16操作系统 17程序设计语言 18应用软件 19各种应用程序包 20现代的社会是一个高速发展的社会,科技发达,信息流通,人们之间的交流越来越密切,生活也越来越方便,大数据就是这个高科技时代的产物. 21

yii2框架随笔23

今天继续阅读BaseYii.php <?php /** * Class autoload loader. * 自动装载类加载程序. * This method is invoked automatically when PHP sees an unknown class. * PHP用该方法自动调用一个未知类. * The method will attempt to include the class file according to the following procedure: * 方

2017/05/23 java 基础 随笔

1.多态的好处: a.提高了代码的维护性(继承保证) b.提高了代码的扩展性(由多态保证) package com.huawei; public class Demo2 { public static void main(String[] args) { creatMethod(new Cat()); } public static void creatMethod(Animal a){ if(a instanceof Cat){ Cat c=(Cat)a; c.catchrat(); }els