1、定义数组
无序属性的集合。数据结构
说白了就是一个容器,能够容纳【基本值、对象或者函数】,这些东西都叫作属性。每一个属性都有一个名字,每一个名字都映射一个值(能够是基本类型的值,也能够是引用类型的值)。从以上描述上来看很像Java里面的Map,但形似神不似,它有它的特色。app
2、对象的属性特色函数
一、 属性类型this
a) 数据属性prototype
数据属性包含一个数据值得位置,在这个位置能够读取和写入值,这类属性有四个描述符描述其特性设计
① 、Configurable:顾名思义是否可配置(经过delete删除属性,可否该变其为访问器属性),默认true。指针
② 、Enumerable:表示可否经过for-in循环返回属性,默认true对象
③ 、Writable:可否修改属性的值,默认true继承
④ 、Value:包含这个属性的数据值。读取属性的时候,从这个位置读,写入时把新值保存在这个位置。默认undefined。
以上能够理解为数据属性的数据结构,能够经过Object.defineProperty()方法来修改属性默认的特性,具体方法参照API。
b) 访问器属性
访问器属性不包含数据值,他们包含一对儿getter(读)和setter(写)函数(非必须)。特性也有4个,前两个跟数据属性一致,另外两个是
① 、Get:读取属性时调用的函数。默认undefined
② 、Set:写入属性时调用的函数。默认undefined
访问器属性只能经过Object.defineProperty()来定义,它长用于访问一些只能经过访问器属性访问的属性,一般这类属性如下划线’_’开头。
二、 Object.defineProperties()方法能够同时定义多个属性的特性。
三、 Object.getOwnPropertyDescriptor()方法,能够取得给定属性的描述符。
经过以上介绍,实现一个遍历对象属性以及属性特性描述符的功能就没什么问题了。
3、建立对象的方法
一、 简单方法
a) 构造函数定义后再追个追加属性var person = new Object();person.name=’xxx’;
b) 对象字面量定义 var person = {name:’xxxx’;};
优势:直观
缺点:若是我要建立不少相同的对象,会产生大量的重复代码。
二、 工厂模式
为解决1中的缺点产生的一种建立对象的方法
Function createPerson(name){
Var o = new Object();
o.name=name;
return o;
}
Var person1 = createPerson(‘xxxxx’);
Var person2 = createPerson(‘xxxxxxxxxx’);
这种模式解决了重复代码的问题,其实还有问题没有解决掉:实际应用中拿到变量极可能须要判断这个变量是哪种变量的类型,而后再进行不一样的处理逻辑。注意这里:变量的类型不要单纯的理解为ECMASript规定的6几种变量类型,要把它理解的更广义一点,向上面的代码其实就是想建立一种person的数据类型。
三、 构造函数模式
理解函数与构造函数的区别,不要想太多,直接从字面上去理解,构造函数也是一种函数,它本质上也是Object类型。当表达式中用【new 构造函数名(参数)】这种形式来使用这个函数时,他会返回一个新的对象,这个对象中的属性就是函数中定义的各类属性的镜像(除了prototype)。
Function Person(name){
This.name=name;
This.helloWold= function(){console.log(this.name)};
}
Var person1 = new Person(‘xxx’);
Var person2 = new Person(‘111’);
一般构造函数名首字母大写以区别于普通的函数。
Person一、person2分别保存着Person的一个不一样的实例,那么问题来了person1与person2是怎么和Person关联的呢?其实person1,person2中有一个constructor属性,是一个引用类型指向Person。
缺点:经过构造函数建立的对象都是不一样的对象,这些对象中包含了代码相同函数,也就是说在每个对象中都定义了逻辑相同的方法。这彷佛有点冗余的赶脚。
四、 原型模式
a) 原型对象的特色
每个函数都有一个prototype(原型)属性,它是一个引用类型,指向一个对象,这个对象的的特色是:包含了一些属性和方法,这些属性和方法属于某个类型的全部而且能够为类型的全部实例共用。是否是很像Java中类的静态变量,静态方法的意思啊?
Function Person(){};
Person.prototype.name=’xxxx’;
Person.prototype.hellworld= function(){console.log(this.name)};
Var p1 = new Person();
Var p2 = new Person();
Alert(p1.name);alert(p2.name);
神奇的发现经过prototype全部Person的实例共享了prototype上的属性。
在默认状况下全部原型对象都会自动得到一个constructor属性,这个属性包含一个执行prototype属性所在函数的指针。当调用构造函数建立一个实例后,该实例内部也会有一个prototype属性,该属性指向构造函数的原型对象。机:Person.prototype==person1.prototype==person2.prototype。
能够经过isPrototypeOf()方法来肯定对象之间是否存在这种关系。
ECMAScript5增长了一个新方法:Objec.getPrototypeOf()能够取得一个对象的原型。
HasOwnProperty()方法能够检测一个属性是存在于实例中仍是存在于原型中,这个方法只在给定属性存在于实例中时,才会返回true。这个方法与in操做符有区别,in操做符是只要可以经过实例访问到给定的属性时都返回ture,不管属性实在实例中仍是在原型中。
For in与Object.keys()均可以取得对象上全部可枚举的属性,前者包含了实例对象+原型对象上的属性,后者仅仅是实例对象属性。
Object.getOwnPropertyNames()方法能够获得对象的全部属性,不管属性是否可枚举。
b) 经过对象字面量设置对象的prototype。
经过对象字面量的方法设置prototype(本质上是重写默认的prototype对象)会切断prototype与对象之间的联系,即:constructor属性再也不指向对象了。可是能够经过显示指定的方式创建这种链接(即在对象字面量中定义一个constructor属性指向对象)。
c) 缺点:
原型中全部的属性都是被不少实例共享的,这种共享对于函数比较合适,对于那些只包含基本值得属性也OK,然而对于包含引用类型(好比数组)的属性来讲,某一个实例修改了这个原型属性,全部的实例都会受到影响。这恐怕在大多数应用中都是不想看到的,由于,实例通常都是要有属于本身的所有属性。
五、 组合使用构造函数和原型模式
这种模式解决了上面所说的问题,经过构造函数模式定义实例属性,而原型模式用于定义方法和共享属性。这样每一个实例都会有本身的一份实例属性的副本,同时又共享着对方法的引用,最大限度地节省了内存。
六、 动态原型模式
这种方式只是写法上与5不一样,它将prototype的定义写在了构造函数内部,完成了对prototype的内部封装。
七、 寄生构造函数模式
这实际上是一种适配器模式,在一个构造函数内部再次封装了对象。
八、 稳妥构造函数模式
所谓稳妥构造指构造函数内部没有公共的属性,并且其方法也不引用this的对象。与寄生构造模式类型,使用稳妥构造函数模式建立的对象与构造函数之间也没有什么关系,使用instanceof操做符对这种对象也没有什么意义。
4、对象的继承
继承是OO程序设计中一个基本的概念,通常经过接口继承和实现继承,接口继承只继承方法签名,而实现继承则继承实际的方法。
ECMAScript中没有方法签名(没有返回值类型,参数统一用arguments表示),因此只支持实现继承,依靠原型链来实现。
一、 原型链
a) 概念:
构造函数与实例之间的关系是,每一个实例都有一个属性prototype指向构造函数的prototype对象。
若是构造函数的prototype对象指向另外一个类型的实例对象,此时会发生什么现象?
① 构造函数的prototype对象与构造函数之间切断联系;
② 构造函数的prototype对象包含了一个指向另外一个类型的原型对象
经过这种prototype的指向咱们能够一直延伸下去,这样就构成了一条原型链条,这就是原型链的概念。
b) 缺点:
① 、在经过原型来实现继承时,原型实际上会变成另外一个类型的实例,因而,原先的实例属性也就变成了如今的原型属性了。这样新建立的全部实例可能会共享一些不该该共享的属性。与原型模式建立对象的问题同样。
② 在建立对象的实例时,没有办法再不影响全部对象实例的前提下向父对象的构造函数传递参数
二、 Constructor stealing
为解决上述问题,使用的一种技术手段,即在子对象的构造函数内部调用父对象的构造函数(经过使用apply()和call()方法,函数只不过是在特定环境中执行代码的对象,这个认识很强大)
Function SuperType(){this.colors=[‘aa’,’bb’,’cc’]};
Function SubType(){SuperType.call(this)};
Var instance1 = new SubType();
Instance1.colors.push(‘dd’);
Console.log(instance1.colors);// ‘aa’,’bb’,’cc’,’dd’
Var instance2 = new SubType();
Console.log(instance2.colors);// ‘aa’,’bb’,’cc’
可是如何解决构造函数带来的代码重复的问题?目前父对象原型中定义的方法对子对象是不可见的。
三、 构造函数+原型链模式(combination inheritance)
很天然的就想到经过这种方式来解决问题,经过原型链来实现对原型属性和方法的继承,经过借用构造函数来实现对实例属性的继承。
问题:子对象实例和原型上都存在父对象的实例属性,实例方法。
四、 寄生组合式继承
3中出现问题的缘由是执行了两次父对象的构造函数,即:子对象构造函数中调用一次,子对象prototype赋值又一次,两次重复部分就是父对象实例属性与实例方法。
缘由清除了,解决起来就不是很难了,在子对象原型赋值的时候赋一个父对象原型的实例就能够了。
Function inheritPrototype(subType, superType){
Var prototype = Object(superType.prototype);//建立父对象原型的实例
Prototype.constructor = subType;//构造器指向子对象
SubType.prototype=prototype;//制定子对象原型指向包装后的父对象原型
}
这段代码是否是很优雅啊~~~
本文属于我的读书总结,转载请注明出处