JAVASCRIPT高程笔记-------第六章 面向对象的程序设计
理解对象的概念 js中的对象与其他 编程语言中的类不一样 ECMAscript 没有类的概念 ECMA-262 把对象定义为 “无序属性的集合,其属性可以包含基本值,对象或者函数” 也就是键值对 可以理解成散列表 示例简单的创建一个对象
var person = new Object();
person.name = "zhangsan"
person.age = 20;
person.sayhi = function (){
return "myName is "+this.name;
}
alert(person.sayhi()); // myName is zhangsan
6.1 属性类型 ---ECMAscript包含两种属性: 数据属性和访问器属性
6.1.1数据属性 ---简单来理解就是 描述对象属性的属性 相当于java中的元数据信息
[[Configgurable]] 表示属性是否可以通过delete 删除属性 从而重新定义属性 或者能否将属性修改为访问器属性 示例中定义的person对象的name、age以及sayhi()方法 默认都是为true
[[Enumerable]] 表示对象的该属性是否可以通过 for-in循环返回该属性 示例中 定义的person对象的name、age以及sayhi()方法 默认都是为true
[[Waitable]] 表示能否修改 该对象的属性的值 相当于java中 final 关键字修饰的常量 示例中 定义的person对象的name、age以及sayhi()方法 默认都是为true
[[Value]] 表示 该对象属性的实际值 例如示例中 person.name 的实际值就是等于"zhangsan" 这个特性的的值默认为undefined
r如何修改对象属性默认的特性 则必须使用ECMAscript5的 Object.defineProperty()方法 此方法接收三个参数 第一个参数为对象 第二个参数为对象的属性,第三个参数为对象属性的描述 示例
var person ={};
Object.defineProperty(person,'name',{
writable: false, //设置person对象的name属性为只读
configurable: true,
Enumerable: true,
value: "zhangsan"
});
alert(person.name); // ‘zhangsan’
person.name = 'lisi'; //因为person对象name属性已经变成只读 所以此处修改无效,并且在严格模式下的js解析将会报错
alert(person.name); // ‘zhangsan’
Object.defineProperty(person,'name',{
writable: true,
configurable: true,
Enumerable: true,
value: "zhangsan"
});
person.name = 'lisi';
alert(person.name); // lisi
当对象属性的 [[Configgurable]] 特性 被修改后 将 无法还原 因为再次修改时会报错
6.1.2 访问器属性 -----相当于java中的属性私有化 提供get和set方法 示例
var person ={
name : 'zhangsan',
_age: 25
};
Object.defineProperty(person,'age',{
get : function(){
return this._age;
},
set : function(newValue){
if(newValue>0){
this._age = newValue;
}
}
});
alert(person.age); //
person.age = -18; //由于设定了年龄不能为负数 所以此处的修改无效
alert(person.age); //
get和set 属性 也可以单独 设置 但若只设置了get 那么意味着该属性为只读 而不能写入 严格模式下 尝试写入操作则会报错
6.1.3 获取对象属性的特性描述 ECMAscript5中 可以设置对象属性的特性,亦可以通过 Object.getOwnPropertyDescriptor()方法来获取这些 特性信息 该方法返回的是一个对象 如果是访问器属性 则这个对象的属性为 configurable、enumerable、get和set 如果是 数据属性 则为 configurable、enumerable、waitable、value
6.2创建对象 简单理解成java中自定义对象
//工厂模式创建对象
function createPerson(name,age){
var o = new Object();
o.name = name;
o.age = age;
o.sayhi = function(){
alert('my name is ' + this.name);
}
return o;
}
var person1 = createPerson('zhangsan',27);
var person2 = createPerson('lisi',25);
//---------缺点 无法指定对象的类型------------------------ //构造函数模式 为了表示与普通函数的区别 函数首字母大写
function Person(name,age){
this.name = name;
this.age = age;
this.sayhi = function(){
alert('my name is ' + this.name);
}
}
var person1 = new Person('zhangsan',27);
var person2 = new Person('lisi',25);
构造函数与工厂模式创建对象的最大区别 就是 对象的类型鉴别
通过工厂模式创建的对象一定是Object类型 而通过构造函数创建的对象 既属于Object对象类型 也属于自定义对象类型 因为任何对象都是由Object对象继承而来的
构造函数模式创建对象带来的问题 由于每个函数都是一个特殊对象 因此person1和 person2的sayhi方法实际上引用了2个不同的对象 而只完成了相同的事情 就是告诉别人我叫啥 当 存在N个person对象的同时 也存在 N个sayh 对象 造成内存浪费
为解决这个问题 ------- 原型模式
function PersonF(name,age){
this.name = name;
this.age = age;
this.sayhi = function(){
alert('my name is ' + this.name);
};
}
var person1 = new PersonF('zhangsan',27);
var person2 = new PersonF('lisi',25);
alert(person1.sayhi === person2.sayhi); // false 因为每创建一个新的person对象 然后也创建了一个新的sayhi函数对象 所以他们不相等
//原型模式
function PersonP(name,age){
this.name = name;
this.age = age;
}
PersonP.prototype.sayhi= function(){
alert('my name is ' + this.name);
}
var person1 = new PersonP('zhangsan',27);
var person2 = new PersonP('lisi',25);
alert(person1.sayhi === person2.sayhi) // true 原型模式下 所有被创建的person对象 将共享sayhi函数对象,所以这里相等
无论任何时候创建一个新的函数 js解析器默认会创建这个函数的prototype对象 默认情况下 这个prototype只包含 函数的构造器constructor属性 而其他方法 都是从Object继承而来 或者 后期手动添加进去 例如示例中我们给PersonP()这个对象的prototype中添加了sayhi方法
当一个实例对象中添加了与原型中重名方法时 则实际使用的是实例对象中的方法 ------相当于java的子类重写父类方法 示例
//原型模式
function PersonP(name,age){
this.name = name;
this.age = age;
}
PersonP.prototype.sayhi= function(){
alert('my name is ' + this.name);
}
var person1 = new PersonP('zhangsan',);
var person2 = new PersonP('lisi',);
person1.sayhi(); // my name is zhangsan 来自于原型中的sayhi方法
person1.sayhi = function(){ //修改当前实例对象的sayhi方法
alert("my age = " + this.age);
};
person1.sayhi(); // my age = 27 来自于当前实例中的sayhi方法 alert(person1.hasOwnProperty("sayhi")); // true 此方法是person1对象实例的方法
alert(person2.hasOwnProperty("sayhi")); // false 此方法来自PersonP 原型中
hasOwnProperty() 检测对象的方法是由原型继承而来还是属于本身实例
原型与in操作符
in 操作符 用于判断 某个属性 是否 属于 指定 对象 示例 alert(('name' in person1)); // 执行结果为true in 操作符 不管对象的属性 是实例对象本身的属性 还是由原型中继承而来 都会返回true
for-in 循环 时 返回的都是能通过对象访问的、可以枚举的属性 ,也包括原型中存在的属性 首先遍历的是对象构造函数内的属性 然后再往原型中查找属性
function PersonP(name,age){
this.name = name;
this.age = age;
this.sayhi= function(){
alert('my name is ' + this.name);
}
}
PersonP.prototype.saybay= function(){
alert('my name is ' + this.name);
}
var person2 = new PersonP('lisi',);
person2.money = ;
for(var property in person2){
console.log(property); // 结果 name age sayhi money saybay 依次出现
}
var propertys = Object.keys(person2);
console.log(propertys); // 结果 ["name", "age", "sayhi", "money"]
Object.keys() 方法 接收一个对象参数 返回该对象上所有可枚举的属性
利用原型创建对象的简写方式
function PersonP(){
}
PersonP.prototype ={
constructor : PersonP, // 此属性表明构造器 为 PersonP 对象的构造器 否则PersonP构造器默认由Object对象继承而来 是属于Object对象的构造器 但是会导致对象的构造器的[[enumerable]]特性 设置为true
name : 'zhangsan',
age : ,
sayhi : function(){
alert('my name is ' + this.name);
}
}
在ECMAscript 5 js解析引擎下 可以重设 对象的对象构造器为不可枚举
Object.definedProperty(PersonP.prototype,'constructor',{
enumerable : false,
value: PersonP
});
原型的动态性 ------js引擎 在原型中查找值的过程是一次搜索 因此 对对象原型的任何修改 都会立即在对象的实例上体现出来
当 对象创建以后 再重写其原型 则会出现 无法调用原有函数的现象 参考示例 原因在于 对象实例原有的原型对象已经被切断与对象联系 因此原型查找时无法获取对象的sayhi方法
function PersonP(){
}
var person1 = new PersonP();
PersonP.prototype = {
constructor : PersonP, // 此属性表明构造器 为 PersonP 对象的构造器 否则PersonP 由于是一个空对象 构造器默认由Object对象继承而来
name : 'zhangsan',
age : ,
sayhi : function(){
alert('my name is ' + this.name);
}
}
person1.sayhi(); // error
6.2.4组合使用构造函数模式和原型模式
将对象私有的属性放在构造函数中,而通用的方法放在原型中 例如 以person为例子 name、age、sex可能因人而已 但都会有打招呼 做自我介绍的方法sayhi方法 是通用的可以放在原型中
动态原型模式
function Person(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
if(typeof this.sayhi != 'function'){
Person.prototype.sayhi = function(){
alert(this.name);
}
}
}
寄生构造函数模式----- 与工厂模式一致 在一个对象有多个实例的时候 而其中某一个实例需要特殊处理 ,在不影响其他实例属性数据的情况下可以使用此模式来完成操作 有点类似与java中的包装设计模式
function Person(name,age,sex){
var o = new Object();
o.name = name ;
o.age = age;
o.sex = sex;
o.sayhi =function(){
alert(this.name);
}
return o;
}
稳妥模式---- 没有公共属性,而且其他方法也不引用this对象 ,稳妥对象适合在一些安全的环境中使用(这些环境会禁用this和new) ,感觉像java中常量一样,一旦初始化就不可修改
function Person(name,age,sex){
var o = new Object();
o.sayhi =function(){
alert(name); //不使用this
}
return o;
}
var person = Person('zhangsan',18,'男'); //不使用new操作符创建对象
person.sayhi(); //除此方法 之外 没有任何其他方法可以访问 person对象的name属性
6.3 继承
一、原型链式继承 其本质 是重写原型对象,
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function(){
return this.property;
}
function SubType(){
this.subproperty = false;
}
SubType.prototype = new SuperType();
var sub = new SubType();
alert(sub.getSuperValue()); //true 通过原型继承而来的方法
原型继承注意的问题: 一、共享原型中的属性和方法 二、不能向超类型的构造函数中传递参数

