第5章 原型

5.1 原型属性(所有函数拥有一个prototype属性,默认为空对象)

  5.1.1 利用原型添加方法和属性

function Gadget(name,color){
this.name=name;
this.color=color;
this.whatAreYou=function(){return 'I am a'+this.color+' '+this.name;}
}
//方案一,对象属性方法追加
Gadget.prototype.price=100;//属性
Gadget.prototype.rating=3;//属性
Gadget.prototype.getInfo=function(){return 'Rating'+this.rating+', price:'+this.price}//方法
//方案二,对象字面量赋值
Gadget.prototype={
price:100,
rating:3,
getInfo:function(){return 'Rating'+this.rating+', price:'+this.price}
}

  5.1.2 使用原型的方法与属性(由于js对象为引用,所以基于原型创建的所有对象(包括修改原型之前创建的)都会继承原型的改变)

  5.1.3 自身属性和原型属性(优先查找自身属性,若没有再逐层向上级原型中查找)

  5.1.4 利用自身属性重写原型属性(自身属性会覆盖原型属性,当删除自身属性后,同名原型属性将浮现,hasOwnProperty()是否自身属性)

  5.1.5 isPrototypeOf()方法(o1.isPrototypeOf(o2),o1在o2的原型链上返回true,即o2继承o1)

  5.1.6 神秘的__proto__链接(IE不兼容,仅调试使用,与prototype不完全等价)

5.2 扩展内建对象

String.prototype.reverse=function(){//为字符串原型扩展翻转方法
return Array.prototype.reverse.apply(this.split('')).join('')
}//先利用split将目标字符转换为数组,再利用数组方法翻转,最后通过join再链接为字符串

  5.2.1 关于扩展内建对象的讨论(prototype利用扩展,YUI则反对;想通过原型为内建对象添加新属性,务必先检查该属性是否已存在)

  5.2.2 一些原型陷阱(完全替换原型对象可能触发异常,constructor属性不可靠;重写对象的prototype时要重置constructor)

5.3 本章小结

5.4 练习题

1、创建一个名为shape的对象,并为该对象设置一个type属性和一个getType()方法

2、定义一个原型为shape的Triange()的构造器函数,用Triange()创建的对象应该具有三个对象属性a、b、c,分别表示三角形的三条边。

3、在对象原型中添加一个名为getPerimeter()的新方法

4、使用下面代码来测试之前的实现:

var shape={
type:'triangle',
getType:function(){return this.type}
}
var Triangle= function(a,b,c){this.a=a;this.b=b;this.c=c;}
Triangle.prototype=shape;
Triangle.prototype.constructor=Triangle;
Triangle.prototype.getPerimeter=function(){return this.a+this.b+this.c}
var t= new Triangle(1,2,3)
console.log(t.constructor)//function (a,b,c){this.a=a;this.b=b;this.c=c;}
console.log(shape.isPrototypeOf(t))//true
console.log(t.getPerimeter())//
console.log(t.getType())//triangle

5、用循环遍历对象t,列出其所有的属性和方法(不含原型部分)。

for(var prop in t){
if(t.hasOwnProperty(prop)){console.log(prop+':'+t[prop]);}
}

6、修改上面的实现,使其能在下面的代码中正常工作

Array.prototype.shuffle=function(){
var arr=[],l=this.length;
for(var i=0;i<l;i++){arr.push(this.splice(Math.floor(Math.random()*this.length),1)[0])}
return arr;
}
console.log([1,2,3,4,5,6,7,8,9].shuffle());//[4, 9, 3, 2, 1, 6, 7, 8, 5]

第6章 继承

6.1 原型链

  6.1.1 原型链示例

function Shape(){
this.name='shape';
this.toString=function(){return this.name;}
}
function TwoDShape(){
this.name='2D shape';
}
function Triangle(side,height){
this.name='triangle';
this.side=side;
this.height=height;
this.getArea=function(){return this.side*this.height/2}
}//接下来就是施展继承
TwoDShape.prototype=new Shape();//用new另建了一个新的对象实体,并赋值覆盖该对象的原型
Triangle.prototype=new TwoDShape();//这样确保继承实现后,对构造器进行修改不会影响该对象,因为继承的是构造器所建的实体
//重写prototype后重置constructor是个好习惯
TwoDShape.prototype.constranctor=TwoDShape;
Triangle.prototype.constranctor=Triangle;
var my=new Triangle(5,10)
console.log(my.getArea());//
console.log(my.toString());//继承的方法,具体步骤(遍历my对象属性没有找到,接着查看my.__proto__所指向的对象,即new TwoDShape()创建的实体,
//依然没找到,又继续查找该实体的__proto__所指向的对象,即new Shape()所创建的实体,找到toString方法,并在my对象中被调用,this指向my)
//通过instanceof操作符,验证my对象同时是上述三个构造器的实例
console.log(my instanceof Shape)//true
console.log(my instanceof TwoDShape)//true
console.log(my instanceof Triangle)//true

  6.1.2 将共享属性迁移到原型中去(必须在扩展原型对象之前完成继承关系的构建)

