Javascript程序设计 Chapter6 - 面向对象(一)
对象概念
数据属性和访问器属性
数据属性
理解:可以直接访问和设置的属性、
(直接定义在对象上的属性的特性默认为:true, value为属性值)- Configurable : 是否可以delete/修改特性/改为访问器属性
- Enumerable : 是否能用for-in返回属性
- Writable : 值是否可以修改
- Value : 属性的数据值,默认为:undefined
var person = {}; // 数据属性 Object.defineProperty(person, "name", { configurable: true, enumerable: false, writable: true, value: "gccll" }); alert(person.name); person.name = "gcc"; // writable为false时无法修改值 alert(person.name);
访问器属性(可以通过改变一个属性的值来改变另一个属性的值)
- Configurable:同上
- Enumerable:同上
- Get: 读取属性值时调用
- Set:设置属性值时调用
var person = { _name: "gccll", age: 18 } // 添加访问器属性name, 可以通过设置该属性的值,从而调用该属性的特性方法:setter中去处理另一个属性的值age Object.defineProperty(person, "name", { // 定义时只有setter和getter方法 get: function() { return this._name; }, set: function(newName) { if (newName == "gcl") { this._name = newName; this.age = 20; } } }); person.name = "gcl"; alert(person.age);
同时定义多个属性 Object.defineProperties(person, {/* 定义属性同时设置特性 */})
var person = {}; Object.defineProperties(person, { // 数据属性 _name: { // 通过此方法而非直接添加的属性,默认特性都为false value: "gccll", writable: true // 可改值 }, age: { configurable: true, // 可删 value: 18 }, // 访问器属性 name: { get: function() { return this._name; }, set: function(newName) { if (newName == "gcl") { this._name = newName; this.age = 20; } } } }); // alert(person._name); person.name = "gcl"; alert(person.age); delete person.age; alert(person.age); // alert(person._name);
读取特性的方法:Object.getOwnPropertyDescription(obj, attr), 返回值为特性对象,包含四个属性(如果是数据属性:configurable, enumerable, writable, value, 访问器属性:configurable, enumerable, get, set)
var person = {}; Object.defineProperties(person, { // 数据属性 _name: { // 通过此方法而非直接添加的属性,默认特性都为false value: "gccll", writable: true // 可改值 }, age: { configurable: true, // 可删 value: 18 }, // 访问器属性 name: { get: function() { return this._name; }, set: function(newName) { if (newName == "gcl") { this._name = newName; this.age = 20; } } } }); // person: 需要取属性的对象,"_name":取特性的属性 // 数据属性 var descriptor = Object.getOwnPropertyDescription(person, "_name"); alert(descriptor.configurable); // false alert(descriptor.get); // "undefined", 数据属性没有setter和getter方法,所以结果未定义 alert(descriptor.writable); // true alert(descriptor.value); // "gccll" // 访问器属性 var descriptor = Object.getOwnPropertyDescription(person, "name"); alert(descriptor.configurable); // false alert(descriptor.enumerable); // false alert(descriptor.set); // "function" alert(descriptor.writable); // "undefined", 访问器属性没有此特性,所以结果未定义
原型 prototype
函数一旦创建就会存在一个prototype属性,即一个指针指向一个包涵可以由所有实例共享的数据属性或方法(可以理解为原型即一个对象的本质,不论创建多少个实例,每个实例的本质都是一样的)。
原型属性和方法的定义
属性和方法的搜索顺序:首先搜索实例对象,没找到的情况下再去搜索原型中是否存在,可以参考代码中的结果得知。
// 构造函数 function Person() { } // 创建原型属性和方法 Person.prototype.name = "gccll"; Person.prototype.age = 30; Person.prototype.sayName = function() { alert(this.name); // this指向该原型对象 } var p1 = new Person(); p1.sayName(); // 输出仍然为“gccll",因为此处调用的是所有实例共享的方法和属性 // 可以通过在实例上添加与原型中同名的属性或方法来覆盖掉原型中的属性或方法 var p2 = new Person(); p1.name = "wife"; alert(p1.name); // 取得的依然是原型中的name, 所以结果依然是:"gccll" alert(p2.name); // 此处p2的name已经被重写或覆盖掉了,输出结果为实例对象的name属性值:"wife" // 可以通过delete来删除实例属性,从而恢复原型中被覆盖的属性作用 delete p1.name; alert(p1.name); // 结果:"gccll"
判断实例对象是否包含某一属性方法:obj.hasOwnProperty(“attrName”)
// 继续上例 ///////////// alert(p1.hasOwnProperty("name")); // false; 上例中已经用delete将name属性删除了 p1.name = "lingling"; alert(p1.hasOwnProperty("name")); // true
原型与in操作符
in单独使用时,可以用来判断属性是否存在与实例或原型对象中,只要其中一个有该属性则返回true,如:
alert("name" in p1); // 如果实例p1中没有,但原型中有,依然返回true, 只有两者之中都没有才返回false
in可以和hasOwnProperty联合使用,用来判断属性到底是原型的还是实例的, 如:
!obj.hasOwnProperty("name") && ("name" in obj)
判断原型属性的方法:hasPrototypeProperty(obj, attr)
function hasPrototypeProperty = function(obj, attr) { return !obj.hasOwnProperty("name") && ("name" in obj); }
for-in循环,遍历对象中属性,包括方法
var p = { name: "lingling", age: 24, // 屏蔽掉了原型中的Enumerable==false的toString toString: function() { return this.name + this.age; } } // 遍历对象属性和方法 for (var tmp in p) { if (o == "toString") { p[o](); } else { alert(p[o]); } } // 结果 // "lingling" // 24 // "lingling24"
- Object.keys(obj) 获取对象obj中可以进行枚举的属性或方法,返回一个数组包含对象中所有能被枚举的属性或方法的字符串,如上例:返回结果为:[“name”, “age”, “toString”]
简化原型语法
相当于重写了原型对象,所以指向构造函数的指针也不存在,如果要重现,则可以在重写的原型对象中重新定义且将其指向构造函数
function P() { } // 字面量法重写原型对象,失去构造函数指针 P.prototype = { constructor: P, // 指向构造函数的指针,默认Enumerable为false name: "lingling", age: 24, sayname: function() { return this.name; } }
构造函数与原型的结合使用(推荐)
原理:一般将属性定义在构造器中,使每个对象实例都具有自己的属性,方法和共享属性使用原型定义
function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.friends = ["John", "Tom"]; } // 共享属性 Person.prototype = { constructor: Person, sayName : function() { alert(this.name); } }; // 通过构造器创建实例对象的同时初始化对象 var p1 = new Person("Nic", 12, "Student"); var p2 = new Person("Jack", 23, "Teacher"); p1.friends.push("Tim"); alert(p1.friends); // John, Tom, Tim alert(p2.friends); // John, Tom