JavaScript学习--Item37 面向对象高级程序设计

1. JS是基于原型的程序

建立一个简单的面向对象的类。有属性,有方法。

function Aaa(){
    this.name = ‘小明‘;
}
Aaa.prototype.showName = function(){
    alert( this.name );
};

var a1 = new Aaa();
a1.showName();

在JS的自身的对象中,也是new一个对象,然后调用方法,比如:

var arr = new Array();
arr.push();
arr.sort();

在JS源码 : 系统对象也是基于原型的程序。比如:

function Array(){
    this.lenglth = 0;
}
Array.prototype.push = function(){};
Array.prototype.sort = function(){};

尽量不要去修改或者添加系统对象下面的方法和属性,这样会改变系统的方法。

var arr = [1,2,3];
Array.prototype.push = function(){}
arr.push(4,5,6);
alert( arr );

这样修改后,push方法就失效了,覆盖了JS本省的push方法。

那么如何自己重写Array的push方法呢?

var arr = [1,2,3];
Array.prototype.push = function(){
    //this : 1,2,3
    //arguments : 4,5,6

    for(var i=0;i<arguments.length;i++){
        this[this.length] = arguments[i]
    }

    return this.length;
};

arr.push(4,5,6);

alert( arr );*/

//pop shift unshift splice sort

2.JS中的包装对象

我们知道js的基本数据类型有 string, number, boolean, null, undefined, object.但是当你看到下面的代码时,你发现了什么?

var str = ‘hello‘;

alert( typeof str );//String

str.charAt(0);
str.indexOf(‘e‘);

除了对象可以调用方法,为什么基本数据类型也可以?这是因为在js中,string, number, boolean都有自己的包装对象。String Number Boolean

var str = new String(‘hello‘);//通过构造函数构造的数据类型就是对象了,可以直接调用方法了

//alert( typeof str );

alert(str.charAt(1));

String.prototype.charAt = function(){};

那基本类型调用方法时,发生了什么呢?

var str = ‘hello‘;
str.charAt(0);  //基本类型会找到对应的包装对象类型,然后包装对象把所有的属性和方法给了基本类型,然后包装对象消失,包装类型就和快递员一样。

比如要实现字符串的一个lastValue方法:

var str = ‘hello‘;

String.prototype.lastValue = function(){
    return this.charAt(this.length-1);
};

alert( str.lastValue() ); //o

str通过原型继承,使本身具有了lastValue()方法,所以可以直接调用,我们再来看个例子:

var str = ‘hello‘;

str.number = 10;

alert( str.number );  //undefined

这里为什么是undefined,因为这里包装类型并没有把number共享给str,只是自己有,而复制完消失,str.name没有接受到值,变成undefined。

3. JS中的原型链

在js中,实例对象与原型之间的连接,叫做原型链,原型链之间的连接叫__proto__( 隐式连接 )

原型链的最外层是Object。

function Aaa(){

}
Aaa.prototype.num = 10;
var a1 = new Aaa();
alert(a1.num);

比如我们一般的定义一个对象Aaa,并在原型上定义了一个属性num,新new的a1是怎么访问到原型上的属性的呢?

这里就是a1有一个__proto__属性,指向Aaa.prototype原型对象,顾可以访问到。

假如这样呢?

function Aaa(){
    this.num = 20;
}
Aaa.prototype.num = 10;
var a1 = new Aaa();
alert(a1.num);//20

这里有两个num,弹出的是哪一个呢?这就是原型链的规则,先从本身对象找,本身对象没有到原型对象上找,。。。。。一直找下去,何处是尽头?知道找到object的原型,如果没有就报错,因为object的原型是null,这个原型连接起来的链就是一条原型链。看例子

function Aaa(){
    //this.num = 20;
}
//Aaa.prototype.num = 10;
Object.prototype.num = 30;

var a1 = new Aaa();
alert(a1.num);

4. 面向对象的一些常用属性和方法

  • hasOwnProperty() : 看是不是对象自身下面的属性
var arr = [];
arr.num = 10;
Array.prototype.num2 = 20;

