无序属性的集合, 其属性能够包含基本值、 对象或者函数javascript
ECMA-262定义了一些为实现JavaScript引擎用的属性,所以不能直接访问,为了表示其是特殊的内部值,把他们放在两对儿方括号中。例如[[[Enumerable]]。对象的属性分为数据属性和访问器属性java
通常数据属性的值都为基础数据类型。设计模式
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
复制代码
var person = new Object();
person.name = "JiangWen";
person.age = 29;
person.job = "Software Engineer";
person.sayName = function(){
alert(this.name);
};
复制代码
var person = {
name: "JiangWen",
age: 25,
job: "Software Engineer",
sayName: function(){
alert(this.name);
}
};
复制代码
虽然上面两种建立对象的方式,均可以用来建立单个对象,可是有个明显的缺点,使用同一接口建立不少对象,会产生大量的重复代码,所以就产生了不一样模式知足不一样状况下的需求prototype
工厂模式是软件工程领域一种广为人知的设计模式,用函数来封装以特定接口建立对象的细节设计
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");
复制代码
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
构造函数模式和工厂模式有如下几个不一样之处
咱们建立的每一个函数都有一个 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()函数,以下图所示:
一些关于原型的方法:
// 肯定实例对象和构造函数原型之间是否存在关系
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"
复制代码
该模式是建立自定义类型的最多见方式: 构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性
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
复制代码
能够经过检查某个应该存在的方法是否有效,来决定是否须要初始化原型,从而避免改写原型上原有的同名属性或方法
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()方法不存在于原型的状况下,才会将它添加到原型中
该模式的基本思想是建立一个函数,该函数的做用仅仅是封装建立对象的代码,而后再返回新建立的对象;
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 语句,能够重写调用构造函数时返回的值。
所谓稳妥对象,指的是没有公共属性,并且其方法也不引用 this 的对象。稳妥构造函数遵循与寄生构造函数相似的模式,但有两点不一样:一是新建立对象的实例方法不引用 this;二是不使用 new 操做符调用构造函数。
function Person(name, age, job){
//建立要返回的对象
var o = new Object();
//能够在这里定义私有变量和函数
//添加方法
o.sayName = function(){
alert(name);
};
//返回对象
return o;
}
复制代码
注意, 在以这种模式建立的对象中, 除了使用 sayName()方法以外, 没有其余办法访问 name 的值
在Javascript中实现继承主要是依靠原型链来实现的;其基本思想是经过原型实现一个引用类型继承另外一个引用类型的属性和方法。
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()
复制代码
子类型构造函数的内部调用超类型构造函数
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"
复制代码
将原型链和借用构造函数的技术组合到一块,从而发挥两者之长的一种继承模式。其背后的思路是使用原型链实现对原型属性和方法的继承,而经过借用构造函数来实现对实例属性的继承。这样,既经过在原型上定义方法实现了函数复用,又可以保证每一个实例都有它本身的属性。
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 中最经常使用的继承模式。
将须要被继承的对象挂载到函数内部新建立的构造函数原型上并返回该构造函数的实例
/* 模拟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"
复制代码
寄生式(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()函数不是必需的;任何可以返回新对象的函数都适用于此模式。
所谓寄生组合式继承,即经过借用构造函数来继承属性,经过原型链的混成形式来继承方法 ; 本质上,就是使用寄生式继承来继承超类型的原型,而后再将结果指定给子类型的原型。
function inheritPrototype(subType, superType){
var prototype = object(superType.prototype); //建立对象
prototype.constructor = subType; //加强对象
subType.prototype = prototype; //指定对象
}
复制代码