JavaScript面对对象(上)

来源:互联网 发布:c语言经典笔试题 编辑:程序博客网 时间:2024/05/06 21:28

JavaScript对象定义

ECMAScript定义对象:”无序属性的集合,其属性可以包括基本值、对象、方法”。
也就是说JavaScript对象的实质是没有特定顺序值得集合,相当于散列表。

属性类型

ECMAScript 5 定义了只有内部才可以使用的特性,用来描述属性的特征。目的是为了实现JavaScript引擎,因此不能直接使用。为了表示是内部值,用[[]]表示。

数据属性
  • [[ Configurable ]]:用来表示是否可以通过delete删除属性并重新定义。是否可以修改属性特性。是否可以修改为访问器属性。
  • [[ Enumerable ]]:是否可枚举(即通过for-in访问)。
  • [[ Writable ]]:表示是否可以修改属性的值。
  • [[ Value ]]:包含保存属性的数据值。
    要修改对象的默认特性,必须使用Object.defineProperty()方法,该方法接受三个参数:属性所在对象 属性名字 描述符对象
    例如:
    1
    2
    3
    4
    5
    var person={};
    Object.defineProperty(person,"name",{
    writable:false,
    value:"MrErHu"
    });

在制定了configurable:false后,再也无法修改除”writable“之外的特性。在不指定特性的情况下,writableconfigurableenumerable默认为false。

访问器属性

访问器属性的四个特性:

  • [[configurable]]:能否通过delete删除属性继而重新定义。能否修改特性。能否修改为数值属性。
  • [[enmuerable]]:可枚举。默认为true
  • [[Get]]
  • [[Set]]
    访问器属性不能直接定义,必须通过Object.defineProperty()定义。
    下面举例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    var book={
    _year:2004;
    edition:1
    }
    Object.defineProperty(book,"year",{
    get:function(){
    return this._year;
    },
    set:function(newValue){
    if(newValue>2014){
    this._year=newValue;
    }
    }
    });

在ECMAScript5之前还有两个非标准方法:

  • __defineGetter__()
  • __defineSetter__()
    如果需要为对象定义多个属性,可以调用函数Object.defineProperties()方法
    我们在ECMAScript5中使用方法:Object.getOwnPropertyDescriptor()取得属性的描述符,方法接受两个参数,一个是对象,另一个是属性名称,返回一个对应的对象。

创建对象

工程模式

工厂模式可以抽象具体创建对象的过程:

1
2
3
4
5
6
7
8
9
10
function createPerson(name,age,sex) {
var object=new Object();
object.name=name;
Object.age=age;
Object.sex=sex;
Object.getName=function () {
return this.name;
}
return object;
}

工厂方式只能解决创建多个相似对象的问题,却不能解决对象识别的问题。

构造函数模式

构造函数创建对象的例子如下:

1
2
3
4
5
6
7
8
9
function Person(name,age,sex) {
this.name=name;
this.sex=sex;
this.age=age;
this.getName=function () {
return this.name;
}
}
var person=new Person("MrErHu","22","man");`

构造函数模式和工厂方法不同之处在于:

  • 没有显式的创建对象
  • 直接将属性和方法赋予this对象
  • 没有return语句

通过构造方法调用函数的四个步骤:

  • 创建一个对象
  • 将函数的作用域复制给对象
  • 执行函数中的方法
  • 返回新的对象

通过构造函数方法产生的对象,其中有一个属性constructor,指向构造函数,因此既可以通过constructor查找类型,也可以利用instanceof来查找类型。

上面的构造函数无非是通过new操作符调用的,如果Person()函数不通过new调用,相当于在全局调用。

构造函数的缺点:实例的每一个方法都属于的函数对象。

原型模式

原型对象

  每个函数都有一个prototype属性。指向一个对象,可以用来由特定类型的所有实例共享的属性和方法。这样可以让所有对象实例共享它所包含的属性和方法。
  在默认情况下,每一个原型对象有会一个constructor属性,指向构造函数。在通过构造函数创建了实例后,每一个实例都会包含一个内部属性[[Prototype]]指向原型对象,在脚本中没有标准的方式可以访问到,但是Firefox、Safari、Chrome都支持类似的属性__proto__
  虽然在实现中没法访问到[[Prototype]],但是可以通过isPrototypeOf()来确定对象之间是否存在关系。例如:如果person是Person实例,那么Person.prototype与person的关系可以通过下面的例子来测试:

1
alert(Person.prototype.isPrototypeOf(person))// alert(true)

  ECMAScript5新方法:

1
Object.getPrototypeOf()//用来返回[[prototype]]

  JavaScript搜寻属性的过程是逐级上升的,即如果在实例对象中可以查找到对应属性则使用,否则继续搜索原型对象没如果在原型对象中找到了这个属性的值,则返回该属性的值。我们可以在实例中访问到原型中的对象,但是不能重写,因为在实例中添加属性,相当于在实例中创建了同名属性,无法修改原型中的属性。
  在JavaScript中可以使用方法hasOwnProperty()来确定一个属性存在于实例中还是原型中。只在给定的属性存在于对象的实例中才会返回true

原型模式

  原型模式演示代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
function Person(){
}
Person.prototype.name='MrErHu';
Person.prototype.sayName=function(){
alert(this.name);
}
var person1=new Person();
person1.sayName();//"MrErHu"
var person2=new Person();
person2.sayName();//“MrErHu”
alert(person1.sayName == person2.sayName);//true

  与构造函数模式不同的是,新对象的所有实例和方法是有所有实例所共享的。所以两个Person实例其实访问的都是同一组属性和同一个sayName()函数。   

原型与in操作符

   单独使用in时,无论属性位于实例中还是原型中,都会返回true
因此我们可以使用下面的代码来判断属性是位于原型中还是实例中:

1
2
3
function hasPrototypeProperty(object,property) {
  return (property in object) && !(object.hasOwnProperty(property));
}

  在使用for-in循环的时候,返回的是通过对象可以访问的、可枚举的属性。既包括实例中的属性也包括原型中的属性,所有屏蔽了原型中的实例属性都会被返回,因为根据规定所有开发人员定义的属性都是可枚举的。在ECMAScript5中使用Object.keys()方法,可以返回一个包含所有可枚举属性的字符串数组。如果想要得到所有实例属性,无论是否可枚举,可以使用Object.getOwnPropertyNames()方法。
  我们为了简便的情况下,可以用一个包含所有属性和方法的对象字面量化来重新整个原型对象。但是这样会出现一个问题,就是constructor属性不会再指向构造函数,而是指向Object构造函数   

组合使用构造函数模式和原型模式

  在创建自定义类型中最常用的方式是将构造函数模式和原型模式组合起来使用,构造函数用来创建实例的属性原型模式用来定义方法和共享的属性。这样每个实例都会有一份自己的实例属性的副本,但同时又会共享方法的应用。
演示代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function Person(name){
this.name=name;
this.friend=['friend1','friend2'];
}
Person.prototype={
constructor:Person,
sayName:function{
alert(this.name);
}
}
var person1= new Person("Alen");
var person2=new Person("Bob");
person1.friend.push("friend3");
alert(person1.friends); //friend1,friend2,friend3
alert(person2.friends); //friend1,friend2
alert(person1.friend===person2.friend);//false
alert(person1.sayName==person2.sayName);//true

  

动态原型模式

  动态原型方法是将所有的信息都封装在构造函数中,在构造函数中初始化原型。例如:

1
2
3
4
5
6
7
8
  funtion Person(name){
   this.name=name;
   if(typeof this.sayName != "function"){
   Person.prototype.sayName=function(){
   alert(this.name);
   }
   }
   }

  该段代码只会在初次调用的时候才会执行if语句,并且将原型都封装在了构造函数中。   

寄生构造函数模式

  这种模式的思想是创建一个函数,函数的作用仅仅是封装创建对象的代码,然后返回新创建的对象。
演示代码:

1
2
3
4
5
6
7
8
9
10
function(name){
var object=new Object();
object.name=name;
object.sayName=function(){
alert(this.name);
}
return object;
}
var friend=new Person('MrErHu');
friend.sayName();//'MrErHu'

  寄生和工厂模式其实是一模一样的,只不过是使用了new操作符。构造函数在不返回值的时候,默认返回的是新对象的实例,而通过在构造函数的末尾添加return语句,可以重写调用构造函数的返回值。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
function SpecialArray(){
//创建数组
var values=new Array();
//添加值
values.push.apply(values,arguments);
//添加方法
values.toPipedString=function(){
return this.join('|');
}
return values;
}
var colors=new SpecialArray("red","green","black");
alert(colors.toPipedString());

  在寄生构造函数模式中,返回的对象与构造函数或者构造函数的原型属性之间没有什么关系。也就是说,构造函数返回的对象和在构造函数外部创建的对象之间没有什么区别。不能依赖instance操作符来确定对象属性。

稳妥构造函数模式

  稳妥对象是指没有公共属性,其方法也不引用this对象。稳妥对象最适合在一些安全的环境中(禁止使用this和new)。稳妥模式类似于寄生构造函数,但是有两点不同:

  • 新创建对象的实例方法不引用this
  • 不使用new操作符调用构造函数
    例如:
    1
    2
    3
    4
    5
    6
    7
    8
    function Person(name){
    var object=new Object();
    //可以添加其他的私有变量和函数
    o.sayName=function(){
    alert(name);
    }
    return o;
    }

  在上面的模式中,除了sayName()方法之外,没有其他的方法可以访问到name值。


原文地址: https://mrerhu.github.io/2016/12/16/JavaScript%E9%9D%A2%E5%AF%B9%E5%AF%B9%E8%B1%A1-%E4%B8%8A/

0 0