//alert(  arr.hasOwnProperty(‘num‘)  );  //true

alert(  arr.hasOwnProperty(‘num2‘)  );  //false
  • constructor : 查看对象的构造函数

    • 每个原型都会自动添加constructor属性
    • For in 的时候有些属性是找不到的
    • 避免修改construtor属性
function Aaa(){
}

var a1 = new Aaa();

alert( a1.constructor );  //Aaa

var arr = [];
alert( arr.constructor == Array );  //true

每个对象的原型的构造函数都自动指向本身,可以修改为别的,但是不建议修改:

function Aaa(){
}
//Aaa.prototype.constructor = Aaa;   //每一个函数都会有的,都是自动生成的

//Aaa.prototype.constructor = Array;

var a1 = new Aaa();
alert( a1.hasOwnProperty == Object.prototype.hasOwnProperty );  //true

说明hasOwnProperty 属性是通过原型链,在Object.prototype原型上的。

有时候我们会不经意的就把原型修改了,这个时候我们就要把原型修正一下:

function Aaa(){
}

Aaa.prototype.name = ‘小明‘;//采用这种原型继承,constructor没有改变
Aaa.prototype.age = 20;

Aaa.prototype = {//采用json格式的对象,这就是相当于一个Object对象赋值,所以constructor变为了Object。
    constructor : Aaa,
    name : ‘小明‘,
    age : 20
};

var a1 = new Aaa();
alert( a1.constructor );

采用for in循环,是循环不到原型上constructor属性的。

function Aaa(){
}

Aaa.prototype.name = 10;
Aaa.prototype.constructor = Aaa;

for( var attr in Aaa.prototype ){
    alert(attr);//只有name
}
  • instanceof : 运算符。对象与构造函数在原型链上是否有关系【对象是否是构造函数的一个实例】
function Aaa(){
}

var a1 = new Aaa();

//alert( a1 instanceof Object );  //true

var arr = [];

alert( arr instanceof Array );//true;
  • toString() : 系统对象下面都是自带的 , 自己写的对象都是通过原型链找object下面的。主要做一些解析,主要用于Array、Boolean、Date、Object、Number等对象
var arr = [];
alert( arr.toString == Object.prototype.toString ); //false*/

/*function Aaa(){
}
var a1 = new Aaa();
alert( a1.toString == Object.prototype.toString );  //true*/
- 把对象转成字符串
/*var arr = [1,2,3];

    Array.prototype.toString = function(){
        return this.join(‘+‘);
    };

    alert( arr.toString() );  //‘1+2+3‘*/
  • 做进制转换,默认10进制
//var num = 255;
    //alert( num.toString(16) );  //‘ff‘
  • 最重要一点是做类型判断
//利用toString做类型的判断 : 

/*var arr = [];

alert( Object.prototype.toString.call(arr) == ‘[object Array]‘ ); */
//‘[object Array]‘

类型判断的常用方法:三种typeof是不行的,根本区分不开。假如判断一个数据类型是数组?

alert( arr.constructor == Array );  

alert( arr instanceof Array );  

alert( Object.prototype.toString.call(arr) == ‘[object Array]‘ ); 

但是在一种很特殊的情况下,就只能用第三种,在iframe中创建一个数组,前两种方法就会失效,但是在大多数情况下,还是可以的。

window.onload = function(){

    var oF = document.createElement(‘iframe‘);
    document.body.appendChild( oF );

    var ifArray = window.frames[0].Array;

    var arr = new ifArray();

    //alert( arr.constructor == Array );  //false

    //alert( arr instanceof Array );  //false

    alert( Object.prototype.toString.call(arr) == ‘[object Array]‘ );  //true
};

5. JS对象的继承

什么是继承?

在原有对象的基础上,略作修改,得到一个新的对象不影响原有对象的功能。

其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。

构造函数、原型和实例的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针_proto_

1.原型继承

实现原型链有一种基本模式,其代码大致如下:

function SuperType(){
    this.property = true;
}
SuperType.prototype.getSuperValue = function(){
    return this.property;
};

