ECMA-262 把对象定义为:“无序属性的集合,其属性可以
包含基本值、对象或者函数。
”可以把 ECMAScript 的对象想象成散列表
:就是一组名值对
,其中值可以是数据或函数
要修改属性默认的特性用Object.defineProperty() 方法。这个方法
接收三个参数
:属性所在的对象、属性的名字和一个描述符对象描述符对象的属性必须是: configurable 、 enumerable 、 writable 和 value
var person = {};
Object.defineProperty(person, "name", {
writable: false,
value: "Nicholas"
});
alert(person.name); //"Nicholas"
person.name = "Greg";
alert(person.name); //"Nicholas"
一旦把属性定义为不可配置的,就不能再把它变回可配置了在把 configurable等特性设置为 false 之后就会有限制了。
访问器属性
不包含数据值
;它们包含一对getter 和 setter 函数
(不过,这两个函数都不是必需的)
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.edition); //2
下划线_是一种记号,用于表示只能通过
对象方法
来访问的属性()
用字面量或者Object构造函数有个明显缺点:使用同一个接口创建很多对象会有很多重复代码,于是出现了多种模式
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("Nicholas", 29, "Software Engineer");
var person2 = createPerson("Greg", 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("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");
1 构造函数名字用一个大写字母开头,非构造函数用小写字母开头
2 创建该例子Person实例的时候,用new,因为这个和上面工厂模式区别有它没有返回值
因此this指向这个新对象
)constructor
或者instance of
),并且上个例子中person1/2是object的实例,因为对象均继承object任何函数,
只要能通过new
来调用,它就可以作为构造函数,而任何函数,如果不通过new调用,那么它和普通函数没有什么不同
但那两个方法不是同一个 Function 的实例
this.sayName = new Function("alert(this.name)");
// 与声明函数在逻辑上是等价的通过把函数定义转移到构造函数外部
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = sayName;
}
function sayName(){
alert(this.name);
}
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");
由于 sayName 包含的是一个指向函数的指针,因此 person1 和 person2 对象就共享了在
全局作用域
中定义的同一个 sayName() 函数
如果对象需要定义很多方法,那么就要定义很多个全局函数
所以看下面这个原型模式
是一个指针,
指向一个对象, prototype 就是通过调用构造函数而创建的那个对象实例的原型对象,可以让所有对象实例
共享它所包含的属性和方法
function Person(){
}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
alert(this.name);
};
var person1 = new Person();
person1.sayName(); //"Nicholas"
var person2 = new Person();
alert(Person.prototype.isPrototypeOf(person1)); //true
alert(Person.prototype.isPrototypeOf(person2)); //true
alert(Object.getPrototypeOf(person1) == Person.prototype); //true
alert(Object.getPrototypeOf(person1).name); //"Nicholas"
先从对象实例本身开始
,如果找到了则返回,否则继续搜索指针指向的原型对象
,即两次搜索通过实例访问
原型但是不能重写原型中的值,如果实例中添加一个属性,并且与原型中的同名,那么该属性会屏蔽原型中的属性
hasOwnProperty()
方法,可以知道自己访问的是实例属性还是原型属性person1.name = "Greg";
alert(person1.name); //"Greg"——来自实例
alert(person1.hasOwnProperty("name")); //true
alert(person2.name); //"Nicholas"——来自原型
alert(person2.hasOwnProperty("name")); //false
alert(typeof Array.prototype.sort); //"function"
alert(typeof String.prototype.substring); //"function"
还可以组合使用构造函数模式和原型模式,以及动态原型模式,寄生构造函数模式,稳妥构造函数模式等
接口继承和实现继承
ECMAScript 只支持实现继承,而且其实现继承主要是依靠原型链来实现的构造函数
有个原型对象
,原型对象都包含一个指向构造函数的指针
,实例
都包含一个指向原型对象的内部指针,如果让原型对象
=另一个
类型的实例
,那么此时的原型对象将包含指另一个原型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数假如另一个原型又是另一个类型的实例,层层递进,就构成了实例与原型的链条的指针console.log(people1.__proto__.__proto__.constructor);//Object(){……}
此段也可以参考https://www.cnblogs.com/ranyonsue/p/11201730.html
原型链
实现对原型
属性和方法的继承,借用构造函数
实现对实例
属性的继承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
组合继承避免了原型链和借用构造函数的缺陷
instanceof
和isPrototypeOf()
也能够用于识别基于组合继承创建的对象
浅复制
。而复制得到的副本
还可以得到进一步改造
。var person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = object(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");
var yetAnotherPerson = object(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");
alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"
也可用object.create()
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"
创建一个对象
,然后增强对象
,最后返回对象
function createAnother(original){
var clone = object(original); //通过调用函数创建一个新对象
clone.sayHi = function(){ //以某种方式来增强这个对象
alert("hi");
};
return clone; //返回这个对象
}
var person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = createAnother(person);
anotherPerson.sayHi(); //"hi"
多次调用
超类型构造函数而导致的低效率问题,并且集寄生式继承和组合继承的优点与一身function inheritPrototype(subType, superType){
var prototype = object(superType.prototype); //创建对象
prototype.constructor = subType; //增强对象
subType.prototype = prototype; //指定对象
}
1、本质上,就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型
3、开发人员普遍认为寄生组合式继承是引用类型最理想的继承范式