JavaScript高级程序设计4--面向对象的程序设计(上)
2016-12-06 16:59
309 查看
一、理解对象
1.属性类型
ECMA-262第5版在定义只有内部采用的特性时,描述了属性的各种特征。
ECMAScript中有两种属性:数据属性和访问器属性。
1)数据属性
数据属性有四个描述其行为的特性。
[[Configurable]]:表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。
[[Enumerable]]:表示能否通过for-in循环返回属性。
[[Writable]]:表示能否修改属性的值。
[[Value]]:包含这个属性的数据值。读取属性的时候,从这个位置读;写入属性的时候,把新值保存在这个位置,默认值为undefined。
要修改属性默认的特性,必须使用ES5的Object.defineProperty()方法。
Object.defineProperty():接收三个参数(属性所在的对象、属性的名字和一个描述符对象),其中描述符对象的属性必须为configurable、enumerable、writable和value。
2)访问器属性
访问器属性有如下4个特性。
[[Configurable]]:表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为数据属性。
[[Enumerable]]:表示能否通过for-in循环返回属性。
[[Get]]:在读取属性时调用的函数。默认值为undefined。
[[Set]]:在写入属性时调用的函数。默认值为undefined。
Object.defineProperties()方法可以通过描述符一次定义多个属性。
Object.getOwnPropertyDescriptor()方法可以取得给定属性的描述符。
接收两个参数:属性所在的对象和要读取其描述符的属性名称。
1.工厂模式
使用简单的函数创建对象,为对象添加属性和方法,然后返回对象。
2.构造函数模式
可以创建自定义引用类型,可以像创建内置对象实例一样使用new操作符。
3.原型模式
使用构造函数的prototype属性来指定那些应该共享的属性和方法。
1)理解原型对象
无论什么时候只要创建了一个新函数,就会根据一定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性是一个指向prototype属性所在函数的指针。
isPrototypeOf()方法:可以确定对象之间是否存在这种关系,即实例指向构造函数的原型对象。
hasOwnProperty()方法:可以检测一个属性是存在于实例中(返回true),还是存在于原型中(返回false)。
2)原型与in操作符
in操作符会在通过对象能够访问给定属性时返回true。
因此同时使用hasOwnProperty()方法和in操作符,可确定该属性是存在于对象中还是原型中。
Object.keys()方法:接收一个对象作为参数,可返回一个包含所有可枚举属性的字符串数组。
Object.getOwnPropertyNames()方法:可以获得所有实例属性,无论它是否可枚举。
3)更简单的原型语法
可以使用下面这种方式特意将它设置回适当的值。
因此使用Object.defineProperty()可解决上述问题。
实例中的指针仅指向原型,而不指向构造函数,因此重写原型对象时,已创建的实例仍然指向先前的原型对象。
5)原生对象的原型
所有原生引用类型(Object、Array、String等)都在其构造函数的原型上定义了方法,比如String.prototype中可以找到substring()方法,当然,你也可以通过原生对象的原型,定义新方法,不过,不推荐这么做。
6)原型对象的问题
不能为构造函数传递初始化参数
对于包含引用类型值的属性来说,实例不能拥有属于自己的全部属性
4.组合使用构造函数模式和原型模式
创建自定义对象的最常见方式,就是组合使用构造函数模式和原型模式。构造函数模式用于定义实例属性,而原型模式用于定义共享的属性和方法。
这个模式与工厂构造模式是一模一样的,它可以在特殊的情况下用来为对象创建构造函数。
7.稳妥构造函数模式
稳妥构造函数遵循与寄生构造函数类似的模式,但有两点不同:
新创建对象的实例方法不引用this;
不使用new操作符调用构造函数;
这种模式非常适合在某些安全执行环境下引用。
1.属性类型
ECMA-262第5版在定义只有内部采用的特性时,描述了属性的各种特征。
ECMAScript中有两种属性:数据属性和访问器属性。
1)数据属性
数据属性有四个描述其行为的特性。
[[Configurable]]:表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。
[[Enumerable]]:表示能否通过for-in循环返回属性。
[[Writable]]:表示能否修改属性的值。
[[Value]]:包含这个属性的数据值。读取属性的时候,从这个位置读;写入属性的时候,把新值保存在这个位置,默认值为undefined。
要修改属性默认的特性,必须使用ES5的Object.defineProperty()方法。
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"注意:一旦把属性定义为不可配置的,就不能再把它变回可配置了。
2)访问器属性
访问器属性有如下4个特性。
[[Configurable]]:表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为数据属性。
[[Enumerable]]:表示能否通过for-in循环返回属性。
[[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 //_year前面的下划线是一种常用的记号,表示只能通过对象方法访问的属性。2.定义多个属性
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(newValue){ if (newValue>2004){ this._year = newValue; this.edition += newValue - 2004; } } } });3.读取属性的特性
Object.getOwnPropertyDescriptor()方法可以取得给定属性的描述符。
接收两个参数:属性所在的对象和要读取其描述符的属性名称。
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");
alert(descriptor.value); //2004
alert(descriptor.configurable); //false
alert(typeof descriptor.get); //"undefined"
//读取访问器属性的特性
var descriptor = Object.getOwnPropertyDescriptor(book, "year");
alert(descriptor.value); //undefined
alert(descriptor.enumerable); //false
alert(typeof descriptor.get); //"function"
1.工厂模式
使用简单的函数创建对象,为对象添加属性和方法,然后返回对象。
function creatPerson(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("Nicholals", 29, "Software Engineer"); var person2 = createPerson("Greg", 27, "Doctor");缺点:没有解决对象识别的问题,即怎样知道一个对象的类型。这个模式后来被构造函数模式所取代。
2.构造函数模式
可以创建自定义引用类型,可以像创建内置对象实例一样使用new操作符。
function Person(name, age, job){ this.name = name; this.age = age; this.job = job; this.sayName = function(){ alert(this.name); }; } var person1 = new Person("Nicholals", 29, "Software Engineer"); var person2 = new Person("Greg", 27, "Doctor"); //按照惯例,构造函数始终都应该以一个大写字母开头,而非构造函数则应该以一个小写字母开头。将构造函数当做函数
//当做构造函数使用 var person = new Person("Nicholals", 29, "Software Engineer"); person.sayName(); //"Nicholals" //作为普通函数调用 Person("Greg", 27, "Doctor"); //添加到window window.sayName(); //"Greg" //在另一个对象的作用域中调用 var o = new Object(); Person.call(o, "Kristen", 25, "Nurse"); o.sayName(); //"Kristen"缺点:它的每个成员都无法得到复用,包括函数。
3.原型模式
使用构造函数的prototype属性来指定那些应该共享的属性和方法。
1)理解原型对象
无论什么时候只要创建了一个新函数,就会根据一定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性是一个指向prototype属性所在函数的指针。
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(); person1.sayName(); //"Nicholas" alert(person1.sayName == person2.sayName); //true下图展示了以上使用Person构造函数和Person.prototype创建实例的代码的各个对象之间的关系:
isPrototypeOf()方法:可以确定对象之间是否存在这种关系,即实例指向构造函数的原型对象。
alert(Person.prototype.isPrototypeOf(person1)); //true alert(Person.prototype.isPrototypeOf(person2)); //trueObject.getPrototypeOf()方法:返回[[Prototype]]的值。
alert(Object.getPrototypeOf(person1) == Person.prototype); //true alert(Object.getPrototypeOf(person1).name); //"Nicholas"注意:在实例中添加一个与实例原型中同名的属性,那么该属性将会屏蔽原型中的那个属性。只有使用delete操作符完全删除实例属性,才可以重新访问原型中的属性。
hasOwnProperty()方法:可以检测一个属性是存在于实例中(返回true),还是存在于原型中(返回false)。
2)原型与in操作符
in操作符会在通过对象能够访问给定属性时返回true。
因此同时使用hasOwnProperty()方法和in操作符,可确定该属性是存在于对象中还是原型中。
function hasPrototypeProperty(object, name){ return !object.hasOwnProperty(name)&&(name in object); }在使用for-in循环时,返回的是所有能够通过对象访问的、可枚举的属性。
Object.keys()方法:接收一个对象作为参数,可返回一个包含所有可枚举属性的字符串数组。
Object.getOwnPropertyNames()方法:可以获得所有实例属性,无论它是否可枚举。
3)更简单的原型语法
function Person(){ } Person.prototype = { name: "Nicholas", age: 29, job: "software Engineer", sayName: function () { alert(this.name); } };上面的原型写法本质上完全重写了默认的prototype对象,因此constructor也就变成了新对象的constructor属性,不再指向Person函数。
可以使用下面这种方式特意将它设置回适当的值。
function Person(){ } Person.prototype = { constructor: Person, name: "Nicholas", age: 29, job: "software Engineer", sayName: function () { alert(this.name); } };但是,以这种方式重设constructor属性会导致它的[[Enumerable]]特性被设置为true,默认情况下,原生的constructor是不可枚举的。
因此使用Object.defineProperty()可解决上述问题。
function Person(){ } Person.prototype = { name: "Nicholas", age: 29, job: "software Engineer", sayName: function () { alert(this.name); } };//重设构造函数,只适用于ES5兼容的浏览器4)原型的动态性
Object.defineProperty(Person.prototype, "constructor", {
enumerable: false,
value: Person
});
实例中的指针仅指向原型,而不指向构造函数,因此重写原型对象时,已创建的实例仍然指向先前的原型对象。
5)原生对象的原型
所有原生引用类型(Object、Array、String等)都在其构造函数的原型上定义了方法,比如String.prototype中可以找到substring()方法,当然,你也可以通过原生对象的原型,定义新方法,不过,不推荐这么做。
6)原型对象的问题
不能为构造函数传递初始化参数
对于包含引用类型值的属性来说,实例不能拥有属于自己的全部属性
4.组合使用构造函数模式和原型模式
创建自定义对象的最常见方式,就是组合使用构造函数模式和原型模式。构造函数模式用于定义实例属性,而原型模式用于定义共享的属性和方法。
function Person(name, age, job){ this.name = name; this.age = age; this.job = job; this.friends = ["Shelby", "Court"]; } Person.prototype = { constructor:Person, sayName: function () { alert(this.name); } } var person1 = new Person("Nicholals", 29, "Software Engineer"); var person2 = new Person("Greg", 27, "Doctor"); person1.friends.push("Van"); alert(person1.friends); //"Shelby,Count,Van" alert(person2.friends); //"Shelby,Count" alert(person1.friends === person2.friends); //false alert(person1.sayName === person2.sayName); //true5.动态原型模式
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("Nicholals", 29, "Software Engineer");friend.sayName();6.寄生构造函数模式
这个模式与工厂构造模式是一模一样的,它可以在特殊的情况下用来为对象创建构造函数。
7.稳妥构造函数模式
稳妥构造函数遵循与寄生构造函数类似的模式,但有两点不同:
新创建对象的实例方法不引用this;
不使用new操作符调用构造函数;
这种模式非常适合在某些安全执行环境下引用。
相关文章推荐
- js怎么把数字金额转换中文大写金额
- 常用插件GsonFormat---json数据格式
- js事件处理
- 如何使用firefox进行网页js调试
- Duilib嵌入CEF以及JavaScript与C++交互
- vc6.0使用Json-cpp的配置方法
- javascript---4
- 工作中遇到的问题总结2(js类)
- 你不知道的javascript之函数作用域和块作用域(一)
- JavaScript高级程序设计3--引用类型(下)
- JavaScript设计模式与开发实践 中介者模式
- JavaScript-合同到期续约案例
- js实现网页瀑布流布局
- json字符串构造为对象
- 浅析JavaScript中作用域和作用域链
- 解析自定义公式是否合法
- gradle Reading and writing JSON
- js 防止表单多次重复提交
- js按钮获取表单内容
- jsp动态显示系统时间页面