二、借用构造函数
利用 apply() 和 call() 方法在新创建的对象上执行构造函数 此方式可以传递参数给超类的构造函数 问题点 一、不能继承超类原型的属性和方法,二、方法都在构造函数当中,无法实现函数复用
function SuperType(){
this.colors = ['red','blue','green'];
}
function SubType(){
SuperType.call(this);
}
var instance1 = new SubType();
instance1.colors.push('black');
alert(instance1.colors); //red,blue,green,black
三、组合式继承 ---又叫做伪经典继承
将原型链和借用构造函数组合到一块 利用原型链继承超类的原型属性和方法,而借用构造函数来实现对实例属性的继承
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.sayAge = function(){
alert(this.age);
}
var instance1 = new SubType('zhangsan',28);
instance1.colors.push('black');
alert(instance1.colors); //red,blue,green,black 继承自超类的属性
instance1.sayName(); //zhangsan 传递给超类的参数 超类原型中的方法
instance1.sayAge(); //28 自定义的方法
四、原型式继承
先建立一个临时性的构造函数 然后将传入的对象当做这个构造函数的原型,最后返回这个临时类型的新实例 从本质上看 是object对传入对象的一次浅复制
function object(o){
function F(){}
F.prototype = o;
return new F();
}
var person = {
name : 'zhangsan',
friends : ['lisi','wangwu']
}
var anotherPerson = object(person);
anotherPerson.name = 'zhaotiezhu';
anotherPerson.friends.push('zhangxiaohua'); //找了个女朋友赵小花
alert(person.friends); //lisi,wangwu,zhangxiaohua
ECMA5.0 中 新增了 Object.create() 方法 规范了原型式继承 此方法接收两个参数,第一个参数为创建新对象的原型 另外一个用于修饰新对象中元数据的描述(可选)如下面代码
var person = {
name : 'zhangsan',
friends : ['lisi','wangwu']
}
var person1 = Object.create(person);
person1.name = 'lisi';
alert(person1.name);
var person2 = Object.create(person,{
name :{
//writable : false,
value : 'wangwu'
}
}); //第二个参数为新对象的元数据修饰参数 此处由于未设置 person2对象的name属性 为可写的 所以导致 在修改 person2的name属性不成功
alert(person2.name); //wangwu
person2.name = 'zhaoliu';
alert(person2.name); //wangwu
寄生式继承 ----接收一个对象参数 在函数内部对此对象进行增强 然后返回这个函数--------------------------感觉像java中的实现接口????
function createAnother(original){
var clone = object(original);
clone.sayhi = function(){ //
alert("hi");
}
return clone;
}
var person ={
name:'zhangsan',
friends:['lisi','wangwu']
}
var anotherPerson = createAnother(person);
anotherPerson.sayhi(); //hi
6.3.6 寄生组合式继承
组合式继承的缺陷 ---无论任何条件下都需要调用两次超类型的构造函数 第一次为指定子类原型 第二次为子类构造函数中 借用超类的构造函数 第一次调用已经拥有了超类的属性 第二次调用 则是在子类上重新创建了与原型中相同属性 从而屏蔽了原型的属性
寄生组合式继承 通过借用构造函数来继承超类属性,通过原型链的混成形式来继承方法---------此方式体现在只调用一次超类的构造函数 避免了subtype.prototype上面创建多余的属性 ,原型链还能保持不变,因此能够正常使用 instanceof和isPrototypeOf()方法去确定类型
function inheritPrototype(subType,superType){
var prototype = Object(superType.prototype); //创建对象
prototype.constructor = subType; //增强对象
subType.prototype = prototype; //指定对象
}
function SuperType(name){
this.name = name;
this.friends = ['xiaohua','xiaoming']
}
SuperType .prototype.sayhi = function (){
alert(this.name);
}
function SubType(name,age){
SuperType.call(this,name);
this.age = age;
}
inheritPrototype(SubType,SuperType);
SubType.prototype.sayAge = function(){
alert(this.age);
}
var person = new SubType('zhangsan',28);
person.friends.push('xiaohong'); //找了个女朋友 小红
person.sayAge(); //
person.sayhi(); //zhangsan
alert(person.friends); //xiaohua,xiaoming,xiaohong
小结 : ECMAscript支持面向对象编程,但没有类和接口的概念 。对象可以在代码执行过程中创建和增强,因此具有动态性而非严格的定义的实体对象,
创建对象 1.工厂模式 定义一个函数 接收指定参数 函数内部创建Object对象 添加传入的参数作为属性,然后返回对象
2.构造函数模式 可以创建自定义引用类型 可以向内置对象一样使用new 操作符 缺点 每个成功都无法得到复用 包括方法
3.原型模式,使用构造函数的prototype属性来指定共有的属性和方法
对象继承 ECMAscript 主要通过原型链实现继承 原型链的构造是将一个实例赋值给另一个构造函数 ,这样通过构造函数 new处理的对象的原型指向超类的实例 实现 属性和方法复用
通常使用 组合式寄生继承 避免了多次调用超类的构造函数 以及属性的唯一性
JAVASCRIPT高程笔记-------第六章 面向对象的程序设计的更多相关文章
- JavaScript高级程序设计学习笔记第六章--面向对象程序设计
1.ECMAScript没有类的概念,ECMA-262 把对象定义为:“无序属性的集合,其属性可以包含基本值.对象或者函数.”,有点类似于散列表 2.ECMAScript 中有两种属性:数据属性和访问 ...
- JAVASCRIPT高程笔记-------第五章 引用类型
一.Object类型 1.1创建方式 ①new关键字 : var person = new Oject(); ②给定直接量: var person = { name : "zhangsan& ...
- javascript高程笔记-------第四章 变量、作用域和内存问题
首先JavaScript中的变量分为基本类型和引用类型. 基本类型就是保存在栈内存中的简单数据段,而引用类型指的是那些保存在堆内存中的对象. 1.参数传递 javascript中所有参数的传递都是值传 ...
- JavaScript高级程序设计-第六章面向对象的程序设计
创建对象主要的两种形式,创建Object实例和创建对象字面量 对象包含属性和方法 数据 .属性有四个特性,特性是为了描述属性行为的,他们是: Configurable(可配置的)是否能删除或是否能修改 ...
- JAVASCRIPT高程笔记-------第 七章 函数表达式
7.1递归 经典递归例子 function factorial(num){ if(num <= 1){ return 1; }else{ return num * factorial(num - ...
- 读书笔记 - js高级程序设计 - 第六章 面向对象的程序设计
EcmaScript有两种属性 数据属性 和 访问器属性 数据属性有4个特性 Configurable Enumerable Writable Value 前三个值的默认值都为false ...
- 【学习笔记】六:面向对象的程序设计——理解JS中的对象属性、创建对象、JS中的继承
ES中没有类的概念,这也使其对象和其他语言中的对象有所不同,ES中定义对象为:“无序属性的集合,其属性包含基本值.对象或者函数”.现在常用的创建单个对象的方法为对象字面量形式.在常见多个对象时,使用工 ...
- javascript高级程序设计第3版——第6章 面向对象的程序设计
第六章——面向对象的程序设计 这一章主要讲述了:面向对象的语言由于没有类/接口情况下工作的几种模式以及面向对象语言的继承: 模式:工厂模式,构造函数模式,原型模式 继承:原型式继承,寄生式继承,以及寄 ...
- Android群英传笔记——第六章:Android绘图机制与处理技巧
Android群英传笔记--第六章:Android绘图机制与处理技巧 一直在情调,时间都是可以自己调节的,不然世界上哪有这么多牛X的人 今天就开始读第六章了,算日子也刚好一个月了,一个月就读一半,这效 ...
随机推荐
- [RxJS] Use RxJS concatMap to map and concat high order observables
Like switchMap and mergeMap, concatMap is a shortcut for map() followed by a concatAll(). In this le ...
- php实现 坐标移动
php实现 坐标移动 一.总结 一句话总结:伪代码,带函数逻辑,函数这样的方式写算法程序会节约超多的时间. 1.为什么算法题数据输入最好用多组数据输入的方式? 因为都是多组数据测试,而且多组数据输入 ...
- href="javascript:;" href="javascript:void(0);" href="#"区别
一.href="javascript:;" 这种用法不正确,这么用的话会出现浏览器访问"javascript:;"这个地址的现象: 二.href="j ...
- Android菜鸟的成长笔记(26)——普通广播与有序广播
BroadcastReceiver是Android系统的四大组件之一,BroadcastReceiver是一个全局的系统级监听器,它拥有自己的独立进程. 我们来写一个最简单的广播接收过程 先在mani ...
- javascript 验证附件大小
///验证单个文件不能超过30M function onChangeFile() { ///定义布尔类型的返回结果,初始值为false(默认不超过30M) var sResult = false; / ...
- Shell脚本实现超简洁的在Linux服务器上安装nginx、resin、java、tomcat、redis等程序
说明: 用平常的方式在Linux服务器上安装程序,需要下载安装包.进入安装包位置.给安装包文件赋予可执行权限.执行安装.设置环境变量--等等一系列复杂的操作.并且如果有关联也需要一个一个的挨着安装.耗 ...
- 【64.52%】【codeforces 697D】Puzzles
time limit per test1 second memory limit per test256 megabytes inputstandard input outputstandard ou ...
- Android官方教程翻译(6)——添加ActionBar
The action bar allows you to add buttons for the most important action items relating to the app's c ...
- sql server通过脚本添加链接服务器
exec sp_addlinkedserver 'ZZSJK','','SQLOLEDB','192.168.10.22' --链接服务器名称 ‘’ ip地址exec sp_addlinkedsr ...
- 解决android模拟器无法上网问题
1. 将 android的tool增加到,windows 环境变量 path中, D:\Android\android-sdk_r3-windows\android-sdk-windows\tool ...