function SubType(){
    this.subproperty = false;
}
//继承了SuperType
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function (){
    return this.subproperty;
};

var instance = new SubType();
alert(instance.getSuperValue()); //true

以上代码定义了两个类型:SuperType 和SubType。每个类型分别有一个属性和一个方法。它们的主要区别是SubType 继承了SuperType,而继承是通过创建SuperType 的实例,并将该实例赋给SubType.prototype 实现的。实现的本质是重写原型对象,代之以一个新类型的实例。换句话说,原来存在于SuperType 的实例中的所有属性和方法,现在也存在于SubType.prototype 中了。在确立了继承关系之后,我们给SubType.prototype 添加了一个方法,这样就在继承了SuperType 的属性和方法的基础上又添加了一个新方法。这个例子中的实例以及构造函数和原型之间的关系如图

我们没有使用SubType 默认提供的原型,而是给它换了一个新原型;这个新原型就是SuperType 的实例。于是,新原型不仅具有作为一个SuperType 的实例所拥有的全部属性和方法,而且其内部还有一个指针,指向了SuperType 的原型。

我们知道,所有引用类型默认都继承了Object,而这个继承也是通过原型链实现的,上面例子展示的原型链中还应该包括另外一个继承层次。

可以通过两种方式来确定原型和实例之间的关系instanceof操作符,isPrototypeOf()方法。

alert(instance instanceof Object); //true
alert(instance instanceof SuperType); //true
alert(instance instanceof SubType); //true

alert(Object.prototype.isPrototypeOf(instance)); //true
alert(SuperType.prototype.isPrototypeOf(instance)); //true
alert(SubType.prototype.isPrototypeOf(instance)); //true

原型继承注意点:

(1)子类型有时候需要重写父类型中的某个方法,或者需要添加超类型中不存在的某个方法。但不管怎

样,给原型添加方法的代码一定要放在替换原型的语句之后。

function SuperType(){
    this.property = true;
}
SuperType.prototype.getSuperValue = function(){
    return this.property;
};
function SubType(){
    this.subproperty = false;
}
//继承了SuperType
SubType.prototype = new SuperType();
//添加新方法,一定放在原型替换之后,不然会被覆盖掉的哦。
SubType.prototype.getSubValue = function (){
    return this.subproperty;
};
    //重写超类型中的方法
    SubType.prototype.getSuperValue = function (){
        return false;
    };
    var instance = new SubType();
    alert(instance.getSuperValue()); //false

(2)通过原型链实现继承时,不能使用对象字面量创建原型方法。因为这样做就会重写原型链,直接被json取代。

function SuperType(){
    this.property = true;
}
SuperType.prototype.getSuperValue = function(){
    return this.property;
};
function SubType(){
    this.subproperty = false;
}
//继承了SuperType
SubType.prototype = new SuperType();
//使用字面量添加新方法,会导致上一行代码无效
SubType.prototype = {
    getSubValue : function (){
        return this.subproperty;
    },
    someOtherMethod : function (){
        return false;
    }
};
var instance = new SubType();
alert(instance.getSuperValue()); //error!

原型链的问题

原型链虽然很强大,可以用它来实现继承,但它也存在一些问题。其中,最主要的问题来自包含引用类型值的原型。包含引用类型值的原型属性会被所有实例共享;在通过原型来实现继承时,原型实际上会变成另一个类型的实例。于是,原来实例的属性也就顺理成章地变成了现在的原型属性了。引用类型共享了数据。。。

function SuperType(){
    this.colors = ["red", "blue", "green"];
}
function SubType(){
}
//继承了SuperType
SubType.prototype = new SuperType();

var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"

var instance2 = new SubType();
alert(instance2.colors); //"red,blue,green,black"

这个例子中的SuperType 构造函数定义了一个colors 属性,该属性包含一个数组(引用类型值)。SuperType 的每个实例都会有各自包含自己数组的colors 属性。当SubType 通过原型链继承了SuperType 之后,SubType.prototype 就变成了SuperType 的一个实例,因此它也拥有了一个它自己的colors 属性——就跟专门创建了一个SubType.prototype.colors 属性一样。但结果是什么呢?结果是SubType 的所有实例都会共享这一个colors 属性。而我们对instance1.colors 的修改能够通过instance2.colors 反映出来,就已经充分证实了这一点。