function Shape(){this.name='shape'}//使用new Shape()新建对象,每个实体都有全新的那么属性并占用独立空间
function Shape(){};Shape.prototype.name='shape';//属性移到原型后,使用new新建对象时,不再含自己独立的这个属性

6.2 只继承于原型

Triangle.prototype=Shape.prototype;//减少继承方法的查询步骤
Triangle.prototype.name='Triangle';//修改子对象原型后父对象原型也随即被改,即再new Shape()新建对象时,新对象name为‘Triangle’
//利用临时构造器new F(),解决修改子对象原型父对象也随之修改的问题
function Shape(){}
Shape.prototype.name='shape';
Shape.prototype.toString=function(){return this.name;}
function TwoDShape(){}
var F=function(){}
F.prototype=Shape.prototype;
TwoDShape.prototype=new F();
TwoDShape.prototype.constructor=
TwoDShape;
TwoDShape.prototype.name='2D shape';
function Triangle(side,height){
this.side=side;
this.height=height;
}
var F=function(){}
F.prototype=TwoDShape.prototype;
Triangle.prototype=new F();
Triangle.prototype.constructor=
Triangle;
Triangle.prototype.name='triangle';
Triangle.prototype.getArea=function(){return this.side*this.height/2};
my.__proto__.__proto__.__proto__.constructor;//Shape()

6.3 uber--子对象访问父对象的方式(uber是德语中super的同义词)

function Shape(){}
Shape.prototype.name='shape';
Shape.prototype.toString=function(){
var result=[];
if(this.constructor.uber){result[result.length]=this.constructor.uber.toString()}
result[result.length]=this.name;
return result.join(',');
}
function TwoDShape(){}
var F=function(){}
F.prototype=Shape.prototype;
TwoDShape.prototype=new F();
TwoDShape.prototype.constructor=TwoDShape;
TwoDShape.uber=Shape.prototype;
TwoDShape.prototype.name='2D shape';
function Triangle(side,height){
this.side=side;
this.height=height;
}
var F=function(){}
F.prototype=TwoDShape.prototype;
Triangle.prototype=new F();
Triangle.prototype.constructor=Triangle;
Triangle.uber=TwoDShape.prototype;
Triangle.prototype.name='triangle';
Triangle.prototype.getArea=function(){return this.side*this.height/2};
var my=new Triangle(5,10)
console.log(my.toString());//shape,2D shape,triangle

6.4 将继承部分封装成函数

function extend(child,parent){
var F=function(){};
F.prototype=parent.prototype;
child.prototype=new F();
child.prototype.constructor=child;
child.uber=parent.prototype;
}
extend(TwoDShape,Shape);
extend(Triangle,TwoDShape);

6.5 属性拷贝

function extend2(child,parent){
var p=parent.prototype;
var c=child.prototype;
for(var i in p){c[i]=p[i]};
c.uber=p;
}//与之前的extend方法相比,extend2为逐一拷贝,而非原型链查找,同时生成自己的属性,但对于非基本数据类型不可复制,只是拷贝引用而已

6.6 小心处理引用拷贝

var A=function(){},B=function(){};
A.prototype.stuff=[1,2,3];
A.prototype.name='a';
extend2(B,A);//让B继承A使用方法二
B.prototype.name+='b';//ab,A.prototype.name依然为a,因为拷贝的是值
B.prototype.stuff.push(4);//此时A和B原型上的stuff同时被修改,因为拷贝的是应用
B.prototype.stuff=['a','b','c']//如果完全重写事情就不一样了,A为原来,B为新的

6.7 对象之间的继承(不用构造器直接复制)

function extendcopy(p){
var c={};
for(var i in p){c[i]=p[i]}
c.uber=p;
return c;
}

6.8 深拷贝(当遇到对象类型时,再次调用拷贝)

function deepcopy(p,c){
var c=c||{};
for(var i in p){
if(typeof p[i]==='object'){
c[i]=(p[i].constructor===Array)?[]:{};
deepcopy(p[i],c[i])
}else{c[i]=p[i]}
};
return c;
}

