红宝书 第6章整理——创建对象

来源:互联网 发布:python处理ajax请求 编辑:程序博客网 时间:2024/05/20 08:25

1、理解对象+对象属性

面向对象语言有一个标志,就是他们都有类的概念。通过类来创建多个具有相同属性的方法和对象。
但是!!!js中没有类的概念,所以js中所谓的面向对象有些不同。
js中的对象可以理解成一组组的名值对(属性 + 值),其中的值可以是数据或者函数。每个对象都是基于一个引用类型创建的,详见第5章。
创建对象可以直接用原生类型(new array,new function....) 也可以自己创建(new object)

属性

1、分类

对象的属性,可以分为两种:数据属性、访问器属性

1、数据属性

即在对象中显式的写出来的属性。可以用Object.defineProperty()函数来对其进行设置,例如该属性是否可删除,可修改等等
var person = { };
Object.defineProperty(person,"name",{
writable:false,  //设置不可写,就是只读的
value:"jack"});
writeable(只读),configurable(删除,注意这个属性一旦改成了false就改不回来了),enumerable(可列举)
若想同时该多个属性的设置,可用defineProperties()函数。

2、访问器属性

这种属性不在对象定义中显式的写出来,而且该属性都是方法函数,不是值。只能通过Object.defineProperty()函数来设置,有点像后台处理的方法
有get和set可以设置,分别对应写入时和读取时的操作。

2、创建对象

当要写一个类来创建多个对象时:
分类:工厂模式
           构造函数模式
           原型模式
           组合使用(构造+原型)
           动态原型模式
           .....

① 工厂模式:

  “工厂模式” 是一种设计模式,就是把创建过程整个进行封装,直接上代码:

function creatPerson(name , age){  var o = new Object();   o.name = name;   o.age = age;   o.say = function(){     alert(this.name);};   return o;}var person1 = creatPerson(jack,20);var person2 = creatPerson(tom.30);

可以看出来在这种模式下,是最无脑的简单的批量创建方法,但是最大的问题是这样创建的对象仅仅属于Object的实例算是原生对象的实例,不算是用户自定义的类的实例

所以进行改进——构造函数模式

② 构造函数模式

function Person (name , age) {  this.name = name;  this.age = age;  this.say = function(){   alert (this.name);};}var person1 = new Person(jack,20);var person2 = new Person(tom.30);

改进之处:

   1、在function中没有显示的创建对象,即new object() ,在想创建对象时再new

   2、在function中用this来指代对象(因为function中没有显式创建)

   3、function中不用return

   4、函数名一般开头字母大写,算是惯例

优点:

  1、 这种方法创建的实例算是自定义类的实例,此时person1 与person2 不仅仅是object的实例,还是Person的实例 。(所有对象都要继承自object啊)

   2、不算优点的优点,构造函数还可以当成普通函数用。

        当构造函数用时,要new来创建

        当普通函数时,不加new,直接Person(jack,20);  此时函数变成了全局函数,this指代的是window,所以window会被添加name,age。

        当普通函数用时,就可以配合call,apply来调用,比如作用在一个对象上,也相当于为这个对象添加 了Person的属性和方法

缺点:

    每次实例化,函数中所有的属性和方法都会被重新实例化——即每个新对象中的name,age,function say,都是彼此不同的,不会互相干扰。

     对于属性来说可能这点是好处,这样可以对每个不同的对象进行后续的修改

     但是对于方法来说不好,每次完成同样功能,但是却每次都被重置

一个改进:

   把类中的方法拿到构造函数外面写。

  

