面向对象的语言有一个标志,即拥有类的概念,抽象实例对象的公共属性与方法,基于类可以创建任意多个实例对象,一般具有封装、继承、多态的特性!但JS中对象与纯面向对象语言中的对象是不同的,ECMA标准定义JS中对象:无序属性的集合,其属性可以包含基本值、对象或者函数。可以简单理解为JS的对象是一组无序的值,其中的属性或方法都有一个名字,根据这个名字可以访问相映射的值(值可以是基本值/对象/方法)。

官方解释

原型模式如类模式一样,都是是一种编程泛型,即编程的方法论。另外最近大红大紫的函数编程也是一种编程泛型。JavaScript之父Brendan Eich在设计JavaScript时,从一开始就没打算为其加入类的概念,而是借鉴了另外两门基于原型的的语言:Self和Smalltalk。

  既然同为面向对象语言,那就得有创建对象的方法。在类语言中,对象基于模板来创建,首先定义一个类作为对现实世界的抽象,然后由类来实例化对象;而在原型语言中,对象以克隆另一个对象的方式创建,被克隆的母体称为原型对象。

一、理解对象:

第一种:基于Object对象

var person = new Object();
person.name = 'My Name';
person.age = 18;
person.getName = function(){
return this.name;
}

第二种:对象字面量方式(比较清楚的查找对象包含的属性及方法)

var person = {
name : 'My name',
age : 18,
getName : function(){
return this.name;
}
}

JS的对象可以使用‘.’操作符动态的扩展其属性,可以使用’delete’操作符或将属性值设置为’undefined’来删除属性。如下:

person.newAtt=’new Attr’;//添加属性
alert(person.newAtt);//new Attr
delete person.age;
alert(person.age);//undefined(删除属性后值为undefined);

二、对象属性类型

ECMA-262第5版定义了JS对象属性中特征(用于JS引擎,外部无法直接访问)。ECMAScript中有两种属性:数据属性和访问器属性

1、数据属性:

数据属性指包含一个数据值的位置,可在该位置读取或写入值,该属性有4个供述其行为的特性:

[[configurable]]:表示能否使用delete操作符删除从而重新定义,或能否修改为访问器属性。默认为true;

[[Enumberable]]:表示是否可通过for-in循环返回属性。默认true;

[[Writable]]:表示是否可修改属性的值。默认true;

[[Value]]:包含该属性的数据值。读取/写入都是该值。默认为undefined;如上面实例对象person中定义了name属性,其值为’My name’,对该值的修改都反正在这个位置

要修改对象属性的默认特征(默认都为true),可调用Object.defineProperty()方法,它接收三个参数:属性所在对象,属性名和一个描述符对象(必须是:configurable、enumberable、writable和value,可设置一个或多个值)。

如下:(浏览器支持:IE9+、Firefox 4+、Chrome、Safari5+)

var person = {};
Object.defineProperty(person, 'name', {
configurable: false,
writable: false,
value: 'Jack'
});
alert(person.name);//Jack
delete person.name;
person.name = 'lily';
alert(person.name);//Jack

