面向对象(第六章)
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script type="text/javascript">
//第六章 面向对象的程序设计
<!--6.1 理解对象-->
var person =new Object();
person.name = "nicholas";
person.age =29;
person.job = "soft Engineer";
person.sayName = function(){
alert(this.name);
}
// 转为字面量语法
var person ={
name:"nichloas",
age:29,
job:"soft Enginerr",
sayName:function(){
alert(this.name);
}
}
//6.1.1属性类型
// defineProperty 用来修改属性默认的特性
// 1.数据属性-->writable 不可写 非严格模式下忽略赋值,严格模式报错
var person ={};
Object.defineProperty(person,"name",{
writable:false, // 表示是否能修改属性的值
value:"Nicholas"
});
alert(person.name); //Nicholas
person.name ="greg";
console.log(person.name); //Nicholas
// configurable 能否通过delete删除属性从而重新定义属性 非严格模式下,忽略删除,严格模式报错
var person={};
Object.defineProperty(person,"name",{
configurable:false,
value:"Nicholas"
});
console.log (person.name); // "Nicholas"
delete person.name;
console.log(person.name); //"Nicholas"
// //一旦configurable属性定义为不可配置,就不能再变为可配置了,此时,调用Object.defineProperty()方法修改writable,会报错
var person ={};
Object.defineProperty(person,"name",{
configurable:false,
value:"Nicholas"
});
//跑出错误
Object.defineProperty(person,"name",{
configrable:true,
value:"Nicholas"
})
//2.访问器属性
// 设置一个属性的值,导致其他属性发生变化
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
console.log(book._year); //2005
// __defineGetter__ 定义访问器的旧有方法
var book={
_year:2004,
edition:1
};
book.__defineGetter__("year",function(){
return this._year;
})
book.__defineSetter__("year",function(newValue){
if(newValue>2004){
this._year=newValue;
this.edition +=newValue-2004;
}
});
book.year=2005;
console.log(book.edition); //2
console.log(book._year); //2005
// 6.1.2 定义多个属性
var booke={};
Object.defineProperties(book,{
_year:{
value:2004
},
edtion:{value:1},
year:{
get:function(){
return this._year;
},
set:function(newValue){
if(newValue>2004){
this._year =newValue;
this.edition +=newValue -2004;
}
}
}
})
// 6.1.3 读取属性的特性
// Object.getOwnPropertyDescriptor(),取得给定属性的描述符,接受两个参数:属性所在的对象和要读取其描述符的属性名称
//如果是访问器属性,这个对象的属性有configurable,enumerable,get和set
//如果是数据属性,这个对象的属性有configurable,enumerable,writable和value
var book ={};
Object.defineProperties(book,{
_year:{
value:2004
},
edition:{
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); //undefinded
console.log(descriptor.enumerable); // false
console.log(typeof descriptor.get); //function
//6.2 创建对象
//6.2.1 工厂模式
// 解决了创建多个相似对象的问题,没有解决对象识别问题(既知道一个对象的类型)
function createPerson(name,age,job){
var o =new Object();
o.name =name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(this.name);
};
return o;
}
var person1 =createPerson("Nicholas",29,"soft Engineer");
var person2 = createPerson("greg",27,"doctor");
// 6.2.2 构造函数模式
function Person(name,age,job){
this.name =name;
this.age=age;
this.job=job;
this.sayName=function(){
alert(this.name);
}
}
var person1 = new Person("Nicholas",29,"software Engineer");
var person2 = new Person("Greg",27,"Doctor");
// constructor(构造函数)
console.log(person1.constructor == Person); //true
console.log(person2.constructor == Person); //true
// instanceof(运算符)
console.log(person1 instanceof Object);//true
console.log(person1 instanceof Person);//true
console.log(person2 instanceof Object);//true
console.log(person2 instanceof Person);//true
//1.将构造函数当做函数
//当构造函数使用
var person = new Person("Nicholas",29,"software Engineer");
// person.sayName(); //"Nicholas"
//当做普通函数使用
Person("greg",27,"doctor");
window.sayName(); //"greg"
//在另个对象的作用域中使用
var o= new Object();
Person.call(o,"kristen",25,"nurse");
// o.sayName(); //"kristen"
//2.函数的问题
// 因为 函数本身也是对象,每次定义一个函数,就是实例化了一个函数
function Person(name,age,job){
this.name =name;
this.age=age;
this.job=job;
this.sayName = new Function(" alert(this.name)"); //与声明函数在逻辑上是等价的
}
var person1 = new Person("Nicholas",29,"software Engineer");
var person2 = new Person("Greg",27,"Doctor");
console.log(person1.sayName == person2.sayName); //false
//改写为放在构造函数外,作为全局作用域,但只能被部分函数使用
function Person(name,age,job){
this.name =name;
this.age=age;
this.job=job;
this.sayName = sayName;
}
function sayName(){
alert (this.name);
}
var person1 = new Person("Nicholas",29,"software Engineer");
var person2 = new Person("Greg",27,"Doctor");
//6.2.3原型模式
function Person(){
}
Person.prototype.name ="Nicholas";
Person.prototype.age =29;
Person.prototype.job ="software Engineer";
Person.prototype.sayName =function(){
alert(this.name);
};
var person1 =new Person();
person1.sayName(); //"Nicholas"
var person2 =new Person();
person2.sayName(); //"Nicholas"
alert(person1.sayname == person2.sayName) //true
// isPrototypeOf判断是否为原型
console.log(Person.isPrototypeOf(person1)); //true
console.log(Person.isPrototypeOf(person2)); //true
//Object.getPrototypeOf(), 返回 [[prototype]]的值
console.log(Object.getPrototypeOf(person1) == Person.prototype); // true
console.log(Object.getPrototypeOf(person1).name);// "nicholas"
function Person(){
}
Person.prototype.name ="Nicholas";
Person.prototype.age =29;
Person.prototype.job ="software Engineer";
Person.prototype.sayName =function(){
alert(this.name);
};
var person1 = new Person();
var person2 = new Person();
person1.name ="greg";
console.log(person1.name); // "greg" -- 来自实例
console.log(person2.name); //"nicholas" -- 来自原型
//添加属性时(即使值为null),会屏蔽原型中的值,可以delete操作符完全删除实例属性,从而让我们重新访问原型中的属性
function Person(){
}
Person.prototype.name ="Nicholas";
Person.prototype.age =29;
Person.prototype.job ="software Engineer";
Person.prototype.sayName =function(){
alert(this.name);
};
var person1 = new Person();
var person2 = new Person();
person1.name ="greg";
console.log(person1.name); // "greg" -- 来自实例
console.log(person2.name); //"nicholas" -- 来自原型
delete person1.name;
console.log(person1.name); //"nicholas" -- 来自原型
//hasOwnProperty() 检测一个属性是否存在于实例中,还是存在于原型中
function Person(){
}
Person.prototype.name ="Nicholas";
Person.prototype.age =29;
Person.prototype.job ="software Engineer";
Person.prototype.sayName =function(){
alert(this.name);
};
var person1 = new Person();
var person2 = new Person();
console.log(person1.hasOwnProperty("name")); //false
person1.name ="greg";
console.log(person1.name); // "greg" -- 来自实例
console.log(person1.hasOwnProperty("name")); //true
console.log(person2.name); //"nicholas" -- 来自原型
console.log(person2.hasOwnProperty("name")); //false
delete person1.name;
console.log(person1.name); //"nicholas" -- 来自原型
console.log(person1.hasOwnProperty("name")); //false
//2. 原型和in操作符
//in 单独使用使用时,in操作符会在通过对象能够访问给定属性时返回true,无论属性在实例中还是原型中
function Person(){
}
Person.prototype.name ="Nicholas";
Person.prototype.age =29;
Person.prototype.job ="software Engineer";
Person.prototype.sayName =function(){
alert(this.name);
};
var person1 = new Person();
var person2 = new Person();
console.log(person1.hasOwnProperty("name")); //false
console.log("name" in person1); //true
person1.name ="greg";
console.log(person1.name); // "greg" -- 来自实例
console.log(person1.hasOwnProperty("name")); //true
console.log("name" in person1); //true
console.log(person2.name); //"nicholas" -- 来自原型
console.log(person2.hasOwnProperty("name")); //false
console.log("name" in person2); //true
delete person1.name;
console.log(person1.name); //"nicholas" -- 来自原型
console.log(person1.hasOwnProperty("name")); //false
console.log("name" in person1); //true
//判断属性存在于原型
function hasPrototypeProperty(object,name){
return !object.hasOwnProperty(name) && (name in object);
}
function Person(){
}
Person.prototype.name ="Nicholas";
Person.prototype.age =29;
Person.prototype.job ="software Engineer";
Person.prototype.sayName =function(){
alert(this.name);
};
var person = new Person();
alert(hasPrototypeProperty(person,"name")); //true
person.name ="greg";
alert(hasPrototypeProperty(person,"name")); //false
//获取对象上的所有可枚举的实例属性. Object.keys(),就收一个对象作为参数,返回一个包含所有可枚举属性的字符串数组
function Person(){};
Person.prototype.name ="Nicholas";
Person.prototype.age =29;
Person.prototype.job ="software Engineer";
Person.prototype.sayName =function(){
alert(this.name);
};
var keys = Object.keys(Person.prototype);
console.log(keys); //["name", "age", "job", "sayName"]
var p1= new Person();
p1.name = "Rob";
p1.age =31;
var p1keys =Object.keys(p1);
console.log(p1keys); //
// 获得所有实例属性,无论是否可枚举
var keys = Object.getOwnPropertyNames(Person.prototype);
console.log(keys); //["constructor", "name", "age", "job", "sayName"]
//3.更简单的原型语法
function Person(){}
Person.prototype ={
name:"nichloas",
age:29,
job:"soft Enginerr",
sayName:function(){
alert(this.name);
}
}
//constructor 不在指向 Person 通过constructor 已经无法确定对象类型
var friend = new Person();
console.log(friend instanceof Object); //true
console.log(friend instanceof Person);//true
console.log(friend.constructor == Person);//false
console.log(friend.constructor == Object);//true
// constructor设置为适当的值
function Person(){}
Person.prototype ={
constructor:Person,
name:"nichloas",
age:29,
job:"soft Enginerr",
sayName:function(){
alert(this.name);
}
};
// 但是通过上面方法会将他的[enumerable]设置为true,可以通过 Object.defineProperty()设置为false
function Person(){}
Person.prototype ={
constructor:Person,
name:"nichloas",
age:29,
job:"soft Enginerr",
sayName:function(){
alert(this.name);
}
};
//重设构造函数,只适用于ECMA5script 兼容的浏览器
Object.defineProperty(Person.prototype,"constructor",{
enumerable:false,
value:Person
});
//4.原型的动态性
var friend = new Person();
Person.prototype.sayHi =function(){
alert("hi");
};
friend.sayHi();
// 实例与原型之间之间的链接只不过是一个指针,而非一个副本
//实例的指针仅仅指向原型,而不是构造函数
// 重写原型对象后切断了现有原型与任何之前已存在的对象实例之间的联系,他们引用的任然是最初的原型
function Person(){
}
var friend =new Person();
Person.prototype ={
constructor:Person,
name:"nichloas",
age:29,
job:"soft Enginerr",
sayName:function(){
alert(this.name);
}
};
friend.sayName(); //error
// 5.原生对象的原型
alert(typeof Array.prototype.sort); // "function"
alert(typeof string.prototype.substring); // "function"
String.prototype.startsWith= function(text){
return this.indexOf(text) == 0;
}
var msg = "hello world!";
alert(msg.startsWith("hello"));
//6.原型对象
// 对于包含引用类型值的属性来说,会出现更改其他实例对应额值
function Person(){
}
Person.prototype ={
constructor:Person,
name:"nicholas",
age:29,
job:"software Engineer",
friends:["shelby","corrt"],
sayName:function(){
alert(this.name);
}
};
var person1 =new Person();
var person2 =new Person();
person1.friends.push("Van");
console.log(person1.friends);//"shelby", "corrt", "Van"]
console.log(person2.friends); //"shelby", "corrt", "Van"]
console.log(person1.friends === person2.friends) ; //true
//6.2.4 组合使用构造函数模式和原型模式
//构造函数模式用户定义实例属性,而原型模式用于定义方法和共享的属性.
function Person(name,age,job){
this.name= name;
this.age=age;
this.job=job;
this.friends=["shelby","corrt"];
}
Person.prototype ={
constructor:Person,
sayName:function(){
alert(this.name);
}
};
var person1 = new Person("Nicholas",29,"softWare Engineer");
var person2 = new Person("Greg",27,"Doctor");
person1.friends.push("Van");
alert(person1.friends); //"shelby,count,Van"
alert(person2.friends); //"shelby.concut"
alert(person1.sayName == person2.friends); // true
alert(person1.sayName == person2.sayName); //false
//6.2.5 动态原型模式
//if判断后,只会在初次调用时才会执行,此后,原型已完成初始化
// 如果有多个属性或方法, 只需判断一个属性或方法即可.
//也可以用in生探测of操作符确定他的类型
function Person(name,age,job){
// 属性
this.name= name;
this.age=age;
this.job=job;
//方法
if(typeof this.sayName != "function"){
Person.prototype.sayName =function(){
alert(this.name);
}
}
}
var friend = new Person("Nicholas",29,"software Engineer");
friend.sayName();
//6.2.6 寄生构造模式
//Person 创建了一个新对象,并以相应的属性和方法初始化该对象,然后又返回了这个对象,
//除了使用new操作符并把使用的包装函数叫做叫做构造函数之外,这个模式跟工厂模式其实一模一样.
function Person(name,age,job){
var o =new Object();
o.name =name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(this.name);
};
return o;
}
var friend= new Person("Nichholas",29,"software Engineer");
friend.sayName();
//返回的对象与构造函数或者与构造函数的原型属性之间没有关系
function SpecialArray(){
//创建数组
var values= new Array();
//添加值
values.push.apply(values,arguments);
//tianjia添加方法
values.toPipedString = function(){
return this.join("|");
}
//返回数组
return values;
}
var colors =new SpecialArray("red","blue","green");
alert(colors.toPipedString());
//6.2.7 稳妥构造函数模式
//稳妥对象,指的是没有公共属性,而且其方法也不引用this的对象.
//适合用在一些安全的环境中(这些环境中会禁止使用this和new),或者在防止数据被其他应用改动时使用.
//稳妥对象与寄生构造函数区别, 1.新创建对象的实例方法不用引用this.2.不使用new调用构造函数
function Person(name,age,job){
//创建要返回的对象
var o =new Object();
//可以再这里定义私有变量和函数
//添加方法
o.sayName = function(){
alert(name);
};
//返回对象
return o;
}
// 重点:除了调用sayname()方法除外,没有别的房访问其数据成员.即使有其他代码会给这个对象添加方法或数据成员,单页不可能有别的方法访问传入到构造函数的原始数据.
var friend = Person("Nicholas",29,"sofeWare Engineer");
friend.sayName(); //"Nicholas"
//6.3 继承
//6.3.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
//1.别忘记默认的原型
//2.确定原型和实例的关系
console.log(instance instanceof object); //true
console.log(instance instanceof SuperType); //true
console.log(instance instanceof SubType); //true
console.log(Object.prototype.isPrototypeOf(instance)); //true
console.log(SuperType.prototype.isPrototypeOf(instance)); //true
console.log(SubType.prototype.isPrototypeOf(instance)); //true
//3.谨慎的定义方法
function SuperType(){
this.prototype =true;
}
SuperType.prototype.getSuperValue =function(){
return this.property;
};
function SubType(){
this.subproperty =false;
}
//继承了 SuperType
SubType.prototype = new SuperType();
//添加新方法
SubType.prototype.getSuperValue = function(){
return false;
};
//chogn重写超类型中的方法
SubType.prototype.getSuperValue =function(){
return false;
};
var instance = new SubType();
alert(instance.getSuperValue()); //false
//在通过原型链实现继承时,不能使用对象字面量创建原型方法.这样会重写原型链
function SuperType(){
this.prototype =true;
}
SuperType.prototype.getSuperValue =function(){
return this.property;
};
function SubType(){
this.subproperty =false;
}
//继承了 SuperType
SubType.prototype = new SuperType();
//使用字面量添加新方法,会导致上一行代码无效
SubType.prototype={
getSubValue:function(){
return this.subprerty;
},
someOtherMethod:function(){
return false;
}
};
var instance =new SubType();
alert(instance.getSuperValue()); //error
//4.原型链的问题
//问题一:包含引用类型值的原型,包含引用类型值的原型属性会被所有实例共享
function SuperType(){
this.colors =["red","blue","green"];
}
function SubType(){
}
//继承了SuperTyp
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"
//问题二:在创建子类的实例时,不能像超类型的构造函数中传递参数\
//6.3.2借用构造函数
//基本思想: 在子类型构造函数内容调用超类型构造函数
function SuperType(){
this.colors =["red","blue","green"];
}
function SubType(){
SuperType.call(this);
}
//继承了SuperTyp
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"
//`1.传递参数
//为了确保SuperType构造函数不会重写子类型的属性,可以在调用超类型构造函数后,再添加应该在子类型中定义的属性
function SuperType(){
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
//2.借用构造函数的问题
//方法都在构造函数中定义,因此函数服用就无从谈起,而且在超类型中定义的方法,对已子类型而言也是不可见的
//6.3.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(instance1.colors); //"red,blue,green"
instance1.sayName(); //"Nicholas"
instance1.sayAge(); //29
//6.3.4 原型式继承
// 借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型.
function object(o){
function F(){}
F.prototype = o;
return new F();
}
//
var person={
name:"Nicholas",
fridens:["shelby","court","van"]
};
var anotherPerson =Object(person);
aonotherPerson ="greg";
anotherPerson.friends.push("Rob");
var yetAnotherPerson = object(person);
yetAnotherPerson.name ="Linda";
YetAnotherPerson.friends.push("Barbie");
alert(person.fridens); // "shelby ,court,van,rob ,barbie"
//在传入一个参数的情况下,Object.create()和object()方法的行为相同
var person ={
name:"Nicholas",
friends:["shelby","court","van"]
}
var anotherPerson =Object.creat(person);
anotherPerson.name ="Greg";
anotherPerson.friends.push("Rob");
var yetAntherPerson =Object.create(person);
yetAnotherPerson.name="linda";
yetAntherPerson.fridens.push("Barbie");
alert(person.friends); //// "shelby ,court,van,rob ,barbie"
//Objerct.create()方法的第二个参数与Object.defineProperties()方法的第二个参数格式相同:每个属性都是通过自己的描述定义.
//以这种方式的指定的任何属性都会覆盖原型对象的同名属性
var person={
name:"nicholas",
friends:['shebly','court','van']
}
var anotherPerson =object.create(person,{
name:{
value:"greg"
}
})
//6.3.5 寄生式继承
//代码基于person返回了一个新对象--anotherPerson,新对象不仅具有person的属性和方法,而且还有自己的sayHi()方法
//在主要考虑对象不是自定义类型和构造函数的情况下,寄生式继承也是一种有用的模式.前面示范继承模式时使用的object()函数不是必须的,任何能够放回新对象的函数都是用此模式/
function createAnother(original){
var clone = object(original);
clone.sayHi =function(){
alert("h1");
};
return clone;
}
var person ={
name:"nicholas",
friends:['shellby','count','van']
};
var anotherPerson = createAnother(person);
anotherPerson.sayHi(); //"hi"
//6.3.6 寄生组合式继承
//组合继承的例子
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);
};
///所谓寄生组合式继承,及通过借用搞糟函数来继承属性,通过原型链的混成形式来继承方法.
//背后的主要思路是:不必为了指定子类型的原型而调用超类型的构造函数,我们所需要的无非就是超类型原型的一个副本而已.
//本质上就是为了使用寄生式继承来继承超类型的原型.然后在将结果指定给子类型的原型.
function inheritPrototype(subType,superType){
var prototype =object(superType.prototype);
prototype.constructor = subType;
subType.prototype =prototype;
}
//组合继承的例子
function SuperType(name){
this.name =name;
this.colors=["red","blue","green"];
}
//第一步常见超类型原型的一个副本,第二部,为创建的副本添加constructor属性.最后希,将新创建的对象(即副本)赋值给子类型的原型.
SuperType.prototype.sayName =function(){
alert(this.name);
};
function SubType(name,age){
//继承属性
SuperType.call(this,name);
this.age =age;
}
inheritPrototype(subTpe,superType);
subType.prototype.sayAge =function(){
alert(this.age);
}
</script>
</body>
</html>