理解对象
- 内部属性特征:数据属性特征和访问器属性特征
- 数据属性特征
- [[Configurable]]:能否通过delete删除属性;能够修改属性的特征;能够把数据属性特征修改为访问器属性特征。默认为true
- [[Enumerable]]:能否通过for-in循环返回属性。默认为true
- [[Writable]]:能否修改属性的值。默认为true
- [[Value]]:这个属性的数据值。默认为undefined
- 访问器属性特征
- [[Configurable]]:能否通过delete删除属性;能够修改属性的特征;能够把访问器属性特征修改为数据属性特征。默认为true
- [[Get]]:读取属性时的行为。默认为undefined
-[[Set]]:写入属性时的行为。默认为undefined
- 修改或定义属性的特征:Object.defineProperty()
var person={};
Object.defineProperty(person,"name",{
writable:false,
value:"Chris"
});
var book={
_year:2014,
edition:1
};
Object.defineProperty(book,"year",{
get:function(){
return this._year;
},
set:function(newValue){
if(newValue>2014){
this._year=newValue;
this.edition+=newValue-2014;
}
}
})
- 只能通过对象方法访问的属性:前置下划线记号。
- 定义多个属性:Object.defineProperties()
- Object.getOwnPropertyDescriptor()
创建对象
function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
this.sayName=function(){
alert(this.name);
}
}
var o = new Object();
Person.call(o,"Kris',25,"Doctor");
Person.sayName();//"Kris"
- 原型模式
- 通过调用构造函数创建实例对象的原型对象
- 所有实例对象共享原型对象的属性和方法
- 任何函数都有prototype属性,这个属性指向函数的原型对象
- 原型对象会有一个constructor属性,这个属性指向拥有该prototype属性的函数
- 对象的实例拥有属性[[Prototype]],指向原型对象
- 所有实现都无法访问[[Prototype]],但是可以通过isPrototypeOf()方法来确定对象之间的关系。如果实例内部存在指向prototype的指针,那么isPrototypeOf()返回true。
Person.prototype.isPrototypeOf(person1)(确定实例与原型之间的关系)
- 通过
Object.getPrototypeOf()可以方便地取得一个对象的原型
- 不能通过实例对象重写原型中的值,实例对象与原型对象重名的属性会屏蔽掉原型对象中的属性
- hasOwnProperty()判断属性是否是实例中的,当给定属性存在于对象实例中时才返回true
- in操作符,单独使用时,无论属性存在于实例还是原型中时都返回true。只有当属性在实例和原型中都搜索不到时才会返回false。
- for-in循环会返回的是能通过对象访问的,可枚举的属性。屏蔽了原型中不可枚举的实例属性也会在for-in循环中返回。
- Object.keys()返回所有可枚举属性的字符串
- Object.getOwnPropertyNames()返回所有属性
function Person(){
Person.prototype.name="Kris";
Person.prototype.age=25;
Person.prototype.job="Doctor";
Person.prototype.sayName=function(){
alert(this.name);
}
}
function Person(){
}
Person.prototype={
name:"Kris",
age:29,
job:"Doctor",
sayName:function(){
alert(this.name);
}
}
//上面的操作改变了Person Prototype中constructor的指向(指向Object)。
Object.defineProperty(Person.prototype,"constructor",{
enumerable:false,//默认情况下,constructor是不可枚举的
value:Person//简化语法会导致constructor指向Object
})
- 原型模式的问题,省略了通过构造函数传递初始化参数这一环节。并且对于引用类型的属性,共享成为了问题。
- 组合使用构造函数模式和原型模式
function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
this.friend=["Bob","Kevin"]//使用构造函数定义实例属性
}
Person.prototype={
constructor:Person,
sayName:function(){
alert(this.name);
}//使用原型模式定义方法以及需要共享的属性
}
- 动态原型模式
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);
};
}
}
- 寄生构造函数模式
- 稳妥构造函数模式
继承
- js只支持实现继承,即extends。依靠原型链实现。
- 让原型对象等于另一个类型的实例。关键代码
SubType.prototype=new SuperType()
- SubType的原型指向SuperType的原型,SuperType原型的constructor指向SuperType。所以所有SubType实例的constructor都指向SuperType
- 确定原型与实例的关系
- instanceof
instance instanceof Object
- isPrototypeOf
Object.prototype.isPrototypeOf(instance)
- 子类重写父类的方法或者子类添加父类中不存在的方法,这些语句需要放在继承语句(即替换原型的语句之后)。并且不能使用对象字面量创建原型方法
- 原型链继承的问题
- 借用构造函数
function SubType(){
//支持传递参数
SuperType.call(this,"Kris");
//添加子类属性
this.age=29;
}
- 组合继承
- 使用原型链完成方法继承
- 使用借用构造函数完成属性和引用类型的继承
function SubType(name,age){
//继承属性
SuperType.call(this,name);
this.age=29;
}
SubType.prototype=new SuperType();
SubType.prototype.constructor=SubType;
SubType.prototype.sayName()=function(){
alert(this.name);
}
- 原型式继承:Object.create()
- 本质上是对传入参数对象进行一次浅复制,因此引用类型属性全实例共享
- 接受参数:一个用作新对象原型的对象和(可选的)一个为新对象定义额外属性
- 返回值:一个新对象,带着指定的原型对象和属性。
o = Object.create(Object.prototype, {
// foo会成为所创建对象的数据属性
foo: {
writable:true,
configurable:true,
value: "hello"
},
// bar会成为所创建对象的访问器属性
bar: {
configurable: false,
get: function() { return 10 },
set: function(value) {
console.log("Setting `o.bar` to", value);
}
}
});
- 寄生式继承
- 对父对象进行一次浅复制
- 在封装函数中对复制后的对象进行方法增强
- 无法做到函数复用
function createAnother(original){
var clone=Object.create(original);
clone.sayHi=function(){
alert("hi");
};
return clone;
}
- 寄生组合式继承
- 原有组合继承,无论什么情况下都会调用两次超类的构造函数
- 使用构造函数来继承属性,使用原型链的混成形式来继承方法
function inheritPrototype(subType,superType){
//获取父类的原型副本
var prototype=Object.create(superType.prototype);
//为创建的副本添加constructor
prototype.constructor=subType;
//将新创建的原型副本指定为子类的原型
subType.prototype=prototype;
}
function SubType(name,age){
SuperType.call(this,name);
this.age=age;
}
inheritPrototype(SubType,SuperType);
- 只调用一次父类的构造函数,避免在子类的原型上创建多余的属性。是引用类型最理想的继承范式