面向对象的程序设计

一、理解对象

 var person=new Object();
 person.name="Nicholas";
 person.age=29;
 person.sayName=function(){
 console.log(this.name)
 }
var person={
name:"Nicholas",
age:29,
job:"Software Engineer",
sayName:function(){
console.log(this.name)
}
}

1.1、属性特性

有两种属性:数据属性和访问器属性

  • 数据属性:包含一个数据值的位置 value [[Configurable]] [[Enumerable]] [[Writable]] [[Value]]
    修改默认属性:Object.defineProperty(obj, prop, descriptor)obj:属性所在对象
    prop:属性名字
    descriptor:一个描述符对象)
Object.defineProperty(person,"name",{
writable:false,
value:"Nicholas"
})
console.log(person.name);
person.name="Greg";
console.log(person.name)
  • 访问器属性:不包含数据值的位置 value[[Configurable]] [[Enumerable]] [[Get]] [[Set]]
var book={
_year:2004,
edition:1
}
Object.defineProperty(book,"year",{
get:function(){
return this._year;
},
set:function(newValue){
if(newValue>2004){    
this._year=newValue;                this.edition+=newValue-2004
}
}    
})    
book.year=2005;    
console.log(book.edition)//2

这是访问器属性中常见的设置方式,即设置一个属性的值会导致其他属性的变化

1.2、定义多个属性

Object.defineProperties(obj, props)
obj:在其上定义或修改属性的对象。
props:要定义其可枚举属性或修改的属性描述符的对象。对象中存在的属性描述符主要有两种:数据描述符和访问器描述符(更多详情,请参阅Object.defineProperty())

var book={};
Object.defineProperties(book,{
_year:{
writable:true,
value:2004
},
edition:{
writable:true,
value:1
},
year:{
get:function(){
return this._year
},
set:function(newValue){
if(newValue>2004){
this._year=newValue;
this.edition+=newValue-2004
}
}
}
}) 
book.year=2005;
console.log(book.edition)//2

1.3、读取属性的特性

Object.getOwnPropertySymbols(obj)
obj:在其上定义或修改属性的对象。
props:要读取其描述符的属性名称(可选)

var book={};
Object.defineProperties(book,{
_year:{
writable:true,
value:2004 
},
edition:{
writable:true,
value:1
},
year:{
get:function(){
return this._year
},
set:function(newValue){
if(newValue>2004){                    
this._year=newValue;                    
this.edition+=newValue-2004
}
}
}
})
var descriptor=Object.getOwnPropertyDescriptor(book,"_year");
console.log(descriptor.value)//2004
console.log(descriptor.configurable)//false    
console.log(typeof descriptor.get);//"undefined"
var descriptor=Object.getOwnPropertyDescriptor(book,"year");
console.log(descriptor.value)//"undefined"
console.log(descriptor.enumerable)//false
console.log(typeof descriptor.get);//"function"

二、创建对象

2.1、工厂模式

 function createPerson(name,age,job){
 var o=new Object();
 o.name=name;
 o.age=age;
 o.job=job;
 o.sayName=function(){
 console.log(this.name)
 }
 return o;
 }
 var person1=createPerson("Nicholas",29,"software Engineer");
 var person2=createPerson("Greg",27,"Doctor")    
 person1.sayName();  
 person2.sayName()

2.2、构造函数模式

function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
this.sayName=function(){
console.log(this.name)
 } 
 }
 var person1=new Person("Nicholas",29,"software Engineer");    
 var person2=new Person("Greg",27,"Doctor")
person1.sayName();
person2.sayName()

构造函数模式与工厂模式的区别

  • 没有显式创造对象
  • 直接将属性和方法赋给了this对象
  • 没有return语句
  • 创建Person,必须使用new操作符
2.2.1、构造函数当函数
2.2.2、构造函数的问题

构造函数虽然好用,但是每个方法都要在每个实例上重新创建一遍,因此我们可以把函数定义转移到构造函数外部:

 function Person(name,age,job){
 this.name=name;
 this.age=age;
 this.sayName=sayName;
 }
 function sayName(){
 console.log(this.name)
 }
 var person1=new Person("Nicholas",29,"software Engineer");
 var person2=new Person("Greg",27,"Doctor")    
 person1.sayName();
 person2.sayName()

但是全局作用域中的函数只能某个对象调用,不太可能,而且,如果对象需要定义很多方法,那么就要定义很多全局函数,没有封装性可言,故使用原型模型

2.3、原型模型

我们创建的每一个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法

2.3.1、理解原型对象

构造函数,原型对象,实例对象三者之间的关系如下:

在这里插入图片描述
虽然在所有的实现中都无法访问到[[Prototype]],但可以通过

  • isPrototypeOf()方法来确定对象之间是否存在这种关系
