一块儿来搞对象吧

什么是对象?

无序属性的集合, 其属性能够包含基本值、 对象或者函数javascript

对象的属性类型

ECMA-262定义了一些为实现JavaScript引擎用的属性,所以不能直接访问,为了表示其是特殊的内部值,把他们放在两对儿方括号中。例如[[[Enumerable]]。对象的属性分为数据属性和访问器属性java

数据属性

通常数据属性的值都为基础数据类型。设计模式

  • [[Configurable]] 表示可否经过delete删除属性从而重新定义…(默认为true PS:一旦修改,不能反悔)
  • [[Enumerable]] 表示可否经过for-in循环返回属性…(自定义属性默认为true,默认属性的默认值为false,例如:constructor )
  • [[Writable]] 表示可否修改属性值…(默认为true))
  • [[Value]] 包含属性的数据值…(就是上例中的‘Jiangwen’)
访问器属性
  • [[Configurable]] 表示可否经过delete删除属性从而重新定义…默认为true)
  • [[Enumerable]] 表示可否经过for-in循环返回属性…(默认为true)
  • [[get]]: 在读取属性时,调用的函数,默认undefined。
  • [[set]]: 在写入属性时,调用的函数,默认undefeated。

修改默认属性

ECMAScript给我提供了一个方法,Object.defineProperty()方法。这个方法接受三个参数:属性所在对象、属性的名字和一个描述对象。函数

var person = {};
Object.defineProperty(person,'name',{
    Writable:false,
    value:'jiangwen'
})
console.log(person.name) //'jiangwen'
person.name = 'xiaoming';
console.log(person.name) // 'jiangwen'
复制代码

修改默认属性为不容许修改,所以从新进行赋值修改无效ui

定义多个对象属性

因为为对象定义多个属性的可能性很大,ECMAScript 5 又定义了一个 Object.definePro- perties()方法。利用这个方法能够经过描述符一次定义多个属性。这个方法接收两个对象参数:第一个对象是要添加和修改其属性的对象,第二个对象的属性与第一个对象中要添加或修改的属性一一对应。this

var people = {};
Object.defineProperties(book,{ //定义多个对象属性,用到Object.defineProperties();
    _year:{
        value:18
    },
    name:{
        value:'jiangwen'
    }
})
复制代码

获取描述属性

ECMAScript也给咱们提供了一个方法:Object.getOwnPropertyDescriptor()这个方法接收两个参数:属性所在的对象和要读取其描述符的属性名称;返回值是一个对象spa

var book = {};  
Object.defineProperties(book, {     
  _year: {          value: 2004     },      
  edition: {         value: 1     },      
  year: {         
    get: function(){             
      return this._year;         
	},          
    set: function(newValue){             
      if (newValue > 2004) {                 
        this._year = newValue;                 
        this.edition += newValue - 2004;             
      }        
    }     
  } 
}); 
var descriptor = Object.getOwnPropertyDescriptor(book,"_year");
console.log(descriptor.value) // 18
console.log(descriptor.enumerable);   //false
复制代码

建立对象

1.基本方法

var person = new Object(); 
person.name = "JiangWen"; 
person.age = 29; 
person.job = "Software Engineer";  
person.sayName = function(){
  	alert(this.name);  
};
复制代码

2.对象字面量方法

var person = {     
  name: "JiangWen",      
  age: 25,     
  job: "Software Engineer",      
  sayName: function(){         
    alert(this.name);     
  } 
}; 
复制代码

虽然上面两种建立对象的方式,均可以用来建立单个对象,可是有个明显的缺点,使用同一接口建立不少对象,会产生大量的重复代码,所以就产生了不一样模式知足不一样状况下的需求prototype

3.工厂模式

工厂模式是软件工程领域一种广为人知的设计模式,用函数来封装以特定接口建立对象的细节设计