function Person(name,age) {  ...  ...  var say = this.say;   //在构造函数内部只声明不实现}function say(){  alert(this.name);}

这样就可以避免方法也跟着被重置了。

但是!!! 还是不好!!  当类中有很多方法时,全放到外面写,就破坏了对象的封装性

继续改进——原型模式

③ 原型模式

关键词:共享

我们创建一个函数都会自带一个prototype(原型)属性,这个属性是一个指针,指向这个函数的原型对象。

这样的话,我们给对象的原型添加的属性,方法,在实例化的时候就可以被继承共享了。

function Person(){}       //不必在构造函数中定义任何属性,方法Person.portotype.name = "jack";       //在构造函数外面,用prototype添加Person.portotype.age = 20;Person.portotype.say = function(){   alert(this.name);};var person1 = new Person();var person2 = new Person();

此时person1 与person2 所有的属性,方法,全是指向同一个,即原型对象中的name,age,function say。

1、理解原型对象

函数的原型对象最初都有一个属性:constructor。 (默认是不可枚举的,就是在for-in中不会被列出)

这个属性代表这个原型对象的构造函数,可以显式的设置一下,例如Person.prototype.constructor = Person。 双重保障。

用prototype方法添加的属性,方法,实际上都不属于Person类,但是new Person的实例可以用,这是因为会自动向上索引。Person中没有,会自动往上原型类中找。Person中有,则用它的。

若用户为Person类添加了与prototype类中同名的属性,方法,那么调用时会优先用Person类的值。 注意prototype中的值没有变,当Person类中这个属性被delete时,还是会向上索引至prototype类。

2、in操作符

单独使用:

 语法:    “属性” in 对象           // “name” in person1;     

 返回:只要这个属性在这个对象中能调用,就返回true。无论它是在Person中,还是在Person.prototype中

for-in

   会返回所有通过这个对象能使用的,可枚举(之前说过可以将enumerated = false)的属性。无论Person还是Person.prototype中的

3、简化写法

利用字面量的写法

function Person(){}Person.prototype = {  name:"jack",  age:20,  say: function(){     alert(this.name); };}

缺点:

这种写法实际上完全重写了prototype对象,这导致了constructor属性不会指向原Person类,此时值没有了。 虽然这对创建对象没什么影响,但是若使用顺序颠倒时,会出错,详见下面的动态性。

如果constructor的值很重要,那么可以在字面量中显式的添加     constructor:Person,此时constructor会变为可枚举的,在for-in中可见。注意,即使用constructor写了指向Person,这个Person也是新Person,非原来的!!这种方法就是会切断,连接不回来!!

4、原型的动态性

当不用字面量方法简写时,创建对象可以放在创建类的前面。因为js中实际没有类的概念,是索引,遇到创建对象它会自动在上下文索引。

但是当用字面量方法简写时,顺序就不能颠倒了,因为会重新创建一个Person类。

5、原生对象的原型

prototype不仅仅可以用在自定义的类,还可以用在原生的类中,比如array,string。

可以为他们添加新方法。  Array.Prototype.newmethod  = function(){....};

6、优缺点

① 由于在prototype中统一设置属性和值,导致所有实例在默认状况下初始化的值都一样

② 最大的问题:引用类型值的原型属性会被所有实例共享。即,若属性的值时引用类型,例如数组时,当有一个实例化对象对其进行修改,那么prototype中的值也会被修改,其他实例化的这个属性也会被改。 之前说的不改的情况是普通数值类型值。


但是prototype的共享,特别适用于对象的方法,即函数,但是并不适合属性,即值

所以继续改进——组合使用

④组合使用(构造+原型)

这种方式是用户创建自定义类型最常见的。

用构造函数方法定义属性——构造函数式每一次实例化都会为不相同的,所以各个实例的属性可以再次自由定义

用原型模式定义方法 + 一些可以共享(即不做个性化设置的)属性——原型模式对于方法共享,每次实例化不用重新创建,节省内存。

//构造函数中的属性可以被分别再赋值function Person(name , age , job){   this.name = name;   this.age = age;} //原型模式中的方法可以被共享Person.prototype = {  constructer : Person;    //保险起见,可以显式的写一下归属  say : function(){  alert(this.name);}}

优点:

节省内存,集两个方法的优点于一身

缺点:

其实没啥缺点,硬要说的话就是把一个类拆成了两段写,不方便看。所以又有了进一步的改进——动态原型模式

⑤动态原型模式

由于之前分段写不好看,这个模式其实就是简单的把这两段(构造函数部分+原型模式部分)捏在了一起。

通过一个if语句判断。如果对象中没有该方法,就用原型模式创建一个,接下来每次再实例化的时候继续判断,如果已经有了这个方法,就不再创建了。见下:

function Person(name , age ){  //正常在构造函数中写属性    this.name = name;    this.age = age;  // 接下来判断,用原型模式加方法  if(typeof this.say != "function"){     //只有初次创建时这个方法不存在,在原型中创建。接下来再创建对象,在原型中已有,就跳过了     Person.prototype.say = function(){    alert(this.name);}}}var friend = new Person("jack", 20);friend.say();

当有多个方法时,判断一个就可以,其他的都在这个方法的判断下添加,反正都是一起在初次被创建。

注意:

在这种方式下,原型模式不可以用简写模式(即字面量重写),因为会重新创建一个Person,与已经写好的name,age分别属于不同的Person,切断了联系。


0 0
原创粉丝点击