原型链的第二个问题是:在创建子类型的实例时,不能向超类型的构造函数中传递参数。实际上,应该说是没有办法在不影响所有对象实例的情况下,给超类型的构造函数传递参数。

2.构造函数继承(类式继承)

这种技术的基本思想相当简单,即在子类型构造函数的内部调用超类型构造函数。别忘了,函数只不过是在特定环境中执行代码的对象,因此通过使用apply()和call()方法也可以在(将来)新创建的对象上执行构造函数

function SuperType(){
    this.colors = ["red", "blue", "green"];
}
function SubType(){
    //继承了SuperType
    SuperType.call(this);
}
var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"

var instance2 = new SubType();
alert(instance2.colors); //"red,blue,green"

传递参数

相对于原型链而言,借用构造函数有一个很大的优势,即可以在子类型构造函数中向超类型构造函

数传递参数。

function SuperType(name){
    this.name = name;
}
function SubType(){
    //继承了SuperType,同时还传递了参数
    SuperType.call(this, "Nicholas");
    //实例属性
    this.age = 29;
}

var instance = new SubType();
alert(instance.name); //"Nicholas";
alert(instance.age); //29

在SubType 构造函数内部调用SuperType 构造函数时,实际上是为SubType 的实例设置了name 属性。为了确保SuperType 构造函数不会重写子类型的属性,可以在调用超类型构造函数后,再添加应该在子类型中定义的属性。

借用构造函数的问题

如果仅仅是借用构造函数,那么也将无法避免构造函数模式存在的问题——方法都在构造函数中定义,因此函数复用就无从谈起了,而且,在超类型的原型中定义的方法,对子类型而言也是不可见的,结果所有类型都只能使用构造函数模式。

3. 组合继承

将原型链和借用构造函数的技术组合到一块,从而发挥二者之长的一种继承模式。其背后的思路是使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。这样,既通过在原型上定义方法实现了函数复用,又能够保证每个实例都有它自己的属性。

function SuperType(name){
    this.name = name;
    this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
    alert(this.name);
};

function SubType(name, age){
    //继承属性
    SuperType.call(this, name);
    this.age = age;
}

//继承方法
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
    alert(this.age);
};

var instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
instance1.sayName(); //"Nicholas";
instance1.sayAge(); //29

var instance2 = new SubType("Greg", 27);
alert(instance2.colors); //"red,blue,green"
instance2.sayName(); //"Greg";
instance2.sayAge(); //27

4.拓展:拷贝继承

先理解一下对象的拷贝,对象的引用是地址引用,双向改变。而基本数据类型是按值传递,不影响原来的数值。

var a = {
    name : ‘小明‘
};

var b = a;

b.name = ‘小强‘;

alert( a.name );//小强

采用拷贝继承的方式,将方法一个个拷贝过去就不会发生双向继承了。

var a = {
    name : ‘小明‘
};

//var b = a;
var b = {};
extend( b , a );
b.name = ‘小强‘;
alert( a.name );    

function extend(obj1,obj2){
    for(var attr in obj2){
        obj1[attr] = obj2[attr];
    }
}

通过这个对象的复制之后,就可以实现拷贝继承了:

//继承 : 子类不影响父类,子类可以继承父类的一些功能 ( 代码复用 )

//属性的继承 : 调用父类的构造函数 call

//方法的继承 : for in :  拷贝继承 (jquery也是采用拷贝继承extend)

function CreatePerson(name,sex){   //父类
    this.name = name;
    this.sex = sex;
}
CreatePerson.prototype.showName = function(){
    alert( this.name );
};

var p1 = new CreatePerson(‘小明‘,‘男‘);
//p1.showName();

function CreateStar(name,sex,job){  //子类

    CreatePerson.call(this,name,sex);

    this.job = job;

}

