文档章节

js程序设计03——面向对象

c
 caiyezi
发布于 2016/11/08 20:24
字数 2382
阅读 0
收藏 0

ECMAScript中有2中属性:数据属性、访问器属性。

数据属性是为了指定某对象的指定key上的一些行为,比如value是否可删除、修改、key可循环遍历等特点。而访问器属性不包含数据值,包含一堆get、set方法(非必须),读取访问对象属性时,采用getter、setter分别实现。

数据属性包括:

  • Configurable:表示能否通过delete删除该属性,或者修改重新定义属性,默认false
  • Enumerable:表示for-in循环返回属性,默认false
  • Writable:表示能否修改属性值,默认false
  • Value:包含这个数据的值

一旦把属性定义为不可配置的,就不能再把它变回可配置了:

var person = {};
Object.defineProperty(person,"name",{
  configurable:false,
  value:"admin"
});

//直接报错:TypeError: Cannot redefine property: name
Object.defineProperty(person,"name",{
  configurable:true,
  value:"test"
})

因为这里的"name"属性已经定义为configurable:false,即不可重新修改属性,所以再次修改会报错。

var person = {
  name:"我是没修改前的值",
  _age:22
};

Object.defineProperty(person,"age",{
  get:function(){
    return this._age;
  },
  set:function(newValue){
    if(newValue > this.age){
      this._age = newValue;
      this.name += " 哈哈,我是修改后的name值";
    }
  }
});

person.age = 22;
console.log(person);

person.age = 23;
console.log(person);

执行结果:

注意:这里的属性下划线是一种记号,表示只能通过对象方法访问的属性,这样一来,对该属性的读取会使用get、set来实现,如果只指定get表示不能写入,只指定set表示不能读取。

那么,如何一次性声明定义多个属性呢,可以使用Object的defineProperties方法,如下:

var person = {};
Objetc.defineProperties(person,{
  _name:{
    value:"chaozhou"
  },
  age:{
    value:23,
    set:function(newValue){
      this.age = newValue;
    },
    get:function(){
      return this.age;
    }
  },
  sex:{
    value:"男"
  }
});

关于对象创建(设计模式)

1、工厂模式

function createPerson(name,age){
  var o = new Object();
  o.name = name;
  o.age = age;
  o.sayName = function(){
    console.log(this.name);
  }
  return o;
}

var p = new createPerson("admin",23);
p.sayName();   //"admin"

2、构造器模式

function Person(name,age){
  this.name = name;
  this.age = age;
  this.sayName = function(){
    console.log(this.name);
  }
}
var p = new Person("admin",23);
p.sayName();   //"admin"
console.log(p instanceof Object);  //true
console.log(p instanceof Person);  //true

function Person2(name,age){
  this.name = name;
  this.age = age;
  this.sayName = sayname;
}
function sayname(){
  console.log(this.name);
}
var p2 = new Person2("admin2",22);
p2.sayName();   //"admin2"
console.log(p2 instanceof Object);  //true
console.log(p2 instanceof Person2);  //true

3、原型模式

function Person(){
  Person.prototype.name = "admin";
  Person.prototype.age = 12;
  Person.prototype.sayName = function(){
    console.log(Person.prototype.name);
  };
}
var p1 = new Person();
p1.sayName();    //"admin"

function Person2(){}

Person2.prototype.name = "admin1";
Person2.prototype.age = 14;
Person2.prototype.sayName = function(){
  console.log(Person2.prototype.name);
};
var p1 = new Person2();
p1.sayName();   //"admin1"

原型模式下,所有对象实例共享prototype定义的属性和方法,相当于直接将这些信息添加到原型中。原型设计中的关系如下图(摘自:“javascript高级程序设计”):

自我理解与解释:创建Person构造函数后,Person默认拥有prototype属性,指向的是Person的原型,而Person的原型默认拥有constructor属性,该属性指向一个包含了prototype属性的函数的指针,即Person构造函数,Person原型中的其它属性比如name,是在Person原型属性之上添加的其它属性。person1、person2实例的prototype属性同是一个指向Person.prototype的指针。

