面向对象的程序设计
1. 创建对象
let person = Object();
person.name = "nicholas";
person.job= "Software Engineer";
person.age = 18;
person.sayName = function (string) {
alert(string + " " + this.name);
}
//或者
let person = {
name : "Nicholas",
job: "Software Engineer",
age: 18,
sayName: function (string) {
alert(string + " " + this.name);
}
}
person.sayName("Hello")
2. 数据属性
- [[Configurable]]:表示能否通过delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。像前面例子中那样直接在对象上定义的属性,它们的这个特性默认值为true。
- [[Enumerable]]:表示能否通过for-in 循环返回属性。像前面例子中那样直接在对象上定义的属性,它们的这个特性默认值为true。
- [[Writable]]:表示能否修改属性的值。像前面例子中那样直接在对象上定义的属性,它们的这个特性默认值为true。
- [[Value]]:包含这个属性的数据值。读取属性值的时候,从这个位置读;写入属性值的时候,把新值保存在这个位置。这个特性的默认值为undefined。
修改属性默认的特性需使用Object.defineProperty()方法,该方法接收三个参数:属性所在的对象、属性的名字和一个描述符对象。
描述符对象的属性必须是:configurable, enumerable,writable和value
let person = {}
Object.defineProperty(person, "name", {
configurable: false,//不可配置,倘若再次定义该属性将会抛出错误
writable: false,//不可修改
value: "Nicholas"
});//在调用该方法时,如果不指定,三个特性都默认为false
alert(person.name);//Nicholas
person.name = "zhj";
alert(person.name);//Nicholas
delete person.name;
alert(person.name);//Nicholas
3. 访问器属性
四个特性:
- [[Configurable]]:表示能否通过delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为数据属性。对于直接在对象上定义的属性,这个特性的默认值为true。
- [[Enumerable]]:表示能否通过for-in 循环返回属性。对于直接在对象上定义的属性,这个特性的默认值为true。
- [[Get]]:在读取属性时调用的函数。默认值为undefined。
- [[Set]]:在写入属性时调用的函数。默认值为undefined。
访问器属性不能直接定义,必须使用Object.defineProperty()来定义
let 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;
}
}
//不一定要同时指定getter与setter.只指定getter一位置属性不能写,只指定setter的属性不能读
});
book.year = 2005;
alert(book.edition);
//定义多个属性使用Object.defineProperties()方法
let 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;
}
}
}
});
读取属性的特性:Object.getOwnPropertyDescriptor()方法
let descriptor = Object.getOwnPropertyDescriptor(book, "_year");
alert(descriptor.value);
alert(descriptor.configurable);
let descriptor2 = Object.getOwnPropertyDescriptor(book, "year");
alert(descriptor2.enumerable);
alert(typeof descriptor.get);
alert(descriptor.value);
4. 创建对象:
-
工厂模式:
function createPerson(name, age, job) { let o = {}; o.name = name; o.age = age; o.job = job; o.sayName = function () { alert(this.name); }; return o; } let person1 = createPerson("Nicholas", 29 ,"Software engineee"); let person2 = createPerson("Greg", 27, "Doctor");
-
构造函数模式:
function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.sayName = function () { alert(this.name); }; } let person3 = new Person("Nicholas", 29, "Software Engineer"); let person4 = new Person("Greg", 27, "Doctor");
//Person函数除了上述的调用方式,还可以采用以下两种方式 Person("1", 24, 'doc'); window.sayName(); let o = {}; Person.apply(o, ['dsaff', 12, 'man']); o.sayName();
-
原型模式:
function Person() { } Person.prototype.name = "Nicholas"; Person.prototype.age = 29; Person.prototype.job = "engineer"; Person.prototype.sayName = function () { alert(this.name); }; let person5 = new Person(); person5.sayName();//"Nicholas" let person6 = new Person(); person6.sayName();//"Nicholas" alert(person5.sayName == person6.sayName)//true
alert(Person.prototype.isPrototypeOf(person1)); //true alert(Person.prototype.isPrototypeOf(person2)); //true
//ECMAScript 5 增加了一个新方法,叫Object.getPrototypeOf(),在所有支持的实现中,这个方法返回[[Prototype]]的值 alert(Object.getPrototypeOf(person1) == Person.prototype); //true alert(Object.getPrototypeOf(person1).name); //"Nicholas"
hasOwnProperty()方法在给定属性存在于对象实例中时返回true。
var person1 = new Person(); alert(person1.hasOwnProperty("name")); //false
in操作符
alert("name" in person1);//无论是在实例中还是原型中都会返回true
hasPrototypeProperty()
方法对在原型中的属性返回trueObject.keys()
方法接收一个对象作为参数,返回所有可枚举属性的字符串数组。let keys = Object.keys(Person.prototype);
getOwnPropertyNames()
方法返回所有实例属性更简单的原型语法:
function Person() { } Person.prototype = { //constructor: Person,如若constructor很重要,可以添加此句话,但这会导致constructor属性可枚举,因此可以使用Object.defineProperty方法修正 name: "Nicholas", age: 29, job: "Software Engineer", sayName: function () { alert(this.name); } };
实例中的指针仅仅指向原型,而不指向构造函数。
重写原型对象切断了现有原型与任何之前已经存在的对象实例之间的联系,引用的仍然是最初的原型
-
原生对象的原型
通过原生对象的原型,不仅可以取得所有默认方法的引用,也可以定义新方法
String.prototype.stawith = function (text) { return this.indexOf(text) == 0; }; let msg = 'hello world'; alert(msg.stawith('hello'));//true
原型的问题:对于包含引用类型的属性来说,会直接修改原型中的内容,而不会像其他属性一样在实例中生成数据覆盖原型的效果
-
组合使用构造函数模式和原型模式
构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。
-
动态原型模式
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); }; } }
-
寄生构造函数模式:与工厂模式是一模一样的
toPipedString()方法,返回以竖线分隔的数组值
-
稳妥构造函数模式:
不使用new和this
-
继承:接口继承和实现继承。在ECMAScript中只支持实现继承
-
原型链:
function SuperType() { this.property = true; } SuperType.prototype.getSuperValue = function () { return this.property; } function SubType() { this.subproperty = false; } SubType.prototype = new SuperType(); SubType.prototype.getSubValue = function () { return this.subproperty; }; let instance = new SubType(); alert(instance.getSuperValue());//true
所有引用类型都继承了Object,继承也是通过原型链来实现的
谨慎的定义方法:(给原型添加方法的代码一定要放在替换原型的语句之后)
function SuperType() { this.property = true; } SuperType.prototype.getSuperValue = function () { return this.property; } function SubType() { this.subproperty = false; } SubType.prototype = new SuperType(); //添加新方法 SubType.prototype.getSubValue = function () { return this.subproperty; } //重写旧方法 SubType.prototype.getSuperValue = function () { return false; }; let instance = new SubType(); alert(instance.getSuperValue());//false
通过原型链实现继承时不能使用对象字面量创建原型方法,这样会重写原型链
原型链的问题:
- 通过原型来实现继承时,原型实际上会变为另一个类型的实例,于是原先的实例属性也将变为现在的原型属性
- 在创建子类型的实例时,向超类型的构造函数中传递参数会影响到所有的对象实例
-
借用构造函数(也叫做伪造对象或经典继承)
function SuperType() { this.colors = ['red', 'black', 'blue']; } function SubType() { SuperType.call(this); } let instance1 = new SubType(); instance1.colors.push('white') alert(instance1.colors);//red,black,blue,white let instance2 = new SubType(); alert(instance2.colors);//red,black,blue
相比原型链的优势:可以传递参数
问题:无法实现函数复用。而且在超类型的原型中定义的方法,对于子类型也是不可见的
-
组合继承:
将原型链和借用构造函数相结合,也称为伪经典继承
function SuperType(name) { this.name = name; this.colors = ['a', 'b', 'c']; } 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); } let instance1 = new SubType("Apple", 20); instance1.colors = instance1.colors.concat(["black", 'white']); alert(instance1.colors);//a,b,c,black,white instance1.sayAge(); instance1.sayName(); let instance2 = new SubType("Orange", 30); alert(instance2.colors);//a,b,c instance2.sayAge(); instance2.sayName();
-
原型式继承:
let person = { name: "Nicholas", friends: ["shel", 'cout', 'van'] } let anotherperson = Object.create(person); anotherperson.name = "Greg"; anotherperson.friends.push("Rob"); let yetanotherperson = Object.create(person); yetanotherperson.name = "Linda"; yetanotherperson.friends.push("bar"); alert(person.friends);//shel,cout,van,Rob,bar,数组和Object类型会共享 alert(person.name);//Nicholas,数值、字符串等简单类型值会创建独立的副本 alert(anotherperson.name);//Greg
Object.create()
方法的第二个参数与Object.defineProperties()
方法的第二个参数格式相同:let anotherperson = Object.create(person, { name: { value: "Greg" } });
-
寄生式继承:
function createAnother(original) { let o = Object(original); o.sayhi = function () { alert("hi"); }; return o; } let person = { name: "Nicholas", friends: ["a", 'b'] } let a = createAnother(person); a.sayhi();
-
寄生组合式继承:
function inheritProtoType(subType, superType){ let prototype = Object(superType.prototype); prototype.constructor = subType; subType.prototype = prototype; }
-
这是我Javascript笔记中的一部分,我将陆陆续续把笔记都更新到博客上来。
如果您觉得我的文章对您有帮助的话,可以扫描下方二维码关注我。我将在这个公众号上更新自己的学习笔记,以及分享一些实用的软件!
Study and progress together with me!