function createPerson(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("Jiangwen", 29, "Software Engineer"); 
var person2 = createPerson("Owen", 27, "Doctor");
复制代码

4.构造函数模式

function Person(name, age, job){     
  this.name = name;     
  this.age = age;     
  this.job = job;     
  this.sayName = function(){         
		alert(this.name);     
  };     
}  
var person1 = new Person("Jiangwen", 29, "Software Engineer"); 
var person2 = new Person("Owen", 27, "Doctor");
复制代码

调用构造函数实际上会经历如下 4 个步骤:3d

  1. 建立一个新对象
  2. 将构造函数的做用域赋值给新对象(所以 this 就指向了这个新对象)
  3. 执行构造函数中的代码(为这个新对象添加属性方法)
  4. 返回新对象 (注:默认返回新建立的对象; 不过若是在构造函数中显式返回一个对象数据类型,那么未来new的对象就是该显式return的对象)

构造函数模式和工厂模式有如下几个不一样之处

  • 没有显式地建立对象
  • 直接将属性方法赋值给this
  • 没有return语句

5.原型模式

咱们建立的每一个函数都有一个 prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含能够由特定类型的全部实例共享的属性和方法。

function Person(){ }  
Person.prototype.name = "Jiangwen"; 
Person.prototype.age = 25; 
Person.prototype.job = "Software Engineer"; 
Person.prototype.sayName = function(){     
  alert(this.name); 
};  
var person1 = new Person(); 
person1.sayName();   //"Jiangwen" 
var person2 = new Person();
person2.sayName();   //"Jiangwen" 
alert(person1.sayName == person2.sayName);  //true
复制代码

与构造函数模式不一样的是,新对象的这些属性和方法是由全部实例共享的。也就是说,person1 和 person2 访问的都是同一组属性和同一个 sayName()函数,以下图所示:

img

一些关于原型的方法:

// 肯定实例对象和构造函数原型之间是否存在关系
Person.prototype.isPrototypeOf(person1) // true
person1 instanceof Person)       //true 

// 获取实例的原型对象
Object.getPrototypeOf(person1) == Person.prototype  // true
Object.getPrototypeOf(person1).name; //"Jiangwen"

// 检测一个属性是存在于实例中,仍是存在于原型中
Person.prototype.name = "Owen"
var person1 = new Person()
person1.hasOwnProperty("name")  // false 来自原型 
person1.name = 'jiangwen'
person1.hasOwnProperty("name")  // true 来自实例

// 检测一个属性是存在于实例或者原型中,即该属性存在便可
"name" in person1  //true

// 取得当前对象上全部可枚举的实例属性
Object.keys(Person.prototype)  // "name,age,job,sayName"

// 取得当前对象上全部实例属性
Object.getOwnPropertyNames(Person.prototype); //"constructor,name,age,job,sayName"
复制代码

6.组合构造函数模式和原型模式

该模式是建立自定义类型的最多见方式: 构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性

function Person(name, age, job){     
  this.name = name;     
  this.age = age;     
  this.job = job;     
  this.friends = ["Shelby", "Court"]; 
}  
Person.prototype = {     
  constructor : Person,     // 对象字面量的形式至关于重写了原型对象,因此须要从新指定
  sayName : function(){         
    alert(this.name);     
  } 
}

var person1 = new Person("Nicholas", 29, "Software Engineer"); 
var person2 = new Person("Greg", 27, "Doctor");  
person1.friends.push("Van"); 
alert(person1.friends);    //"Shelby,Count,Van" 
alert(person2.friends);    //"Shelby,Count" 
alert(person1.friends === person2.friends);    //false 
alert(person1.sayName === person2.sayName);    //true
复制代码

7.动态原型模式

能够经过检查某个应该存在的方法是否有效,来决定是否须要初始化原型,从而避免改写原型上原有的同名属性或方法

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);         
    };              
  }
}
复制代码

该模式下只在 sayName()方法不存在于原型的状况下,才会将它添加到原型中

8.寄生构造函数模式(不建议使用)

该模式的基本思想是建立一个函数,该函数的做用仅仅是封装建立对象的代码,而后再返回新建立的对象;