6.9 object()(用object函数来接受父对象,并返回一个以该对象为原型的新对象)

function object(o){
var n;
function F(){}
F.prototype=o;
n=new F();
n.uber=o;
return n;
}//这个函数与extendcopy基本相同

6.10 原型继承与属性拷贝的混合应用

function objectplus(o,stuff){
var n;
function F(){}
F.prototype=o;
n=new F();
n.uber=o;
for(var i in stuff){n[i]=stuff[i]}
return n;
}//两对象o用于继承,stuff用于拷贝方法与属性

6.11 多重继承(一个对象中有不止一个父对象的继承)

function multi(){
var n={},stuff,j=0,len=arguments.length;
for(j=0;i<len;j++){
stuff=arguments[j];
for(var i in stuff){n[i]=stuff[i]}
}
return n;
}//内层循环用于拷贝属性,外层循环用于遍历多个父对象参数,若有相同属性后面替代之前

6.12 寄生式继承(拷贝一个父对象为that,然后为that添加更多属性)

var twoD={name:'2D shape',dimensions:2}
function triangle(s,h){
var that=object(twoD);//应用上面的object()拷贝
that.name='Triangle';
that.getArea=function(){return this.side*this.height/2}
that.side=s;
that.height=h;
return that;
}//由于triangle()是一个返回对象的函数,不属于构造器,所以用不用new都可以
var t=triangle(5,10);console.log(t.getArea());
var t2=new triangle(5,5);console.log(t2.getArea());

6.13 构造器借用(指对象构造器通过call或apply来调用父对象的构造器)

function Shape(id){this.id=id}
Shape.prototype.name='shape';
Shape.prototype.toString=function(){return this.name};
function Triangle(){Shape.apply(this,arguments)}
Triangle.prototype.name='triangle';
var t=new Triangle(101);
console.log(t.id)//
t.toString()//"[object Object]",之所以不包含Shape的原型属性,是因为没用调用new Shape创建实例,在创建t之前Triangle.prototype=new Shape()即可;

6.14 本章小结

6.15 案例学习:图形绘制

function Point(x,y){this.x=x;this.y=y;}
function Line(p1,p2){
this.p1=p1;this.p2=p2;
this.length=Math.sqrt(Math.pow(p1.x-p2.x,2)+Math.pow(p1.y-p2.y,2));
}
function Shape(){this.points=[];this.lines=[];this.init();}
Shape.prototype={
constructor:Shape,
init:function(){//初始化
if(typeof this.context==='undefined'){
var canvas=document.getElementById('canvas');
Shape.prototype.context=canvas.getContext('2d');
}
},
draw:function(){//画线
var ctx=this.context;
ctx.strokeStyle=this.getColor();
ctx.beginPath();
ctx.moveTo(this.points[0].x,this.points[0].y);
for(var i=1;i<this.points.length;i++){ctx.lineTo(this.points[i].x,this.points[i].y)}
ctx.closePath();
ctx.stroke();
},
getColor:function(){//随机获取色值
var rgb=[];
for(var i=0;i<3;i++){rgb[i]=Math.round(255*Math.random())};
return 'rgb('+rgb.join(',')+')'
},
getLines:function(){//获取新增包含的线条
if(this.lines.length>0){return this.lines;}
var lines=[];
for(var i=0;i<this.points.length;i++){lines[i]=new Line(this.points[i],(this.points[i+1])?this.points[i+1]:this.points[0])}
this.lines=lines;return lines;
},
getArea:function(){},//获取面积,需子对象自建覆盖
getPerimeter:function(){//获取周长
var perim=0,lines=this.getLines();
for(var i=0;i<lines.length;i++){perim+=lines[i].length}
return perim;
}
}
function Triangle(a,b,c){
this.points=[a,b,c];
this.getArea=function(){//海伦公式Area=p(p-a)(p-b)(p-c),p为半周长
var p=this.getPerimeter()/2;
return Math.sqrt(p*(p-this.lines[0].length)*(p-this.lines[1].length)*(p-this.lines[2].length))
}
}
function Rectangle(p,side_a,side_b){
this.points=[p,new Point(p.x+side_a,p.y),new Point(p.x+side_a,p.y+side_b),new Point(p.x,p.y+side_b)];
this.getArea=function(){return side_a*side_b;}
}
function Square(p,side){Rectangle.call(this,p,side,side)}
//以上所有构造器已经实现完成,接下来给它们制作继承关系
(function(){
var s=new Shape();
Triangle.prototype=s;
Rectangle.prototype=s;
Square.prototype=s;
})()
//以下测试
var t=new Triangle(new Point(100,100),new Point(300,100),new Point(200,0));
t.draw();
console.log(t.getPerimeter())//482.842712474619
console.log(t.getArea())//10000.000000000002
var r=new Rectangle(new Point(200,200),50,100)
r.draw()
console.log(r.getArea())//
console.log(r.getPerimeter())//
var s=new Square(new Point(130,130),50)
s.draw()
console.log(s.getArea())//
console.log(s.getPerimeter())//
new Square(new Point(100,100),200).draw()

