标签:集合 括号 person desc 引擎 通过 png 包含 描述
理解对象属性
ECMA-262称对象为:无序属性的集合,其属性可以包含基本值,对象或者函数。
由此在ECMAScript中可以把对象想象成散列表,无非就是键值对,值可以为数据或者函数。
//两种定义对象的方式 var person = new Object(); person.name="xiaoming"; person.age=29; person.showName = function(){ alert(this.name); }
var person = { name:"xiaoming", age:29, showName:function(){ alert(this.name); } }
属性类型
有些特性定义是为了实现JavaScript引擎用的。为了表示特性时内部值,ECMA-262规范把他们放在两对方括号中,例如:[ [ Enumerable ] ]。
ECMAScript中有两种属性:数据属性和访问器属性。
数据属性 有以下描述行为的特性(多数情况下很少用到这些高级功能)
修改以上属性的默认特性使用Object.defineProperty()方法
var person={} //接收三个参数:属性所在的对象,属性的名字和配置特性的对象 Object.defineProperty(person,"name",{ writable:false, value:"xiaoming" })
访问器属性 包含一对getter和setter函数。有如下4个特性:
1 var book = { 2 _year:2017, 3 edition:1 4 } 5 6 Object.defineProperty(book,"year", 7 { 8 get:function() 9 { 10 return this._year; 11 } 12 set:function(newValue) 13 { 14 if(newVaule>2017) 15 { 16 this._year = newValue; 17 this.edition+=newVlaue-2017 18 } 19 } 20 }) 21 22 //定义多个属性 23 var book2 ={}; 24 Object.defineProperties(book2, 25 { 26 _year: 27 { 28 writable:true, 29 value:2004 30 }, 31 32 edition: 33 { 34 writable:true, 35 value:1 36 }, 37 38 year: 39 { 40 get:function() 41 { 42 return this._year; 43 } 44 set:function(newValue) 45 { 46 if(newVaule>2017) 47 { 48 this._year = newValue; 49 this.edition+=newVlaue-2017; 50 } 51 } 52 } 53 })
若想要读取属性的某些特性,可以利用Object.getOwnPropertyDescriptor()方法,接收两个参数:属性所在的对象和要读取其描述的属性名称。
创建对象
问题一: 前面通过Object构造函数或对象字面量的形式创建单个对象,但是这种方式使用同一接口创建很多对象,产生大量重复代码。
解决方法:工厂模式,用函数来封装以特定接口创建对象的细节。
具体实现:函数createSchool()能够根据接收的参数来构建一个包含所有必要信息的School对象。
function createSchool(NO,name,address) { var obj = new Object(); obj.No = NO; obj.name = name; obje.address = address; return obj; }
问题二:工厂模式解决了创建多个相似对象的问题,但是没有解决对象识别问题,不知道对象是什么类型,如上面的createSchool方法,得不到创建的类型是School类型。
解决方法:构造函数模式。创建自定义的构造函数,从而自定义对象类型的属性和方法。
具体实现:与工厂模式区别在于没有显示的创建对象,直接将属性和方法赋给了this对象,没有return语句。
function School(No,name,address) { this.No = No; this.name = name; this.address = address; this.showName = function(){ console.log(this.name); } }
要创建新实例要使用new操作符,会经历四个步骤
创建后的对象都会有一个constructor属性,该属性指向School。通过利用创建的实例对象school1.constructor == School来检测对象类型。比较可靠的方法还是使用
school1 instanceof School方式。另外构造函数其实也就是JS中简单的函数,当通过new操作符调用时,作为构造函数来使用,否则它也就是简单的函数。
问题三:构造函数虽然解决了工厂模式的缺陷,但是对于每个方法都需要在每个实例上重新创建一遍。也就是说对于上面School中的showName方法,其实等价于this.showName = new Function("console.log(..)");
也就是又实例化了一个对象。以这种方式会导致不同作用域链和标识符解析,不同实例上的同名函数是不相等的,不能实现共享。例如:school1.showName==school2.showName 返回的是false.
解决方法:将函数定义放在构造函数的外部。
function School(No,name,address) { this.No = No; this.name = name; this.address = address; this.showName = showName; } function showName(){ console.log(this.name); }
问题四:将方法定义外面,确保了实例对象调用了相同的事件,但是在全局作用域中定义函数使得在任意地方均可调用,没有封装性可言。
解决方法:原型模式。每个函数都有一个prototype(原型)属性,它是一个指针,指向一个对象(原型对象),用途可以将想要实例间共享的属性和方法定义在这个对象上。
function School(){} School.prototype.No = 1001; School.prototype.name = "xxx"; School.prototype.showName = function(){ console.log(this.name); } var school = new School(); school.showName(); var school2 = new School(); school2.showName(); console.log(school.showName==school2.showName);//true
知识点:
School.prototype = { //为了使自动获取的constructor指向School, //因为通过这种原型语法constructor等于Object,需要手动设置如下 constructor:School, name:"", showName:function(){ console.log(this.name); } }
问题四:通过原型模式中原型对象的方式,会导致所有实例在默认情况下都将取得相同的属性值,造成了不便。同时由于他的共享性本身,对于引用类型的值来说问题较大,因为引用类型保存的是一个指针,共享导致某个实例改变改属性会同步到其他实例中。
解决方法:将构造函数模式和原型模式组合使用:构造函数模式用于定义实例属性,而原型模式用于定义方法和需要共享的属性。 使用最广泛的一种方法。
function School(No,name) { this.No = No; this.name = name; this.Classes=["一年级","二年级"]; } School.prototype = { constructor:School, showName:function(){ console.log(this.name); } }
继承
说明:ECMAScript支持实现继承,主要依靠原型链
原型链方法:利用原型让一个引用类型继承另一个引用类型的属性和方法。
function SuperType() { this.supername="super"; } function SubType() { this.subname = "sub"; } SubType.prototype = new SuperType(); var instance = new SubType(); alert(instance.subname);
解析:在上述代码中,没有使用SubType的默认提供的原型,而是替换为SuperType的实例。新原型不仅作为基类(SuperType)的实例而具有全部的属性和方法,并且其内部还有一个指针,指向了SuperType的原型。从而instance指向SubType的原型,SubType的原型又指向SuperType的原型。通过之前讲过的,原型搜索机制,沿着原型链继续向上,直到找到指定的方法或属性。
补充:
原型链的问题:前面讲过,通过原型链的形式会使得引用类型值的原型属性被所有实例共享,从而造成不便。另外创建子类的实例时,不能向超类型的构造函数传递参数。看如下代码
function SuperType() { this.colors = ["red","blue"]; } function SubType() { } SubType.prototype = new SuperType(); var instance = new SubType(); instance.colors.push("green"); console.log(instance.colors);//red,bule,greed var instance2 = new SubType(); console.log(instance2.colors);//red,bule,greed
解决方法:借用构造函数,解决原型中包含引用类型值所带来的问题
实现思路:在子类型构造函数的内部调用超类型构造函数。使用apply()或call()方法,只要将上面的代码中SubType函数里加入SuperType.call(this);即可。同时也可以传递参数,如下
function SuperType(name) { this.name = name; } function SubType() { SuperType.call(this,"jack"); } var instance = new SubType(); console.log(instance.name);//jack
借用构造函数方法的问题:仅仅使用这一模式无法使函数复用。而且在基类中定义的方法,对于子类型而言是不可见的。
解决方法:组合继承。
实现思路:使用原型链实现对原型属性和方法的继承,通过借用构造函数来实现对实例属性的继承。
function SuperType(name) { this.name = name; this.colors = ["red","blue"]; } SuperType.prototype.showName = function() { console.log(this.name); } function SubType(name,age) { SuperType.call(this,name); this.age = age; } //继承形式 SubType.prototype = new SuperType(); SubType.prototype.constructor = SubType; //给子类添加方法 SubType.prototype.showAge = function() { console.log(this.age); }
组合继承避免了原型链和借用构造函数的缺陷,融合了优点。
组合继承问题:其无论在什么情况下,都会调用两次超类型构造函数:一次是在创建子类型原型的时候(SubType.prototype = new SuperType())。另一次是在子类型构造函数内部(SuperType.call(this,name))。这种情况会造成两组name和colors属性,实例上和SubType原型中。
解决方法:寄生组合式继承
思路:通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。我们所需要的就是超类型的副本。所以这种模式的基本代码结构如下
function SuperType(name) { this.name = name; this.colors = ["red","blue"]; } SuperType.prototype.showName = function() { console.log(this.name); } function SubType(name,age) { SuperType.call(this,name); this.age = age; } //将基类与子类联系在一起 inheritPrototype(subType,superType); //然后给子类添加方法 SubType.prototype.showAge = function() { console.log(this.age); } function inheritPrototype(subType,superType) { var prototype = Object(superType.prototype); prototype.constructor = subType; subType.prototype = prototype; }
寄生组合式继承,只调用一次SuperType构造函数,避免在SubType.prototype上创建不必要,多余的属性,原型链还能保持不变。
总结:ECMAScript支持面向对象编程,使用如下模式创建对象:
JavaScript主要通过原型链实现继承。原型链的构建是通过将一个类型的实例赋值给另一个构造函数的原型实现的。这样,子类型就能够访问超类型所有属性和方法。原型链的问题是对象实例之间共享所有继承的属性和方法,不适宜单独使用。配和借用构造函数,即在子类型构造函数内部调用超类型构造函数。原型链继承共享的属性和方法,借用构造函数继承实例属性。另外还有寄生组合式继承,原型继承,寄生式继承等。
参考:《JavaScript高级程序设计》
标签:集合 括号 person desc 引擎 通过 png 包含 描述
原文地址:http://www.cnblogs.com/mjys-gh/p/7198068.html