判断对象是否是某原型:

Person.prototype.isPrototypeOf(person1)  //判断person1内部是否含所有指向Person.prototype的指针

获取某对象的原型:

alert(Object.getPrototypeOf(person1) == Person.prototype); //true

使用delete操作符只能删除实例对象上的属性,不能删除原型上的同名属性:

首先看不使用delete时:

function Person2(){}

Person2.prototype.name = "我是原型的name";
Person2.prototype.age = 14;
Person2.prototype.sayName = function(){
  console.log(Person2.prototype.name);    //"我是原型的name"
  console.log(this.name);   //"我是实例的name"
};
var p1 = new Person2();

p1.name = "我是实例的name"

p1.sayName();

使用delete后:

function Person2(){}

Person2.prototype.name = "我是原型的name";
Person2.prototype.age = 14;
Person2.prototype.sayName = function(){
  console.log(Person2.prototype.name);    //"我是原型的name"
  console.log(this.name);   //"我是原型的name"
};
var p1 = new Person2();

p1.name = "我是实例的name"

delete p1.name;

p1.sayName();

判断实例中是否包含某属性hasOwnProperty使用:

function Person2(){}

Person2.prototype.name = "我是原型的name";
Person2.prototype.age = 14;
Person2.prototype.sayName = function(){
  console.log(this.name);   //"我是实例的name"
};
var p1 = new Person2();
p1.name = "我是实例的name"
p1.sayName();
console.log(p1.hasOwnProperty("name"));   //true
delete p1.name;
console.log(p1.hasOwnProperty("name"));   //false

相比于hasOwnProperty,in操作符可以判断该实例是否包含该属性,不管该属性存在于实例还是原型中,in可以单独使用,也可以在for-in循环(只能遍历数据属性中Enumerable为true的属性)中使用:

function Person2(){}

Person2.prototype.name = "我是原型的name";
Person2.prototype.age = 14;
Person2.prototype.sayName = function(){
  console.log(this.name);   //"我是实例的name"
};

var p = new Person2();

var arr = [];
for(var attr in p){
  arr.push(attr);
}

console.log(arr);  //["name", "age", "sayName"]

使用Object.keys同样可以列出所有对象属性,不同的是对原型使用会返回原型中的所有属性数组,对实例使用仅仅返回实例属性数组:

function Person2(){}

Person2.prototype.name = "我是原型的name";
Person2.prototype.age = 14;
Person2.prototype.sayName = function(){
  console.log(this.name);   //"我是实例的name"
};

var p = new Person2();
p.test = "admin"
console.log(Object.keys(Person2.prototype));   //["name", "age", "sayName"]
console.log(Object.keys(p));   //["test"]
console.log(Object.getOwnPropertyNames(Person2.prototype));  //["constructor", "name", "age", "sayName"] 获取所有原型属性(不管是否可枚举)

关于原型简写:

可以直接使用如下字面量形式简写:

function Person(){
  
}

//constructor 属性不再指向Person 了,而是指向Object
Person.prototype = {
  name:"admin",
  age:23,
  sayName:function(){
    console.log(this.name);
  }
};

var p = new Person();
console.log(p instanceof Object);   //true
console.log(p instanceof Person);   //true
console.log(p.constructor == Object);   //true
console.log(p.constructor == Person);   //false

简写之后只有一个问题,就是constructor指向变了,可以人为指定constructor指向:

function Person(){
  
}

//constructor 属性不再指向Person 了,而是指向Object
Person.prototype = {
  constructor:Person,
  name:"admin",
  age:23,
  sayName:function(){
    console.log(this.name);
  }
};

var p = new Person();
console.log(p instanceof Object);   //true
console.log(p instanceof Person);   //true
console.log(p.constructor == Object);   //false
console.log(p.constructor == Person);   //true

原型的动态性

