读书笔记-JavaScript面向对象编程(二)
第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面向对象编程(二)的更多相关文章
- 读书笔记-JavaScript面向对象编程(一)
PDF下载链接: http://pan.baidu.com/s/1eSDSTVW 密码: 75jr 第1章 引言 1.1 回顾历史 1.2 变革之风 1.3 分析现状 1.4 展望未来 1.5 面向对 ...
- 读书笔记-JavaScript面向对象编程(三)
第7章 浏览器环境 7.1 在HTML页面中引入JavaScript代码 7.2概述BOM与DOM(页面以外事物对象和当前页面对象) 7.3 BOM 7.3.1 window对象再探(所以JavaSc ...
- Javascript高级程序设计--读书笔记之面向对象(二)
前面讲了面向对象的封装,这章我们就来说一说继承 1.原型链 实现原型链有一种基本模式,其代码大概如下 <script> function SuperType(){ this.propert ...
- Java Script 读书笔记 (四) 面向对象编程
1. 对象,属性 前面看到对象里删除属性一直疑惑,什么是对象,为什么属性可以删除, 我印象里的属性还是停留在property, 总想不明白为什么属性竟然能够删除.直到看到标准库才明白,原来对象就是py ...
- 《JavaScript面向对象编程指南(第2版)》读书笔记(二)
<JavaScript面向对象编程指南(第2版)>读书笔记(一) <JavaScript面向对象编程指南(第2版)>读书笔记(二) 目录 一.基本类型 1.1 字符串 1.2 ...
- 《JavaScript面向对象编程指南(第2版)》读书笔记(一)
目录 一.对象 1.1 获取属性值的方式 1.2 获取动态生成的属性的值 二.数组 2.1 检测是否为数组 2.2 增加数组长度导致未赋值的位置为undefined 2.3 用闭包实现简易迭代器 三. ...
- 《JavaScript面向对象编程指南》读书笔记②
概述 <JavaScript面向对象编程指南>读书笔记① 这里只记录一下我看JavaScript面向对象编程指南记录下的一些东西.那些简单的知识我没有记录,我只记录几个容易遗漏的或者精彩的 ...
- 《JavaScript面向对象编程指南》读书笔记①
概述 JavaScript快忘完了,想看一本专业书拾遗,所以看了这本<JavaScript面向对象编程指南>. 个人觉得这本书讲的很透彻很易懂,一些原来有疑惑的地方在这本书里面豁然开朗,看 ...
- JavaScript面向对象编程学习笔记
1 Javascript 面向对象编程 所谓"构造函数",其实就是一个普通函数,但是内部使用了this变量.对构造函数使用new运算符,就能生成实例,并且this变量会绑定在实例 ...
随机推荐
- C. Jury Marks 思维题
http://codeforces.com/contest/831/problem/C 做的时候想不到,来了个暴力. 对于每个b[i],枚举每一个a[i],就有1个可能的情况. 然后用vector存起 ...
- Raymond Mill In Lisp
Raymond Mill is suitable for producing minerals powder, which is widely used in the metallurgy, buil ...
- Dedecms标签arclistsg调用单表模型出错的解决方法
使用arclistsg标签调用分类信息等单表模型出错提示Column 'id' in where clause is ambiguous, 修改文件:include\taglib\arclistsg ...
- using System.Web.Script.Serialization
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式.易于人阅读和编写.同时也易于机器解析和生成.它基于JavaScript Programming Langu ...
- asp.net 在IIS上配置出现的一些问题
1.可能会遇到一下图的错无.请求的内容似乎是脚本.因而将无法由静态文件处理程序来处理---大概的原因是应用程序池选择错误了.如第二幅图如此解决即可 解决方案如下两个图所示. 我遇到了以上的问题之后能也 ...
- java中的递归思想及应用
递归就是自己调自己,最需要注意的就是结束条件,否则可能就是死循环,导致内存溢出 public T a(Object x,Object y) { if(条件true) { a(x1,y1); } els ...
- Android 环信 调用相机崩掉 mikdir()
protected void selectPicFromCamera() { if (!EaseCommonUtils.isSdcardExist()) { Toast.makeText(getAct ...
- tomcat jvm参数优化
根据gc(垃圾回收器)的选择,进行参数优化 JVM给了三种选择:串行收集器.并行收集器.并发收集器,但是串行收集器只适用于小数据量的情况,所以这里的选择主要针对并行收集器和并发收集器. -XX:+Us ...
- 卸载gitlab
一.停止gitlab sudo gitlab-ctl stop 二.卸载gitlab sudo rpm -e gitlab-ce三.查看gitlab进程 杀掉第一个守护进程 kill -9 4473 ...
- GetIPAddress——获得本地IP地址信息
1.gethostname()需要初始化套接字库 加载#pragma comment(lib,"ws2_32.lib"),和WSAStartup(MAKEWORD(2,2),&am ...