从面向对象看JavaScript(一)

前言

  JavaScript作为一种脚本语言,语法简单(求其),易上手,适合开发;同时,作为当今前端编程方面占据垄断地位,甚至逐步向后端发展的强势语言,它的前景十分美好,功能足够强大。既然是脚本语言,自然没有c,c++,Java等传统语言的严谨,但是利用它仍然可以基本覆盖其他语言能做到的高级功能。

  下面我就从面向对象的角度,整合JavaScript里函数,对象,引用类型,原型,闭包,作用域链等知识点,去探讨JavaScript是如何定义对象,构造类,设置属性和函数的私有公有权限,实现继承,利用作用域以及管理内存的。下面的代码例子基本都出自《JavaScript高级程序设计》。

创建单个对象

  与Java类似,JavaScript的所有对象都继承自Object类,我们可以在Object类的基础上扩展对象的属性与函数,从而创建出一个新的对象:

1

2

3

4

5

6

7

8
var person = new Object();

person.name = "Nicholas";

person.age = 29;

person.job = "Software Engineer";

person.sayName() = function(){

alert(this.name);

};

  对象包含属性与函数,可以直接在Object类对象上扩展。

  用对象字面量的方式,可以更简单地定义对象:

1

2

3

4

5

6

7

8

9
var person = {

name: "Nicholas",

age: 29,

job: "Software Engineer"

sayName: function(){

alert(this.name);

}

};

对象属性

  对象包含属性与函数,其中函数在JavaScript里也可以视为一种特殊的属性,会在下一节重点讲述。这里先总结一下属性,属性可以分为数据属性与构造器属性。