也就说,可以先声明一个实例,然后修改实例原型某属性(如果修改整个原型,则情况有变),则修改后的原型属性或者方法实例可立即调用:

function Person(){
  
}

var p = new Person();
Person.prototype.name = "admin";
console.log(p.name);  //"admin"

组合使用构造模式及原型模式

function Person(name,age){
  this.name = name;
  this.age = age;
  this.friends = ["jefy","lorry"];
}

Person.prototype = {
  constructor:Person,
  sayName:function(){
    console.log(this.name);
  }
}

var p1 = new Person("p1",11);
var p2 = new Person("p2",22);

p1.friends.push("gatu");
console.log(p1.friends);   //["jefy", "lorry", "gatu"]
console.log(p2.friends);   //["jefy", "lorry"]
console.log(p1.friends === p2.friends);   //false
console.log(p1.sayName === p2.sayName);   //true

关于对象继承

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
alert(instance.getSubValue()); //false

继承实现的本质是重写原型对象,换句话说,原来存在于SuperType 的实例中的所有属性和方法,现在也存在于SubType.prototype 中了。在确立了继承关系之后,我们给SubType.prototype 添加了一个方法,这样就在继承了SuperType 的属性和方法的基础上又添加了一个新方法,结构如下:

  我们没有使用SubType 默认提供的原型,而是给它换了一个新原型;这个新原型就是SuperType 的实例。于是,新原型不仅具有作为一个SuperType 的实例所拥有的全部属性和方法,而且其内部还有一个指针,指向了SuperType 的原型。最终结果就是这样的:instance 指向SubType的原型, SubType 的原型又指向SuperType 的原型。getSuperValue() 方法仍然还在SuperType.prototype 中,但property 则位于SubType.prototype 中。这是因为property 是一个实例属性,而getSuperValue()则是一个原型方法。既然SubType.prototype 现在是SuperType的实例,那么property 当然就位于该实例中了。

关于应用类型值的原型问题

先看一个例子:

function SuperType(){
 this.colors = ["red", "blue", "green"];
}
function SubType(){
}
//继承了SuperType
SubType.prototype = new SuperType();
var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
var instance2 = new SubType();
alert(instance2.colors); //"red,blue,green,black"

  当SubType 通过原型链继承了SuperType 之后,SubType.prototype 就变成了SuperType 的一个实例,因此它也拥有了一个它自己的colors 属性——就跟专门创建了一个SubType.prototype.colors 属性一样。但结果是什么呢?结果是SubType 的所有实例都会共享这一个colors 属性。而我们对instance1.colors 的修改能够通过instance2.colors 反映出来,就已经充分证实了这一点。
  如下方式可以解决这个问题:

function SuperType(){
  this.colors = ["red", "blue", "green"];
}
function SubType(){
//继承了SuperType
SuperType.call(this);
}
var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
var instance2 = new SubType();
alert(instance2.colors); //"red,blue,green"

  通过使用call()方法(或apply()方法也可以),我们实际上是在(未来将要)新创建的SubType 实例的环境下调用了SuperType 构造函数。这样一来,就会在新SubType 对象上执行SuperType()函数中定义的所有对象初始化代码。
借用构造函数向超类传递参数:

function SuperType(name){
  this.name = name;
}
function SubType(){
  //继承了SuperType,同时还传递了参数
  SuperType.call(this, "Nicholas");
  //实例属性
  this.age = 29;
}
var instance = new SubType();
alert(instance.name); //"Nicholas";
alert(instance.age); //29

组合继承

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;
}
//继承方法
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
alert(this.age);
};
var instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
instance1.sayName(); //"Nicholas";
instance1.sayAge(); //29
var instance2 = new SubType("Greg", 27);
alert(instance2.colors); //"red,blue,green"
instance2.sayName(); //"Greg";
instance2.sayAge(); //27

  在这个例子中,SuperType 构造函数定义了两个属性:name 和colors。SuperType 的原型定义了一个方法sayName()。SubType 构造函数在调用SuperType 构造函数时传入了name 参数,紧接着又定义了它自己的属性age。然后,将SuperType 的实例赋值给SubType 的原型,然后又在该新原型上定义了方法sayAge()。这样一来,就可以让两个不同的SubType 实例既分别拥有自己属性——包括colors 属性,又可以使用相同的方法了。