function Person(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 friend = new Person("Nicholas", 29, "Software Engineer"); 
friend.sayName();  //"Nicholas"
复制代码

构造函数在不返回值的状况下,默认会返回新对象实例。而经过在构造函数的末尾添加一个 return 语句,能够重写调用构造函数时返回的值。

9.稳妥构造函数模式

所谓稳妥对象,指的是没有公共属性,并且其方法也不引用 this 的对象。稳妥构造函数遵循与寄生构造函数相似的模式,但有两点不一样:一是新建立对象的实例方法不引用 this;二是不使用 new 操做符调用构造函数。

function Person(name, age, job){          
  //建立要返回的对象 
  var o = new Object();
  
  //能够在这里定义私有变量和函数 
  
  //添加方法 
  o.sayName = function(){         
    alert(name);     
  };              
  //返回对象 
  return o; 
}
复制代码

注意, 在以这种模式建立的对象中, 除了使用 sayName()方法以外, 没有其余办法访问 name 的值

对象的继承

在Javascript中实现继承主要是依靠原型链来实现的;其基本思想是经过原型实现一个引用类型继承另外一个引用类型的属性和方法。

1.原型链

function SuperType(){
  this.super = 'SuperType'
}
SuperType.prototype.sayType = function(){
  alert(this.super)
}
function SubType(){
  this.sub = 'SubType'
}
SubType.prototype = new SuperType()
let a1 = new SubType()
复制代码

2.借用构造函数

子类型构造函数的内部调用超类型构造函数

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"
复制代码

3.组合继承

将原型链和借用构造函数的技术组合到一块,从而发挥两者之长的一种继承模式。其背后的思路是使用原型链实现对原型属性和方法的继承,而经过借用构造函数来实现对实例属性的继承。这样,既经过在原型上定义方法实现了函数复用,又可以保证每一个实例都有它本身的属性。

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
复制代码

因此组合继承避免了原型链和借用构造函数的缺陷,融合了它们的优势,成为 JavaScript 中最经常使用的继承模式。

4.原型式继承

将须要被继承的对象挂载到函数内部新建立的构造函数原型上并返回该构造函数的实例

/* 模拟Object.create()方法 * o: 须要被继承的对象 * o2: 自定义参数对象,默认为空 */
function object(o,o2={}){
   // 建立一个构造函数F为接收自定义参数
  var F = function(){
    for (const key in o2) {
      for (const key2 in o2[key]) {
        this[key] = o2[key][key2]//跳坑指南: 不能使用this.key,由于点赋值会将变量key转为字符串“key” 
      }
    }
  };
  F.prototype = o   // 重写构造函数F原型对象为传入的对象o
  return new F()  //返回构造函数的实例
}

var person = {
  name: 'no-Name',
  friend:[1,2,3],
  sayName:function(){
    alert(this.name)
  }
}
let a1 = object(person)

复制代码

ECMAScript 5 经过新增 Object.create()方法规范化了原型式继承。这个方法接收两个参数:一个用做新对象原型的对象和(可选的)一个为新对象定义额外属性的对象

// 在传入一个参数的状况下,Object.create()与 object()方法的行为相同
 var person = {     
   name: "Nicholas",     
   friends: ["Shelby", "Court", "Van"] 
 };  
var anotherPerson = Object.create(person); 
anotherPerson.name = "Greg"; anotherPerson.friends.push("Rob");      
var yetAnotherPerson = Object.create(person); 
yetAnotherPerson.name = "Linda"; 
yetAnotherPerson.friends.push("Barbie");  
alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"

复制代码

5.寄生式继承

寄生式(parasitic)继承是与原型式继承紧密相关的一种思路 ,其与工厂模式相似,即建立一个仅用于封装继承过程的函数,该函数在内部以某种方式来加强对象,最后再像真地是它作了全部工做同样返回对象

function createAnother(original){
  var clone = object(original)  //建立对象
  clone.sayHi = function(){			
    alert('hi!')
  }
  return clone
}
var person = {     
  name: "Jiangwen",     
  friends: ["A", "B", "C"] 
};  
var anotherPerson = createAnother(person); 
anotherPerson.sayHi(); //"hi"

复制代码

在主要考虑对象而不是自定义类型和构造函数的状况下,寄生式继承也是一种有用的模式。前面示范继承模式时使用的 object()函数不是必需的;任何可以返回新对象的函数都适用于此模式。

6.寄生组式继承

所谓寄生组合式继承,即经过借用构造函数来继承属性,经过原型链的混成形式来继承方法 ; 本质上,就是使用寄生式继承来继承超类型的原型,而后再将结果指定给子类型的原型。

function inheritPrototype(subType, superType){     
  var prototype = object(superType.prototype);     //建立对象 
	prototype.constructor = subType;                 //加强对象 
  subType.prototype = prototype;               //指定对象 
}

复制代码
相关文章
相关标签/搜索