console.log(Person.prototype.isPrototypeOf(person1));//true
console.log(Person.prototype.isPrototypeOf(person2))//true
  • Object.getPrototypeOf():方法返回[[prototype]]的值
 console.log(Object.getPrototypeOf(person1)==Person.prototype);
 console.log(Object.getPrototypeOf(person1).name)

每当代码读取某个属性时,先在实例中搜索,如果没有找到,则沿着原型链搜索

function Person(){
    }  
Person.prototype.name="Nicholas";    
Person.prototype.age=29;    Person.prototype.job="Software Engineer";  
Person.prototype.sayName=function(){
console.log(this.name)
}
var person1=new Person()
var person2=new Person();
person1.name="Greg";
console.log(person1.name);
console.log(person2.name)
  • hasOwnProperty():检测一个属性是存在与实例还是原型
    返回值:来自实例:true
    来自原型:false
2.3.2、原型与in操作符

有两种方式使用in操作符:单独使用和在for-in循环中使用
在单独使用时,in操作符会在通过对象能够访问给定属性时返回true,无论该属性存在原型还是实例

  • 判断存在实例还是原型
function hasPrototypeProperty(object,name){  
return object.hasOwnProperty(name)&&(name in object)    }
  • 取得对象上所有可枚举的属性:Object.keys(obj)
    返回值:方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致 。
var keys=c(Person.prototype);
console.log(keys);//["name", "age", "job", "sayName"]
var p1=new Person();
p1.name="Rob";
p1.age=28
var p1keys=Object.keys(p1);
console.log(p1keys)//["name", "age"]
  • 得到所有属性:Object.getOwnPropertyNames(obj)
    返回值:方法返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括Symbol值作为名称的属性)组成的数组。
var key=Object.getOwnPropertyNames(Person.prototype)console.log(key)//["constructor", "name", "age", "job", "sayName"]
2.3.3、更简单的原型语法
Person.prototype={
name:"nicholas",
age:23,
job:"Software Engineer",
sayName:function(){
constructor.log(this.name)
}
}

但是会导致,通过constructor已经无法确定对象的类型了

console.log(friend instanceof Object);//true
console.log(friend instanceof Person);//true
console.log(friend.constructor==Person);//false
console.log(friend.constructor==Object)//true
2.3.4、原型的动态性

由于在原型中查找值的过程时一次搜索,因此我们对原型对象所作的任何修改都能立即从实例上反应出来-即使先创建了实例,后修改原型

2.3.5、原生对象的原型

可以通过原生对象的原型定义新方法

2.3.6、原型对象的问题

对于应用类型的属性:

2.3.7、组合使用构造函数模式和原型模式

构造函数模式用于实例属性,而原型模型用于定义方法和共享属性

2.3.8、动态原型模式
function Person(name,age,job){
this.name=name;
this.age=age;
this.job-job;
if(typeof this.sayName!="function"){
Person.prototype.sayName=function(){       
console.log(this.name)//"Nicholas"
}    
}
}
var friends=new Person("Nicholas",28,"Software Engineer");
friends.sayName()
2.3.9、寄生构造函数模式
function Person(name,age,job){
var o=new Object();
o.name=name;
o.age=age;
o.job=job
o.sayName=function(){
console.log(this.name)
}
return o;
}
var friends=new Person("Nicholas",28,"Software Engineer");
friends.sayName()

实例:想创建一个具有额外方法的数组,由于不能直接修改Array构造函数,因此可以使用这个模式

function SpecialArray(){
//创建数组
var value=new Array();
//添加值
value.push.apply(value,arguments)
//添加方法
value.toPipedString=function(){
return this.join("|")
}
//返回数组
return value}var colors=new SpecialArray("red","blue","green");
console.log(colors.toPipedString())
2.3.10、稳妥构造函数模式

三、继承

3.1.1、原型链
3.1.2、别忘记默认的原型

所有的引用类型默认都继承Object

3.1.3、确定原型和实例关系

instanceof :测试原型链中出现过的实例

 console.log(instance instanceof Object);//true  
 console.log(instance instanceof SuperType);//true     
 console.log(instance instanceof SubType)//true

isPrototypeOf():测试原型链出现的原型

 console.log(Object.prototype.isPrototypeOf(instance));//true 
 console.log(Object.prototype.isPrototypeOf(instance));//true
 console.log(Object.prototype.isPrototypeOf(instance))//true
3.1.4、谨慎的定义方法

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

 function SuperType(){
 this.property=true;
 }
 SuperType.prototype.getSuperValue=function()
 {
 return this.property;
 }
 function SubType(){
 this.subproperty=true
 }
 SubType.prototype=new SuperType();  
 SubType.prototype.getSubValue=function(){
 return this.subproperty;
 }
 SubType.prototype.getSuperValue=function(){
 return false;
 }
 var instance=new SuperType();
 console.log(instance.getSuperValue())//true
 var instance1=new SubType();
 console.log(instance1.getSuperValue())//false