数据属性

  JavaScript里的每一个属性都有一些特征,相当于是属性的属性,用于JavaScript底层维护属性。数据属性有4个特性,分别是Configurable,Enumerable,Writable与Value,规范里记为[[Configurable],[[Enumerable]],[[Writable]],[[Value]]。这些特性是不能直接访问的。

  • [[Configurable]]:能否通过delete删除属性,能否修改属性特性,能否把属性修改为访问器属性。如果像上面直接在对象上创建属性,则默认为true。
  • [[Enumerable]]:能否通过for-in语法循环返回属性,默认为true。
  • [[Writable]]:能否修改属性的值,默认为true。
  • [[Value]]:属性的数据值,默认为undefined,像上面创建属性后就保存属性的值。

  除了直接在对象上创建属性以外,还可以通过Object.defineProperty()方法创建属性。这个方法接收3个参数,分别是属性所在对象,属性的名字,以及一个描述符对象。描述符对象的属性必须是configurable,enmuerable,writable和value。通过这个描述符对象,则可以定义属性的一或多个特性。在这种情况下,configurable,enumerable和writable默认为false。

  这里要注意一点是,当把configurable设置为false,则不能再设置属性的特性,包括不能将configuralble重新设为true。

访问器属性

  访问器属性也具有[[Configurable]]和[[Enumerable]]特性,但没有[[Writable]]和[[Value]],而替换为[[Get]],[[Set]]。[[Get]]和[[Set]]保存相应的函数,默认为undefined。当用一般的JavaSript读写该属性时,则会分别调用[[Get]]和[[Set]]里保存的函数,从而返回或写入相应的值。这就相当于Java里的getter和setter方法。只定义getter则不能写,只定义setter则不能读。访问器属性只能通过Object.defineProperty方法定义。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19
var book = {

_year: 2004,

edition: 1

};

Object.defineProperty(book, "year", {

get: function(){

return this._year;

},

set: function(newValue){

if(newValue > 2004){

this._year = newValue;

this.edition += newValue - 2004;

}

}

});

book.year = 2005;

alert(book.year);

  第一次访问book.year是写数据,调用setter方法,第二次访问book.year是读数据,调用getter方法。在Java里,getter和setter往往是针对私有属性的,用于向外界提供访问私有属性的一个公共接口,但访问器属性我觉得并不是为了这个目的而产生的。访问器属性,往往与一个数据属性相关联,例如这里的_year。这里_year前面的下划线是一种规范,用于表示与某个访问器属性相关联的数据属性。访问器属性是用于在设置一个属性的同时,导致其他属性的变化,这是它的最主要作用。至于私有公有属性的权限设定,则是由另外的技术实现的,这我也将会在后面几讲阐述。

同时定义多个属性

  可以用Object.defineProperties()方法,这个方法接收两个对象参数,一个是要添加和修改其属性的对象,另一个对象包含多个对象属性,每个属性与要添加或修改的属性一一对应,这些对象属性里的属性则是要定义的属性特性。

读取属性的特性

  用Object.getOwnPropertyDescriptor()方法可以获取给定属性的描述符,这个描述符对象的属性则是相应属性的特性。这个方法接收两个参数,分别是属性所在的对象和其描述符的属性名称。

  这个描述符对象的属性便是原对象对应属性的特性。
  

构造类

简易生产对象——工厂模式

  如果按照上面创建对象的方法,那么每次创建具有相同属性和方法的对象,都需要手动写一遍重复的代码。我们希望像其他面向对象语言一样,有“类”的概念,可以通过一个封装好的方法,批量地生成同一类型的对象。例如:

1

2

3

4

5

6

7

8

9

10

11

12

13
function (name,age,job){

var o = new Object();

o.name = name;

o.age = age;

o.job = job;

o.sayName = function(){

alert(this.name);

}

return o;

}

var person1 = createPerson("Nicholas",29,"Software Engineer");

var person2 = createPerson("Greg",27,"Doctor");

  creatPerson就是一个工厂方法,封装了创建对象的代码,使得批量生产对象变得十分简单。

  但是,工厂模式存在两个主要的问题。第一个是虽然能够封装创建对象的代码,但是仍然不能将这个对象称为“类”,无法识别这些具有相同属性类型和方法的对象是属于同一个类的;第二个是,一般的面向对象语言,属于同一类的对象共享同一套方法,但属性则各有不同。具体到内存里,以java为例,它的类里的实例方法都是存储在代码区的,同一类的不同对象是共享代码区的相同方法的,而属性则存储在堆栈或堆里,各个对象不同。

  JavaScript作为成熟的面向对象语言,这两个问题当然是可以解决的。下面我们分别来分析一下。

构造函数模式

1

2

3

4

5

6

7

8

9

10

11
function Person(name,age,job){

this.name = name;

this.age = age;

this.job = job;

this.sayName = function(){

alert(this.name);

};

}

var person1 = createPerson("Nicholas",29,"Software Engineer");

var person2 = createPerson("Greg",27,"Doctor");

  构造函数本身与普通函数是没有区别的。按照惯例,构造函数一般以大写字母开头,普通函数则以小写字母开头。如果不用new操作符,则调用构造函数与调用普通函数没有任何区别。如果调用了new操作符,且构造函数内部没有return,则会经历以下四个步骤:

  1. 创建一个新对象。
  2. 将构造函数的作用域赋给新对象。(this指向新对象)
  3. 执行构造函数中的代码。
  4. 返回新对象

  
  利用构造函数生成的对象,都有一个constructor属性,指向Person函数。我们可以用这个属性来作为类判别的依据。但更可靠的做法是用instanceof操作符,如:

1

2
alert(person1 instanceof Object);  

alert(person1 instanceof Person);  

  由于Person类继承自Object类,因此person1既可以说是Object类的对象,也可以说是Person类的对象。

动态原型模式

  每当创建一个新函数,该函数都自动获得一个prototype属性,该属性指向函数的原型对象。这个原型对象本身与普通的实例对象没有任何区别。通过构造函数创建的对象,都会含有一个内部属性[[prototype]],这个内部属性不能直接访问,其指向构造函数的原型对象。显然,所有通过该构造函数创建的对象,都有这么一个指针指向同一个原型对象。我们访问实例对象的属性和方法,会先看看实例对象有没有相应的属性或方法,没有的话再从原型对象上找。因此,我们可以把类里的实例方法以及共有的属性都定义在原型对象上,而把每个实例对象都不同的属性定义在实例上。

1

2

3

4

5

6

7

8

9

10

11

12

13
function Person(name,age,job){

this.name = name;

this.age = age;

this.job = job;

if(typeof this.sayName != "function"){

Person.prototype.sayName = function(){

alert(this.name);

}

}

}

var friend = new Person("Nicholas",29,"Software Engineer");

friend.sayName();

  sayName方法定义在原型对象上,其他属性则直接定义在实例对象上。为了更好地封装代码,动态原型模式把原型的属性和方法定义都包含在构造函数里,而没有放在构造函数外面进行。但这样存在一个问题,就是每创建一个实例,都会定义一个新的函数,因此我们需要通过一个判断语句,来判断sayName函数是否已被初始化。如果有多个函数定义在原型对象上,我们也只需要判断一个函数有没有被初始化就可以了,因为初始化都是一起进行的,一个函数初始化了说明其他函数也被初始化。

  原型对象,实例对象,构造函数的关系:

  由于访问对象的方法和属性时,是按照先实例对象再原型对象进行的,因此在实例对象上创建的同名属性或方法可以覆盖原型对象上的属性或方法。

总结

  面向对象语言的最基本特点就是有对象,有类,类里面定义了属性和方法。这一讲简单地介绍了怎么创建对象,怎么构造类,以及引入了原型的概念,总结了怎么利用原型与构造函数来定义实例属性,实例方法和共享属性。关于JavaScript的面向对象设计部分,还有很多内容,欲知后事如何,请看下回分解。
  
  

原文:大专栏  从面向对象看JavaScript(一)

原文地址:https://www.cnblogs.com/chinatrump/p/11615244.html

时间: 2024-08-14 21:52:57

从面向对象看JavaScript(一)的相关文章

全面理解面向对象的JavaScript

转载:http://justcoding.iteye.com/blog/2019293 原文:http://www.ibm.com/developerworks/cn/web/1304_zengyz_jsoo/index.html?ca=drs-#major6 前言 当今 JavaScript 大行其道,各种应用对其依赖日深.web 程序员已逐渐习惯使用各种优秀的 JavaScript 框架快速开发 Web 应用,从而忽略了对原生 JavaScript 的学习和深入理解.所以,经常出现的情况是,

前端开发:面向对象与javascript中的面向对象实现(一)

前端开发:面向对象与javascript中的面向对象实现(一) 前言: 人生在世,这找不到对象是万万不行的.咱们生活中,找不到对象要挨骂,代码里也一样.朋友问我说:“嘿,在干嘛呢......”,我:“找不到对象!”,他:“就你那样也能找得到对象?”.我一脸黑线...... 废话不多说,今天博主要跟大家聊的是<面向对象与javascript中的面向对象实现>”. 面向对象理解: 面向对象是一种对现实世界理解和抽象的方法,是一种先进的程序设计理念,是一种比较抽象的,多形态的设计模式.我们可以这么理

深入全面理解面向对象的 JavaScript

深入全面理解面向对象的 JavaScript (原著: 曾 滢, 软件工程师, IBM,2013 年 4 月 17 日) JavaScript 函数式脚本语言特性以及其看似随意的编写风格,导致长期以来人们对这一门语言的误解,即认为 JavaScript 不是一门面向对象的语言,或者只是部分具备一些面向对象的特征.本文将回归面向对象本意,从对语言感悟的角度阐述为什么 JavaScript 是一门彻底的面向对象的语言,以及如何正确地使用这一特性. 前言 当今 JavaScript 大行其道,各种应用

JavaScript Oriented[探究面向对象的JavaScript高级语言特性]

JavaScript Oriented 探究面向对象的JavaScript高级语言特性 Prologue . JavaScript Introduce 1.  JS Abstract JavaScript是由Netscape公司工程师Brendan Eich研发的脚本语言,经过推广和流行,兼容ECMA-262标准,至今用于描述HTML网页行为.(前端验证,检测,响应,触发,控制等动态行为) Knowledge Tree 2.     About Document 本文涉及到的概念有JavaScr

前端开发:面向对象与javascript中的面向对象实现(二)构造函数与原型

前端开发:面向对象与javascript中的面向对象实现(二)构造函数与原型 前言(题外话): 有人说拖延症是一个绝症,哎呀治不好了.先不说这是一个每个人都多多少少会有的,也不管它究竟对生活有多么大的影响,单单是自己的念想受到了一定得局限,想法不能够像平地而起的高楼大厦建成一样.可是那大楼也是有烂尾的呀,我觉得最重要的还是外在环境与个人观念的先决条件,决定了拖延症的症状的好坏,有那么一些人,它也有拖延症,但是它在拖的中间,想的更多,看的更远.事情在做的时候更加有条不紊,这拖延症这样看来,它也是好

用面向对象的Javascript来介绍一下自己

看了一道题目<用面向对象的Javascript来介绍一下自己>,然后自己觉得挺好玩的,所以就编写如下的代码. // HELPER function extend(sup, overrides) { var sub = overrides && overrides.constructor || function() { sup.apply(this, arguments); }; var fn = function() {}; var subp; fn.prototype = n

面向对象的JavaScript --- 多态

面向对象的JavaScript --- 多态 多态 "多态"一词源于希腊文 polymorphism,拆开来看是poly(复数)+ morph(形态)+ism,从字面上我们可以理解为复数形态. 多态的实际含义是:同一操作作用于不同的对象上面,可以产生不同的解释和不同的执行结果.换句话说,给不同的对象发送同一个消息的时候,这些对象会根据这个消息分别给出不同的反馈.从字面上来理解多态不太容易,下面我们来举例说明一下. ? 主人家里养了两只动物,分别是一只鸭和一只鸡,当主人向它们发出&quo

面向对象的JavaScript --- 动态类型语言

面向对象的JavaScript --- 动态类型语言 动态类型语言与面向接口编程 JavaScript 没有提供传统面向对象语言中的类式继承,而是通过原型委托的方式来实现对象与对象之间的继承. JavaScript 也没有在语言层面提供对抽象类和接口的支持. 正因为存在这些跟传统面向对象语言不一致的地方,我们在用设计模式编写代码的时候,更要跟传统面向对象语言加以区别.我们有必要先了解一些 JavaScript 在面向对象方面的知识. 编程语言按照数据类型大体可以分为两类,一类是静态类型语言,另一

全面理解面向对象的 JavaScript

对象的上下文依赖 var str = "我是一个 String 对象 , 我声明在这里 , 但我不是独立存在的!" var obj = { des: "我是一个 Object 对象 , 我声明在这里,我也不是独立存在的." }; var fun = function() { console.log( "我是一个 Function 对象!谁调用我,我属于谁:", this ); }; obj.fun = fun; console.log( this