//CreateStar.prototype = CreatePerson.prototype;

extend( CreateStar.prototype , CreatePerson.prototype );

CreateStar.prototype.showJob = function(){
};

var p2 = new CreateStar(‘黄晓明‘,‘男‘,‘演员‘);

p2.showName();

function extend(obj1,obj2){
    for(var attr in obj2){
        obj1[attr] = obj2[attr];
    }
}

6.实例:用继承实现拖拽

<!DOCTYPE html>
<html>
<head>
    <title>拖拽继承</title>
    <style type="text/css">
        #div1{width: 100px;height: 100px;background: red;position: absolute;}
        #div2{width: 100px;height: 100px;background: blue;position: absolute;left: 100px;}
    </style>
</head>

<body>
<div id="div1"></div>
<div id="div2"></div>

<script type="text/javascript">

window.onload = function(){
    var d1 = new Drag(‘div1‘);
    d1.init();

    var d2 = new childDrag(‘div2‘);
    d2.init();
}

function Drag(id){
    this.obj = document.getElementById(id);//定义拖拽的对象
    this.disX = 0;//水平边距
    this.disY = 0;//垂直边距
}

Drag.prototype.init = function(){//拖拽函数
    var that = this;
    this.obj.onmousedown = function(e){
        var e = e || window.event;
        that.fnDown(e);

        document.onmousemove = function(e){
            var e = e || window.evnet;
            that.fnMove(e);
        }
        document.onmouseup = function(){
            that.fnUP();
        }
        return false;
    }

}

Drag.prototype.fnDown = function(e){
    //clientX 返回当事件被触发时,鼠标指针的水平坐标
    //offsetLeft:获取对象相对于版面左侧位置
    this.disX = e.clientX - this.obj.offsetLeft;//鼠标位置到obj左边位置
    this.disY = e.clientY - this.obj.offsetTop;
}

Drag.prototype.fnMove = function(e){
    this.obj.style.left = e.clientX - this.disX + ‘px‘;
    this.obj.style.top = e.clientY - this.disY +‘px‘;
}

Drag.prototype.fnUP = function(){
    document.onmousedown= null;
    document.onmousemove = null;
}

//定义子类,子类继承父类,但是与父类有不同,不能拖拽脱离可视区域

function childDrag(id){
    Drag.call(this,id);//属性用call,apply
}

// 方法用for in
extend(childDrag.prototype,Drag.prototype);

//重写父类的方法
childDrag.prototype.fnMove = function(e){
    var leftX = e.clientX - this.disX;
    var topY = e.clientY - this.disY;
    var clientW = document.documentElement.clientWidth - this.obj.offsetWidth;
    var clientH = document.documentElement.clientHeight  - this.obj.offsetHeight;

    if (leftX < 0){
        leftX = 0;
    }else if (leftX >clientW) {
        leftX = clientW;
    }

    if (topY < 0 ) {
        topY = 0;
    }else if (topY> clientH) {
        topY = clientH;
    }

    this.obj.style.left = leftX + ‘px‘;
    this.obj.style.top = topY + ‘px‘;
}

function extend(obj1, obj2){//拷贝函数
    for(var attr in obj2){
        obj1[attr] = obj2[attr];
    }
}

</script>
</body>
</html>
时间: 2024-11-09 01:27:36

JavaScript学习--Item37 面向对象高级程序设计的相关文章

JavaScript Item37—— 面向对象高级程序设计

1. JS是基于原型的程序 建立一个简单的面向对象的类.有属性,有方法. <code class="hljs javascript has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre

python学习之面向对象高级特性

类属性与实例属性类属性就是类对象所拥有的属性,它被所有类对象的实例对象所共有,在内存中只存在一个副本.在前面的例子中我们接触到的就是实例属性(对象属性),它不被所有类对象的实例对象所共有,在内存中的副本个数取决于对象个数. 05_类属性鱼实例属性.py import random class Turtle(object): # power是类属性. power = 100 def __init__(self): # x,y:实例属性. self.x = random.randint(0, 10)

【JavaScript】之面向对象的程序设计