本文转载自:http://www.cnblogs.com/vipzhou/p/5912519.html

共有 人打赏支持
c
粉丝 1
博文 108
码字总数 0
作品 0
西安
程序员
JavaScript 中的继承:ES3、ES5 和 ES6

选择一种继承方式 JavaScript 是一门动态语言,动态意味着高灵活性,而这尤其可以体现在继承上面。JavaScript 中的继承有很多种实现方式,可以分成下面四类: Mixin 模式,即属性混入,从一个...

天方夜
07/04
0
0
面向对象,更适合JavaScript

面向对象程序设计是软件开发中一个很庞大很复杂的话题,它并不是仅仅学会类、继承、封装、多态这些面向对象编程语法元素就表示掌握的,这些语法元素只是实现面向对象程序的工具, 就像砖块、...

陈宏鸿
05/07
0
0
世界级javascript大师们的视频与PPT

阅读: 6 评论: 0 作者: 阿K&LiveCai 发表于 2010-03-26 10:45 原文链接 来源:css9.net 导读:本文中的javascript大师们大多来自yahoo公司,可能说起他们的名字国内熟悉的人并不多,不过说...

陈波
2010/03/30
0
0
三分钟读懂Java与JavaScript的区别,让小白摘帽

Java跟JavaScript虽然在名称上有些许相似,但其实是两种完全不同的语言。Java是一种程序设计语言,JavaScript是客户端的脚本语言,把这两样东西放在一起比较在科学上其实并不严谨。但它们唯一...

小欣妹妹
2017/10/21
0
0
细说JavaScript数据类型及转换

细说JavaScript数据类型及转换 JavaScript数据类型 1.Boolean(布尔) 布尔:(值类型)var b1=true;//布尔类型 2.Number(数字) 数值:(值类型)var n1=3.1415926;//数值类型 n1.toFixed...

开元中国2015
2015/07/13
0
0

没有更多内容

加载失败,请刷新页面

加载更多

macOs-挂载能读写的NTFS硬盘

转自:https://nicklinyi.gitee.io/blog/2018/04/macOS-ntfs.html Mac本身是支持NTFS写入的,只是NTFS是微软开发,由于版权和技术细节原因,苹果不愿公开说自己支持NTFS写入,也是有自己以后...

北风刮的不认真了
2分钟前
0
0
Namespace 命名空间

命名空间可以定义为一种封装方式。 为了解决开发中库和程序中可重用类和方法问题: 1.解决 PHP内部方法类/方法/常量 或者第三方 类/方法/常量之间的命名冲突 2.能够简化为了防止命名冲突而给...

忙碌的小蜜蜂
4分钟前
0
0
CDH的坑之Deploy Client Configuration Failed

Deploy Client Configuration Failed 1.问题描述 当使用CDH增添spark服务的时候,出现了以下错误: Faile to deploy client configuration to the cluster. 具体如下图: 2.思路 网上查了...

星汉
5分钟前
0
0
java guava 集合的操作:交集、差集、并集

Guava:google的工程师利用传说中的“20%时间”开发的集合库,它是对jdk提供的扩展,提供了很多实用的类来简化代码。 开源地址:https://github.com/google/guava jar包下载:http://maven....

帅的不像男的
6分钟前
0
0
从八个层面比较分析 Java 8, RxJava, Reactor

响应式编程在单机环境下是否鸡肋? 结论是:没有结论,我觉得只能抱着怀疑的眼光审视这个问题了。另外还聊到了 RSocket 这个最近在 SpringOne 大会上比较火爆的响应式”新“网络协议,githu...

小刀爱编程
9分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部