您的位置:首页 > Web前端 > JavaScript

javascript高级程序设计笔记-第六章(面向对象编程)

2016-03-02 12:19 651 查看

一、对象属性

1、属性类型

数据属性的特性:

[[Configurable]]:能否通过delete删除属性,默认值为true

[[Enumerable]]:能否通过for-in循环返回属性,默认值为true

[[Writable]]:能否修改属性的值,默认值为true

[[Value]]:包含这个属性的值,默认是undefined

直接在对象上定义的属性,[[Configurable]]、[[Enumerable]]、[[Writable]]都被设置为true,[[Value]]被设置为指定的值

Object.defineProperty()方法,可以修改属性默认的特性

//接收三个参数:属性所在对象、属性名字、描述符对象
//描述符对象的属性必须是configurable、enumerable、writable、value
var person = {};
Object.defineProperty(person, "name", {
writable: false,
value: "Nicholas"
});
alert(person.name);         //Nicholas
person.name = "Greg";
alert(person.name);         //Nicholas(属性值被设置为不可修改)

在调用Object.defineProperty()方法时,如果不指定,configurable、enumerable、writable的默认值都是false(与在对象上定义属性情况相反)

2、访问器属性的特性:

[[Configurable]]:能否通过delete删除属性,默认值为true

[[Enumerable]]:能否通过for-in循环返回属性,默认值为true

[[get]]:读取属性时调用的函数,默认值为undefined

[[set]]:写入属性时调用的函数,默认值为undefined

设置一个属性的值导致其他属性发生变化

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;
alert(book.edition);        //2

3、定义多个属性:

Object.defineProperties()方法,接收两个参数:要添加和修改其属性的对象、第二个对象的属性与第一个对象中要添加和修改的属性一一对应

var book = {};
Object.defineProperties(book, {
_year: {
writable: true,
value: 2004
},
edition: {
writable: true,
value: 1
},
year: {
get: function() {
return this._year;
},
set: function() {
if (newValue > 2004) {
this._year = newValue;
this.edition += newValue - 2004;
}
}
}
});
//在book对象上定义了两个数据属性和一个访问器属性

4、读取属性的特性:

Object.getOwnPropertyDescriptor()读取给定属性的描述符,接收两个参数:属性所在对象、要读取其描述符的属性名称

//承接上例
var descriptor = Object.getOwnPropertyDescriptor(book, "_year");
alert(descriptor.value);            //2004
alert(descriptor.configurable);     //false

二、创建对象

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, "FE");
var person2 = createPerson("Greg", 27, "Doctor");

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, "FE");
var person2 = new Person("Greg", 27, "Doctor");
//不是同一个方法
alert(person1.sayName === person2.sayName); //false

与工厂模式区别:

没有显示创建对象

直接将属性和方法赋予this对象

没有return语句

调用构造函数经历四步:

创建一个新对象

将构造函数的作用域赋给新对象(因此this指向这个新对象)

执行构造函数中的代码

返回新对象

调用函数的几种方法:

//当作构造函数使用
var person = new Person("Nicholas", 29, "FE");
person.sayName();       //Nicholas
//作为普通函数使用
Person("Greg", 27, "Doctor");
window.sayName();       //Greg
//在另一个对象的作用域中调用
var o = new Object();
Person.call(o, "John", 25, "Nurse");
o.sayName();            //John

3、原型模式

让所有对象实例共享原型所保存的的属性和方法

function Person() {}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "FE";
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

简洁写法

function Person() {}
Person.prototype = {
name: "Nicholas",
age: 29,
job: "FE",
sayName: function() {
alert(this.name);
}
};
//区别是:constructor属性不再指向Person,而是指向Object构造函数(这种写法在重构原型函数)
//下面设置constructor属性,但其现在默认是可枚举的
function Person() {}
Person.prototype = {
constructor: Person,
name: "Nicholas",
age: 29,
job: "FE",
sayName: function() {
alert(this.name);
}
};
//下面是把上例的constructor属性可枚举改为原生的不可枚举
Object.defineProperty(Person.prototype, "constructor", {
enumerable: false,
value: Person
});