最近几天跟着视频利用原生的JavaScript做了几个小插件,有图片画廊和图片轮转,实践的过程中,发现对JS的面向对象部分很是陌生,今日重看了<JavaScript高级程序设计>的面向对象部分,并结合了IMOOC网上的<JS深入浅出>教程在此做一下知识的梳理和总结. 一.什么是对象 ECMA-262中把对象定义为:"无序属性的组合,每个属性包含基本值.对象或者函数." 这就相当于说对象是一组没有特定顺序的值,每个属性或方法都有一个名字key,和对应的value.

学习笔记-面向对象的程序设计

面向对象的语言有一个标志,即拥有类的概念,抽象实例对象的公共属性与方法,基于类可以创建任意多个实例对象,一般具有封装.继承.多态的特性!但JS中对象与纯面向对象语言中的对象是不同的,ECMA标准定义JS中对象:无序属性的集合,其属性可以包含基本值.对象或者函数.可以简单理解为JS的对象是一组无序的值,其中的属性或方法都有一个名字,根据这个名字可以访问相映射的值(值可以是基本值/对象/方法). 一.理解对象: 第一种:基于Object对象 var person = new Object(); pe

Javascript学习总结-面向对象-(五)

1. 面向对象 JavaScript面向对象的脚本语言,此时开发者在开发的时候需要找对象,默认提供了内置的对象.也可以根据开发者的需求自己定义对象. 1.1.     基本数据类型包装类 为了便于操作基本类型值,ECMAScript 提供了3个特殊的引用类型:Boolean,Number,String.它们是引用类型.当读取基本数据类型时,后台就会创建一个对应的基本包装类对象,所以我们在操作基本数据类型时,可以直接调用一些方法. 1.1.1.  String //通过构造函数创建String 对

你不知道的JavaScript--Item37 面向对象高级程序设计

1. JS是基于原型的程序 建立一个简单的面向对象的类.有属性,有方法. function Aaa(){ this.name = '小明'; } Aaa.prototype.showName = function(){ alert( this.name ); }; var a1 = new Aaa(); a1.showName(); 在JS的自身的对象中,也是new一个对象,然后调用方法,比如: var arr = new Array(); arr.push(); arr.sort(); 在JS

javascript学习笔记 - 面向对象 理解对象

ECMAScript 中有两种属性:数据属性和访问器属性 一 属性类型 1.数据属性.数据属性有4个描述其行为的特性 [[Configurable]]表示能否通过delete删除属性从而重新定义属性: 能否修改属性: 能否把属性修改为访问器属性.  [[Enumerable]] 表示是否能通过for-in循环进行枚举.  [[Writable]] 表示能否修改属性的值  [[value]] 包含这个属性的值.读取和写入属性值,都是从这个位置来操作  对象字面量初始化时定义的属性,这些特性默认都是

c++学习笔记の面向对象高级编程@Boolan

一.函数的传参&返回值(引用传值效率高于值传递) 1)参数尽量考虑使用对象引用,对于要求函数不改变对象内容的情况使用const. 2)  返回值尽量使用对象引用, 对于返回值是局部对象的情况只能返回对象不能是引用 原因是局部的对象在函数执行完毕之后会被析构,外部的引用将对应一个无效的对象. 举个栗子理解下函数传参,假设自己有份文件需要别人帮你处理,你把文件传递给别人的方式简单来说可以有两种: 1.将文件拷贝一份发送给对方,之后让他处理,这样的好处是他的修改不会影响到你的原始文件 缺点是浪费空间,

56本前端工程师必备的javaScript学习书籍

分享56本前端工程师必备的javaScript学习书籍(包括了pdf,chm,doc) 1.JavaScript权威指南(第6版)(中文版)2.深入浅出Javascript.O‘reilly.Head.First.Javascript.Jan.20083.JavaScript王者归来月影扫描版4.JavaScript.DOM高级程序设计贝斯扫描版5.精通javascript(图灵计算机科学丛书)6.JavaScript面向对象15分钟教程7.原型.作用域.闭包的完整解释8.Javascript面