6.16 练习题

利用上面的画布示例,尝试如下:

1、绘制一些Triangle、Sqiare、Rectangle图形(略)

2、天假更多图形构造器,例如Trapezoid、Rhombus、Kite、Diamond以及Pentagon等,如果还想对canvas标签有更多了解,也可以创建一个Circle构造器,重写父对象的draw方法。

function Trapezoid(p1,p2,side1,side2){//梯形
this.points=[p1,new Point(p1.x+side1,p1.y),new Point(p2.x+side2,p2.y),p2]
this.getArea=function(){return (side1+side2)*Math.abs(p2.y-p1.y)/2}
}
Trapezoid.prototype=new Shape();
var tt=new Trapezoid(new Point(0,50),new Point(25,0),100,50)
tt.draw()
console.log(tt.getArea())//
console.log(tt.getPerimeter())//261.8033988749895 function Kite(p,d,b){//筝形
this.points=[p,new Point(p.x+d/2,p.y-d*b),new Point(p.x+d,p.y),new Point(p.x+d/2,p.y+d*(1-b))]
this.getArea=function(){ return d*d/2;}
}
Kite.prototype=new Shape();
var ki=new Kite(new Point(0,100),60,1/3)
ki.draw()
console.log(ki.getArea())//
console.log(ki.getPerimeter())//172.11102550927978 function Rhombus(p,side){//菱形
Kite.call(this,p,side,1/2)//构造器借用
}
Rhombus.prototype=new Shape();
var rh=new Rhombus(new Point(0,200),60)
rh.draw()
console.log(rh.getArea())//
console.log(rh.getPerimeter())//169.7056274847714 function Diamond(p1,p2,side1,side2){//钻石形
Trapezoid.call(this,p1,p2,side1,side2)
this.points.splice(1,0,new Point(p1.x+side1/2,p1.y+side1/2*Math.tan(45*Math.PI/180)))
}
Diamond.prototype=new Shape();
var di=new Diamond(new Point(0,300),new Point(20,270),80,40)
di.draw() function Pentagon(p1,side){//正五边形
Trapezoid.call(this,p1,new Point(p1.x+side*Math.cos(72*Math.PI/180),p1.y-side*Math.sin(72*Math.PI/180)),side+side*Math.cos(72*Math.PI/180)*2,side)
this.points.splice(1,0,new Point(p1.x+(side+side*Math.cos(72*Math.PI/180)*2)/2,p1.y+(side+side*Math.cos(72*Math.PI/180)*2)/2*Math.tan(36*Math.PI/180)))
}
Pentagon.prototype=new Shape();
var pe=new Pentagon(new Point(0,450),80)
pe.draw() function Circle(p1,r){//圆形
this.draw=function(){
var ctx=this.context;
ctx.strokeStyle=this.getColor();
ctx.beginPath();
ctx.arc(p1.x,p1.y,r,0,2*Math.PI);
ctx.closePath();
ctx.stroke();
}
}
Circle.prototype=new Shape();
var ci=new Circle(new Point(200,400),60)
ci.draw()

3、考虑是否还有其他方式可以实现并使用这些类型的继承关系?

4、请选择一个子对象能通过uber属性访问的方法,并为其添加新的功能,使得父对象可以追踪该方法所属的指对象。例如我们可以在父对象中建立一个用于存储其子对象的数组属性。