注意:不能使用字面量创建原型方法

3.1.5、原型链的问题

引用类型值的原型

 function SuperType(){
 this.colors=["red","blue","green"];
 }
 function SubType(){
        }        
SubType.prototype=new SuperType();
var instance1=new SubType();
instance1.colors.push("black");
console.log(instance1.colors);//["red", "blue", "green", "black"]
var instance2=new SubType();
console.log(instance2.colors)//["red", "blue", "green", "black"]
3.1.6、借用构造函数

在子类型构造函数内部调用超类型构造函数,因为函数只不过是在特定环境中执行的代码,因此通过使用apply()和call()方法

function SuperType(){
this.colors=["red","blue","green"]; 
}
function SubType(){
SuperType.call(this)
}
var instance1=new SubType();
instance1.colors.push("black");
console.log(instance1.colors);//["red", "blue", "green", "black"]
var instance2=new SubType();
console.log(instance2.colors)//["red", "blue", "green"]
  • 传递参数
  • 借用构造函数的问题
3.1.7、组合继承
function SuperType(name){
this.name=name;
this.colors=["red","blue","green"];
}
SuperType.prototype.sayName=function(){
console.log(this.name)
}
function SubType(name,age){
SuperType.call(this,name);
this.age=age;
}
SubType.prototype=new SuperType();
SubType.prototype.sayAge=function(){
console.log(this.age)
}
var instance1=new SubType("Nicholas",29);
instance1.colors.push("black");
console.log(instance1.colors)// ["red", "blue", "green", "black"]
instance1.sayName();//Nicholas
instance1.sayAge();//29
var instance2=new SubType("Greg",30);
console.log(instance2.colors)//["red", "blue", "green"] 
instance1.sayName();//Nicholas
instance1.sayAge();//29

同样,与原型中一样,也可以使用instanceof和isPrototypeOf()方法

3.1.8、原型式继承

本质是执行对给定对象的浅复制

 function object(o){
 function F(){
 }
 F.prototype=o;
 return new F()
 }
 var person={name:'Nicholas',
 friends:["shelby","Count","Van"]
 }
 var anotherPerson=object(person);
 anotherPerson.name="Greg";
 anotherPerson.friends.push("Rob");
 var yetAnotherPerson=object(person);  
 yetAnotherPerson.name="Linda";  
 yetAnotherPerson.friends.push("barbie");
 console.log(person.friends)//["shelby", "Count", "Van", "Rob", "barbie"]

原型式继承,要求必须有一个对象可以作为另一个对象的基础,如果有这么一个对象,可以把它传递给object()函数
ECMAScript5中规范了原型式继承:Object.create()

 var person={name:'Nicholas',
 friends:["shelby","Count","Van"]
 }
var anotherPerson=Object.create(person) 
 anotherPerson.name="Greg";
 anotherPerson.friends.push("Rob");
 var anotherPerson=Object.create(person)    
 yetAnotherPerson.name="Linda";  
 yetAnotherPerson.friends.push("barbie");
 console.log(person.friends)//["shelby", "Count", "Van", "Rob", "barbie"]

Object.create()的第二个参数与Object.defineProperties()方法参数格式相同

var person={
name:'Nicholas',
friends:["shelby","Count","Van"]
}
var anotherPerson=Object.create(person,{
name:{
value:"Greg"
}
})
console.log(anotherPerson.name)
3.1.9、寄生式继承

与原型式继承很像,也是基于某个对象或者某些信息创建一个对象,然后增强对象,最后返回对象。

 function createAnother(original){
 var clone=object(original);//通过调用函数创建一个新对象
 clone.sayHi=function(){//以某种方式来增强这个对象
 console.log("hi");
 }
 return clone;//返回这个对象
 }

在主要考虑的是对象而不是自定义类型和构造函数时,可以考虑寄生式继承

3.1.10、寄生组合式继承

通过借用构造函数来继承属性,通过原型链来形成继承方法。其背后的基本思路是不必为了指定子类型原型而调用超类型(SuperType)的构造函数

  function inheritPrototype(SubType,SuperType)
  {
  var prototype=object(SuperType.prototype);  
  prototype.constructor=SubType; 
  SubType.prototype=prototype 
  }
function SuperType(name){
his.name=name;
this.colors=["red","green","blue"];
}
SuperType.prototype.sayName=function(){
console.log(this.name)
}
function SubType(name,age){
SuperType.call(this,name)
this.age=age
}
inheritPrototype(SubType,SuperType);
SubType.prototype.sayAge = function(){
console.log(this.age);
}; 
var instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
console.log(instance1.colors);  //"red,blue,green,black"
instance1.sayName();      //"Nicholas";   
instance1.sayAge();       //29
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值