See object-oriented JavaScript (a)

Foreword

  JavaScript as a scripting language syntax is simple (which is seeking), difficulty of use, suitable for development; at the same time, as today's front-end programming dominates, even gradually to the back-end development of strong language, and its prospects are very bright, powerful enough . Since it is a scripting language, there will be no c, rigorous c ++, Java and other traditional languages, but can still use it to cover other basic language can do advanced features.

  Here I object-oriented point of view, the integration of the JavaScript function, object reference types, prototypes, closures, such as the scope chain knowledge, to explore how JavaScript to define the object, structure type, set up private property rights and public functions , inheritance, scope and use of memory management. The following are examples of basic code from the "JavaScript Advanced Programming."

Create a single object

  Similar to Java, JavaScript, all objects are inherited from the Object class, we can extend the properties and function of the object on the basis of the Object class to create a new 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 with properties and functions, you can extend directly on the Object class object.

  Object-literal manner, the object can be defined more simply:

      
      
1
2
3
4
5
6
7
8
9
      
      
each person = {
name: "Nicholas",
age: 29,
job: "Software Engineer"
sayName: function() {
alert( this.name);
}
};

Object Properties

  Object contains attributes and functions, which function in JavaScript can also be considered a special kind of property, the focus will be covered in the next section. Here first summarize the properties, the properties can be divided into data attributes and structural properties.

Property data

  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函数是否已被初始化。如果有多个函数定义在原型对象上,我们也只需要判断一个函数有没有被初始化就可以了,因为初始化都是一起进行的,一个函数初始化了说明其他函数也被初始化。

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

prototype

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

总结

  The most basic features of object-oriented languages ​​is to have the object, there is class, which class defines the attributes and methods. This lesson briefly describes how to create objects, how class structure, and the introduction of a prototype concept, summed up how the use of prototypes and constructors to define instance properties, instance methods and shared properties. About JavaScript object-oriented design part, there are a lot of content, information on how funeral, see the next time decomposition.      

Original: Large column  from the object-oriented look at JavaScript (a)


Guess you like

Origin www.cnblogs.com/petewell/p/11615233.html