第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. C. Jury Marks 思维题

    http://codeforces.com/contest/831/problem/C 做的时候想不到,来了个暴力. 对于每个b[i],枚举每一个a[i],就有1个可能的情况. 然后用vector存起 ...

  2. Raymond Mill In Lisp

    Raymond Mill is suitable for producing minerals powder, which is widely used in the metallurgy, buil ...

  3. Dedecms标签arclistsg调用单表模型出错的解决方法

    使用arclistsg标签调用分类信息等单表模型出错提示Column 'id' in where clause is ambiguous,  修改文件:include\taglib\arclistsg ...

  4. using System.Web.Script.Serialization

    JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式.易于人阅读和编写.同时也易于机器解析和生成.它基于JavaScript Programming Langu ...

  5. asp.net 在IIS上配置出现的一些问题

    1.可能会遇到一下图的错无.请求的内容似乎是脚本.因而将无法由静态文件处理程序来处理---大概的原因是应用程序池选择错误了.如第二幅图如此解决即可 解决方案如下两个图所示. 我遇到了以上的问题之后能也 ...

  6. java中的递归思想及应用

    递归就是自己调自己,最需要注意的就是结束条件,否则可能就是死循环,导致内存溢出 public T a(Object x,Object y) { if(条件true) { a(x1,y1); } els ...

  7. Android 环信 调用相机崩掉 mikdir()

    protected void selectPicFromCamera() { if (!EaseCommonUtils.isSdcardExist()) { Toast.makeText(getAct ...

  8. tomcat jvm参数优化

    根据gc(垃圾回收器)的选择,进行参数优化 JVM给了三种选择:串行收集器.并行收集器.并发收集器,但是串行收集器只适用于小数据量的情况,所以这里的选择主要针对并行收集器和并发收集器. -XX:+Us ...

  9. 卸载gitlab

    一.停止gitlab sudo gitlab-ctl stop 二.卸载gitlab sudo rpm -e gitlab-ce三.查看gitlab进程 杀掉第一个守护进程 kill -9 4473 ...

  10. GetIPAddress——获得本地IP地址信息

    1.gethostname()需要初始化套接字库 加载#pragma comment(lib,"ws2_32.lib"),和WSAStartup(MAKEWORD(2,2),&am ...