可以看出,delete及重置person.name的值都没有生效,这就是因为调用defineProperty函数修改了对象属性的特征;值得注意的是一旦将configurable设置为false,则无法再使用defineProperty将其修改为true(执行会报错:can't redefine non-configurable property);

2、访问器属性:

它主要包括一对getter和setter函数,在读取访问器属性时,会调用getter返回有效值;写入访问器属性时,调用setter,写入新值;该属性有以下4个特征:

[[Configurable]]:是否可通过delete操作符删除重新定义属性;

[[Numberable]]:是否可通过for-in循环查找该属性;

[[Get]]:读取属性时调用,默认:undefined;

[[Set]]:写入属性时调用,默认:undefined;

访问器属性不能直接定义,必须使用defineProperty()来定义,如下:

var person = {
_age: 18
};
Object.defineProperty(person, 'isAdult', {
get: function () {
if (this._age >= 18) {
return true;
} else {
return false;
}
}
});

alert(person.isAdult?'成年':'未成年');//成年
从上面可知,定义访问器属性时getter与setter函数不是必须的,并且,在定义getter与setter时不能指定属性的configurable及writable特性;

此外,ECMA-262(5)还提供了一个Object.defineProperties()方法,可以用来一次性定义多个属性的特性:

var person = {};
Object.defineProperties(person,{
_age:{
value:19
},
isAdult:{
get: function () {
if (this._age >= 18) {
return true;
} else {
return false;
}
}
}
});

alert(person.isAdult?'成年':'未成年');//成年

上述代码使用Object.defineProperties()方法同时定义了_age及isAudlt两个属性的特性

此外,使用Object.getOwnPropertyDescriptor()方法可以取得给定属性的特性:

var descriptor = Object.getOwnPropertyDescriptor(person,'_age');
alert(descriptor.value);//19
对于数据属性,可以取得:configurable,enumberable,writable和value;

对于访问器属性,可以取得:configurable,enumberable,get和set

三、创建对象

使用Object构造函数或对象字面量都可以创建对象,但缺点是创建多个对象时,会产生大量的重复代码,因此下面介绍可解决这个问题的创建对象的方法

1、工厂模式

function createPerson(name, age, job) {
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.getName = function () {
return this.name;
}
return o;//使用return返回生成的对象实例
}
var person = createPerson('Jack', 19, 'SoftWare Engineer');

创建对象交给一个工厂方法来实现,可以传递参数,但主要缺点是无法识别对象类型,因为创建对象都是使用Object的原生构造函数来完成的。

2、构造函数模式

function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.getName = function () {
return this.name;
}
}
var person1 = new Person('Jack', 19, 'SoftWare Engineer'); var person2 = new Person('Liye', 23, 'Mechanical Engineer');

使用自定义的构造函数(与普通函数一样,只是用它来创建对象),定义对象类型(如:Person)的属性和方法。它与工厂方法区别在于:

没有显式地创建对象
直接将属性和方法赋值给this对象;
没有return语句;
此外,要创建Person的实例,必须使用new关键字,以Person函数为构造函数,传递参数完成对象创建;实际创建经过以下4个过程:

创建一个对象
将函数的作用域赋给新对象(因此this指向这个新对象,如:person1)
执行构造函数的代码
返回该对象
上述由Person构造函数生成的两个对象person1与person2都是Person的实例,因此可以使用instanceof判断,并且因为所有对象都继承Object,因此person1 instanceof Object也返回真:

alert(person1 instanceof Person);//true;
alert(person2 instanceof Person);//true;
alert(person1 instanceof Object);//true;
alert(person1.constructor === person2.constructor);//ture;

虽然构造函数方式比较不错,但也存在缺点,那就是在创建对象时,特别针对对象的属性指向函数时,会重复的创建函数实例,以上述代码为基础,可以改写为:

function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.getName = new Function () {//改写后效果与原代码相同,不过是为了方便理解
return this.name;
}
}

上述代码,创建多个实例时,会重复调用new Function();创建多个函数实例,这些函数实例还不是一个作用域中,当然这一般不会有错,但这会造成内存浪费。当然,可以在函数中定义一个getName = getName的引用,而getName函数在Person外定义,这样可以解决重复创建函数实例问题,但在效果上并没有起到封装的效果,如下所示:

function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.getName = getName;
}
function getName() {//到处是代码,看着乱!!
return this.name;
}

3、原型模式

JS每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,它是所有通过new操作符使用函数创建的实例的原型对象。原型对象最大特点是,所有对象实例共享它所包含的属性和方法,也就是说,所有在原型对象中创建的属性或方法都直接被所有对象实例共享。

function Person(){
}
Person.prototype.name = 'Jack';//使用原型来添加属性
Person.prototype.age = 29;
Person.prototype.getName = function(){
return this.name;
}
var person1 = new Person();
alert(person1.getName());//Jack
var person2 = new Person();
alert(person1.getName === person2.getName);//true;共享一个原型对象的方法

原型是指向原型对象的,这个原型对象与构造函数没有太大关系,唯一的关系是函数的prototype是指向这个原型对象!而基于构造函数创建的对象实例也包含一个内部指针为:[[prototype]]指向原型对象。

实例属性或方法的访问过程是一次搜索过程:

首先从对象实例本身开始,如果找到属性就直接返回该属性值;
如果实例本身不存在要查找属性,就继续搜索指针指向的原型对象,在其中查找给定名字的属性,如果有就返回;
基于以上分析,原型模式创建的对象实例,其属性是共享原型对象的;但也可以自己实例中再进行定义,在查找时,就不从原型对象获取,而是根据搜索原则,得到本实例的返回;简单来说,就是实例中属性会屏蔽原型对象中的属性;

原型与in操作符

一句话:无论原型中属性,还是对象实例的属性,都可以使用in操作符访问到;要想判断是否是实例本身的属性可以使用object.hasOwnProperty(‘attr’)来判断;

原生对象中原型

原生对象中原型与普通对象的原型一样,可以添加/修改属性或方法,如以下代码为所有字符串对象添加去左右空白原型方法:

String.prototype.trim = function(){
return this.replace(/^\s+/,'').replace(/\s+$/,'');
}
var str = ' word space ';
alert('!'+str.trim()+'!');//!word space!

原型模式的缺点,它省略了为构造函数传递初始化参数,这在一定程序带来不便;另外,最主要是当对象的属性是引用类型时,它的值是不变的,总是引用同一个外部对象,所有实例对该对象的操作都会其它实例:

function Person() {
}
Person.prototype.name = 'Jack';
Person.prototype.lessons = ['Math','Physics'];
var person1 = new Person();
person1.lessons.push('Biology');
var person2 = new Person();
alert(person2.lessons);//Math,Physics,Biology,person1修改影响了person2

4、组合构造函数及原型模式

目前最为常用的定义类型方式,是组合构造函数模式与原型模式。构造函数模式用于定义实例的属性,而原型模式用于定义方法和共享的属性。结果,每个实例都会有自己的一份实例属性的副本,但同时又共享着对方方法的引用,最大限度的节约内存。此外,组合模式还支持向构造函数传递参数,可谓是集两家之所长。

function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.lessons = ['Math', 'Physics'];
}
Person.prototype = {
constructor: Person,//原型字面量方式会将对象的constructor变为Object,此外强制指回Person
getName: function () {
return this.name;
}
}
var person1 = new Person('Jack', 19, 'SoftWare Engneer');
person1.lessons.push('Biology');
var person2 = new Person('Lily', 39, 'Mechanical Engneer');
alert(person1.lessons);//Math,Physics,Biology
alert(person2.lessons);//Math,Physics
alert(person1.getName === person2.getName);//true,//共享原型中定义方法

在所接触的JS库中,jQuery类型的封装就是使用组合模式来实例的!!!

5、动态原型模式

组合模式中实例属性与共享方法(由原型定义)是分离的,这与纯面向对象语言不太一致;动态原型模式将所有构造信息都封装在构造函数中,又保持了组合的优点。其原理就是通过判断构造函数的原型中是否已经定义了共享的方法或属性,如果没有则定义,否则不再执行定义过程。该方式只原型上方法或属性只定义一次,且将所有构造过程都封装在构造函数中,对原型所做的修改能立即体现所有实例中:

function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.lessons = ['Math', 'Physics'];
}
if (typeof this.getName != 'function') {//通过判断实例封装
  Person.prototype = {
    constructor: Person,//原型字面量方式会将对象的constructor变为Object,此外强制指回Person
    getName: function () {
      return this.name;
    }
  }
}
var person1 = new Person('Jack', 19, 'SoftWare Engneer');
person1.lessons.push('Biology');
var person2 = new Person('Lily', 39, 'Mechanical Engneer');
alert(person1.lessons);//Math,Physics,Biology
alert(person2.lessons);//Math,Physics
alert(person1.getName === person2.getName);//true,//共享原型中定义方法

注:以上内容参考《JavaScript 高级程序设计》第3版