isPrototypeOf()方法,测试对象之间的关系

alert(Person.prototype.isPrototypeOf(person1));     //true
alert(Person.prototype.isPrototypeOf(person2));     //true

Object.getPrototypeOf()方法,取得原型对象中的值

alert(Object.getPrototypeOf(person1) === Person.prototype); //true
alert(Object.getPrototypeOf(person1).name);         //Nicholas

读取某个对象的属性时,首先从对象实例本身开始,如果没有找到,再继续搜索指针指向的原型对象

可以创建同名属性屏蔽原型中的属性,但是无法修改原型中的属性

hasOwnProperty()方法,检测一个属性是存在于实例中,还是存在于原型中

function Person() {}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "FE";
Person.prototype.sayName = function() {
alert(this.name);
};
var person1 = new Person();
alert(person1.name);                        //Nicholas
alert(person1.hasOwnProperty("name"));      //false

person1.name = "Greg";
alert(person1.name);                        //Greg
alert(person1.hasOwnProperty("name"));      //true

delete person1.name
alert(person1.name);                        //Nicholas
alert(person1.hasOwnProperty("name"));      //false

原型与in操作符:

单独使用in操作符,只要对象能够访问到给定属性就能返回true,而不管属性是在原型还是实例中

在for-in循环中,返回的是所有能够通过对象访问的、可枚举的属性,其中既包括实例中的属性、也包括原型中的属性

同时使用in操作符和hasOwnProperty()方法,可以确定该属性是存在于对象中,还是原型中

function hasPrototypeProperty(object, name) {
return !object.hasOwnProperty(name) && (name in object);
}

原型中的基本属性和方法(具体见基本概念中Object类型那一节)都不可枚举(后来添加到原型中的属性和方法默认可枚举)、实例中的添加的属性和方法默认可枚举(不使用Object.defineProperty()方法)

var o = {
name: "Nicholas",
};
for (var propName in o) {
alert(propName);        //name
}
//o的原型中的基本属性和方法都没有枚举

function Person() {}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "FE";
Person.prototype.sayName = function() {
alert(this.name);
};
var keys = new Person();
for (var propName in keys) {
alert(propName);        //name,age,job,sayName
}
//后来添加到原型中的属性和方法默认可以枚举

Object.keys()方法,接收一个对象作为参数,返回该对象所有可枚举属性的字符串数组

function Person() {}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "FE";
Person.prototype.sayName = function() {
alert(this.name);
};
var keys = Object.keys(Person.prototype);
console.log(keys);      //["name", "age", "job", "sayName"](原型的constructor属性默认不可枚举)

var p1 = new Person();
p1.name = "Bob";
p1.age = 31;
var p1keys = Object.keys(p1);
console.log(p1keys);            //["name", "age"](只限该对象,不看Person.prototype)
//上下两例比较
var p1 = new Person();
p1.name = "Bob";
p1.age = 31;
for (var propName in p1) {
console.log(propName);  //name,age,job,sayName(该对象和原型中所有可枚举的)
}

Object.getOwnPropertyNames()方法,获得所有实例属性,无论是否可枚举

var keys = Object.getOwnPropertyNames(Person.prototype);
console.log(keys);          //["constructor", "name", "age", "job", "sayName"]

var p1 = new Person();
p1.name = "Bob";
p1.age = 31;
console.log(Object.getOwnPropertyNames(p1));    //["name", "age"](只限该对象)

原型的动态性:对原型对象所做的任何修改都能够立即从实例上反映出来,但是通过简洁写法重写原型对象时,如果之前已经创建了实例,情况就不一样了

function Person() {}
var friend = new Person();
Person.prototype = {
constructor: Person,
name: "Nicholas",
age: 29,
job: "FE",
sayName: function() {
alert(this.name);
}
};
friend.sayName();           //报错