读书笔记-JavaScript面向对象编程(二)的更多相关文章

  1. 读书笔记-JavaScript面向对象编程(一)

    PDF下载链接: http://pan.baidu.com/s/1eSDSTVW 密码: 75jr 第1章 引言 1.1 回顾历史 1.2 变革之风 1.3 分析现状 1.4 展望未来 1.5 面向对 ...

  2. 读书笔记-JavaScript面向对象编程(三)

    第7章 浏览器环境 7.1 在HTML页面中引入JavaScript代码 7.2概述BOM与DOM(页面以外事物对象和当前页面对象) 7.3 BOM 7.3.1 window对象再探(所以JavaSc ...

  3. Javascript高级程序设计--读书笔记之面向对象(二)

    前面讲了面向对象的封装,这章我们就来说一说继承 1.原型链 实现原型链有一种基本模式,其代码大概如下 <script> function SuperType(){ this.propert ...

  4. Java Script 读书笔记 (四) 面向对象编程

    1. 对象,属性 前面看到对象里删除属性一直疑惑,什么是对象,为什么属性可以删除, 我印象里的属性还是停留在property, 总想不明白为什么属性竟然能够删除.直到看到标准库才明白,原来对象就是py ...

  5. 《JavaScript面向对象编程指南(第2版)》读书笔记(二)

    <JavaScript面向对象编程指南(第2版)>读书笔记(一) <JavaScript面向对象编程指南(第2版)>读书笔记(二) 目录 一.基本类型 1.1 字符串 1.2 ...

  6. 《JavaScript面向对象编程指南(第2版)》读书笔记(一)

    目录 一.对象 1.1 获取属性值的方式 1.2 获取动态生成的属性的值 二.数组 2.1 检测是否为数组 2.2 增加数组长度导致未赋值的位置为undefined 2.3 用闭包实现简易迭代器 三. ...

  7. 《JavaScript面向对象编程指南》读书笔记②

    概述 <JavaScript面向对象编程指南>读书笔记① 这里只记录一下我看JavaScript面向对象编程指南记录下的一些东西.那些简单的知识我没有记录,我只记录几个容易遗漏的或者精彩的 ...

  8. 《JavaScript面向对象编程指南》读书笔记①

    概述 JavaScript快忘完了,想看一本专业书拾遗,所以看了这本<JavaScript面向对象编程指南>. 个人觉得这本书讲的很透彻很易懂,一些原来有疑惑的地方在这本书里面豁然开朗,看 ...

  9. JavaScript面向对象编程学习笔记

    1  Javascript 面向对象编程 所谓"构造函数",其实就是一个普通函数,但是内部使用了this变量.对构造函数使用new运算符,就能生成实例,并且this变量会绑定在实例 ...

随机推荐

  1. Bellman_Ford算法(求一个点到任意一点的最短距离)

    单源最短路问题是固定一个起点,求它到任意一点最短路的问题. 记从起点出发到顶点 i 的最短距离为d[i],则有以下等式成立 d[i]=min{d[j]+(从j到 i 的边的权值) 看代码 #inclu ...

  2. Spark最简安装

    该环境适合于学习使用的快速Spark环境,采用Apache预编译好的包进行安装.而在实际开发中需要使用针对于个人Hadoop版本进行编译安装,这将在后面进行介绍. Spark预编译安装包下载——Apa ...

  3. 物体检测丨浅析One stage detector「YOLOv1、v2、v3、SSD」

    引言 之前做object detection用到的都是two stage,one stage如YOLO.SSD很少接触,这里开一篇blog简单回顾该系列的发展.很抱歉,我本人只能是蜻蜓点水,很多细节也 ...

  4. RabbitMQ使用教程(三)如何保证消息99.99%被发送成功?

    1. 前情回顾 RabbitMQ使用教程(一)RabbitMQ环境安装配置及Hello World示例 RabbitMQ使用教程(二)RabbitMQ用户管理,角色管理及权限设置 在以上两篇博客发布后 ...

  5. Golang: runnerw.exe: CreateProcess failed with error 216 (no message available)

    话说这个我应该遇到几次了 每次新项目都有这问题,我的记忆是金鱼的记忆吗? 好在这次隐约记起是包名的问题... 方法 修改包名为main

  6. 修改response,报错Cannot call getWriter(), getOutputStream() already called

    往response里面改数据,然后系统报这个错 此时直接return null即可解决 但是,要想返回相应的页面呢? 可以直接在response里设置返回的页面

  7. asp.net MVC 4.0 Controller回顾——ModelBinding实现过程

    以DefaultModelBinder为例 为简单模型绑定(BindSimpleModel)和复杂模型绑定(BindComplexModel) public virtual object BindMo ...

  8. vue-extend 选项

    vue-extend 选项 mixins 和extend 很相似,但有区别: var extendNews={ //后来的内容用变量接收 updated:function(){ console.log ...

  9. Lua学习---函数定义

    1.函数定义的格式: Lua使用function定义函数,语法如下: function function_name (arc) --arc表示参数列表,函数的参数列表可以为空 --body end 上 ...

  10. 2013 QConf上海软件开发大会总结

    带着工作中的一些疑问,我参加了在上海举办的QConf 全球软件开发大会.会议以主题的形式按分会场召开,我主要选择知名网站案例分析.大数据处理技术.高效能团队建设和金融系统架构与设计四个主题内容.三天会 ...