怎么理解js的面向对象编程的更多相关文章

  1. JavaScript--我发现,原来你是这样的JS:面向对象编程OOP[2]--(创建你的那个对象吧)

    一.介绍 我们继续面向对象吧,这次是面向对象编程的第二篇,主要是讲创建对象的模式,希望大家能从博客中学到东西. 时间过得很快,还是不断的学习吧,为了自己的目标. 二.创建对象 1.前面的创建对象方式 ...

  2. JS--我发现,原来你是这样的JS:面向对象编程OOP[3]--(JS继承)

    一.面向对象编程(继承) 这篇博客是面向对象编程的第三篇,JS继承.继承顾名思义,就是获取父辈的各种"财产"(属性和方法). 怎么实现继承? 我们的JavaScript比较特别了, ...

  3. JS--我发现,原来你是这样的JS:面向对象编程OOP[2]--(创建你的那个对象吧)

    一.介绍 我们继续面向对象吧,这次是面向对象编程的第二篇,主要是讲创建对象的模式,希望大家能从博客中学到东西. 时间过得很快,还是不断的学习吧,为了自己的目标. 二.创建对象 1.前面的创建对象方式 ...

  4. JavaScript--我发现,原来你是这样的JS:面向对象编程OOP[1]--(理解对象和对象属性类型)

    一.介绍 老铁们,这次是JS的面向对象的编程OOP(虽然我没有对象,心累啊,但是可以自己创建啊,哈哈). JS高程里第六章的内容,这章内容在我看来是JS中很难理解的一部分.所以分成三篇博客来逐个理清. ...

  5. JS--我发现,原来你是这样的JS:面向对象编程OOP[1]--(理解对象和对象属性类型)

    一.介绍 老铁们,这次是JS的面向对象的编程OOP(虽然我没有对象,心累啊,但是可以自己创建啊,哈哈). JS高程里第六章的内容,这章内容在我看来是JS中很难理解的一部分.所以分成三篇博客来逐个理清. ...

  6. JS的面向对象编程

    一.什么是"非构造函数"的继承? 比如,现在有一个对象,叫做"中国人". var Chinese = { nation:'中国' }; 还有一个对象,叫做&qu ...

  7. js中面向对象编程

    一.理解对象: 第一种:基于Object对象 var person = new Object(); person.name = 'My Name'; person.age = 18; person.g ...

  8. js总结-面向对象编程,DOM,BOM

  9. js面向对象编程-高级内容

    JavaScript面向对象 一.为每个对象动态添加属性或方法 功能:返回对象类型原型的引用 prototype的使用 格式:class.prototype 场景: 比如说:A写了一个类,交给B,B在 ...

随机推荐

  1. ASP.NET MVC TempData使用心得

    说明: 在ASP.NET MVC中資料傳遞主要有ViewData與TempData ViewData主要是Controller傳遞Data給View,存留期只有一個Action,要跨Action要使用 ...

  2. Spark SQL原理及实战

    一.Spark SQL的发展 1.spark SQL和shark SparkSQL的前身是Shark,给熟悉RDBMS但又不理解MapReduce的技术人员提供快速上手的工具,Hive应运而生,它是当 ...

  3. Oracle中 Instr 这个函数

    http://www.jb51.net/article/42369.htm sql :charindex('字符串',字段)>0 charindex('administrator',MUserI ...

  4. Linux下添加shell脚本使得nginx日志每天定时切割压缩

    Linux下添加shell脚本使得nginx日志每天定时切割压缩一 简介 对于nginx的日志文件,特别是access日志,如果我们不做任何处理的话,最后这个文件将会变得非常庞大 这时,无论是出现异常 ...

  5. CodeForces757B

    B. Bash's Big Day time limit per test 2 seconds memory limit per test 512 megabytes input standard i ...

  6. java gc的调用机制 和编程规则

    转载:http://sunzhyng.iteye.com/blog/480148 一个优秀的Java程序员必须了解GC的工作原理.如何优化GC的性能.如何与GC进行有限的交互,有一些应用程序对性能要求 ...

  7. php扩展SeasLog应用于 yii2 组件

    一.seaslog 简单介绍及使用原因 它是C 写的PHP扩展,性能很高,使用简单,能满足大部分简单的日志需求.(个人感觉) 其他优势请看-->https://github.com/Neeke/ ...

  8. Spark:一个独立应用

    [TOC] Spark:一个独立应用 关于构建 Java和Scala 在Java和Scala中,只需要给你的应用添加一个对于spark-core的Maven依赖. Python 在Python中,可以 ...

  9. We Chall-Training: Stegano I-Writeup

    MarkdownPad Document html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,ab ...

  10. 模块化写法-IIFE解析

    一.IIFE解释 全拼Imdiately Invoked Function Expression,立即执行的函数表达式.  像如下的代码所示,就是一个匿名立即执行函数: (function(windo ...