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); }
相关文章推荐
- Ajax提交,对象转换成json,在js中解析?
- JS打开窗口问题
- 浅谈html转义及防止javascript注入攻击
- JSP九大隐式对象归纳总结
- JSP会话跟踪学习记录
- Jstl <c:forEach varStatus="status">中 varStatus的属性简介
- js弹窗总结
- JS 与OC 交互篇
- JSP之session
- js delete 用法(删除对象属性及变量)
- extjs中通过tpl实现带图标的ComboBox
- Extjs学习笔记(四) 数据代理
- 关于 Ajax中返回json类型数据为什么使用? eval()
- JS魔法堂:判断节点位置关系
- js框架
- js显示yyyy年mm日dd天 星期几 的格式日期
- 加载js 简单获取get传递参数
- javascript里的DOM和BOM
- JSP默认HTML4改为HTML5
- JS 倒计时