如图,因为实例中的指针仅指向原型,而不指向构造函数,所以原型修改为另一个对象,就等于切断了构造函数与最初原型之间的联系



原型对象的问题

function Person() {}
Person.prototype = {
constructor: Person,
name: "Nicholas",
age: 29,
job: "FE",
friends: ["Bob", "Amy"],
sayName: function() {
alert(this.name);
}
};
var person1 = new Person();
var person2 = new Person();
person1.friends.push("Van");
console.log(person1.friends);       //["Bob", "Amy", "Van"]
console.log(person2.friends);       //["Bob", "Amy", "Van"]
//Van添加到原型对象中

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

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

每个实例都有自己的一份实例属性的副本,但同时又共享着对方法的引用

function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.friends = ["Bob", "Amy"];
}
Person.prototype = {
constructor: Person,
sayName: function() {
alert(this.name);
}
}

var person1 = new Person();
var person2 = new Person();

person1.friends.push("Van");
console.log(person1.friends);       //["Bob", "Amy", "Van"]
console.log(person2.friends);       //["Bob", "Amy"]
console.log(person1.friends === person2.friends);   //false
console.log(person1.sayName === person2.sayName);   //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;
}
//创建实例
var instance = new SubType();
alert(instance.getSuperValue());        //true




原型链的问题

function SuperType() {
this.colors = ["red", "green", "blue"];
}
function SubType() {}
SubType.prototype = new SuperType();
var instance1 = new SubType();
instance1.colors.push("black");
console.log(instance1.colors);      //["red", "green", "blue", "black"]
var instance2 = new SubType();
console.log(instance2.colors);      //["red", "green", "blue", "black"]

当SubType通过原型链继承了SuperType之后,SubType.prototype就变成了SuperType的一个实例,因此它也拥有了一个它自己的colors属性(类似于专门创建了一个SubType.prototype.colors属性一样)。结果SubType所有实例都会共享这个colors属性。对其中一个实例的colors进行修改,其它实例的colors属性也会同步修改

注意不要使用对象字面量创建原型方法,因为这样会重写原型链

2、借用构造函数

在子类型构造函数的内部调用超类型构造函数

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", "green", "blue", "black"]
var instance2 = new SubType();
console.log(instance2.colors);      //["red", "blue", "green"]

3、组合继承

使用原型链实现对原型属性和方法的继承,而通过借用构造函数实现对实例属性的继承

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.constrcutor = SubType;
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", 28);
console.log(instance2.colors);  //["red", "blue", "green"]
instance2.sayName();            //Greg
instance2.sayAge();             //28

4、原型式继承

借助原型基于已有的对象创建新对象

function object(o) {
function F() {}
F.prototype = o;
return new F();
}
var person = {
name: "x",
friends: ["a", "b", "c"]
};
var person1 = object(person);
person1.name = "y";
person1.friends.push("d");

var person2 = object(person);
person2.name = "z";
person2.friends.push("e");

console.log(person.friends);        //["a", "b", "c", "d", "e"]

5、寄生式继承

创建一个仅用于封装继承过程的函数

function object(o) {
function F() {}
F.prototype = o;
return new F();
}
function createAnother(original) {
var clone = object(original);
clone.sayHi = function() {
alert("hi");
};
return clone;
}
var person = {
name: "x",
friends: ["a", "b", "c"]
};
var person1 = createAnother(person);
person1.sayHi();                //"hi"
console.log(person1.friends);   //["a", "b", "c"]
//新对象不仅有person所有的属性和方法,还有自己的sayHi()方法

6、寄生组合式继承

function inheritPrototype(subType, superType) {
var prototype = Object(superType.prototype);    //创建对象
prototype.constrcutor = subType;                //增强对象
subType.prototype = prototype;                  //指定对象
}
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;
}
inheritPrototype(SubType, SuperType);
SubType.prototype.sayAge = function() {
alert(this.age);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: