属性
ECMA-262 把对象定义为:无序属性的集合,其属性可以包含基本值、对象或者函数。
这里的属性包括两种类型:
数据属性
数据属性包含4个特性参数
1.configurable: 是否可以配置,通过点定义的属性默认值为true(如:obj.name),可配置包括是否可设置属性其他3个参数的值和属性是否可删除
2.writable:是否可写,通过点定义的属性默认值为true,如果为false表示属性是只读的
3.enumerable:是否可以通过for-in进行遍历,通过点定义的属性默认值为true
4.value:属性值
访问器属性
1.configurable: 是否可以配置,通过点定义的属性默认值为true(如:obj.name),可配置包括是否可设置属性其他3个参数的值和属性是否可删除
2.enumerable:是否可以通过for-in进行遍历,通过点定义的属性默认值为true
3.get:在读取属性时调用的函数。默认值为 undefined 。
4.set:在写入属性时调用的函数。默认值为 undefined 。
属性相关方法
1.Object.defineProperty() 定义属性,如果属性存在则设置属性(前提是configurable为true)
var person = {};
Object.defineProperty(person, "name", {
writable: false,
value: "zhagnsan"
});
2.Object.defineProperties() 定义多个属性
var person={};
Object.defineProperties(pereson,{
'name':{
configurable:true,
enumerable:true,
writable:true,
value:'zhangsan'
},
'age':{
configurable:true,
enumerable:true,
writable:true,
value:12
}
})
3.Object.getOwnPropertyDescriptor() 获取属性特性参数对象
var descriptor = Object.getOwnPropertyDescriptor(person, "name");
alert(descriptor.value); //zhangsan
alert(descriptor.configurable); //true
创建对象
创建简单的对象可以使用new Object()和对象字面量的方式,如果想要创建复杂的对象就必须使用构造函数模式和原型模式来创建
构造函数
构造函数和普通函数几乎没有区别,唯一区别就是构造函数通过new来执行,普通函数通过()来执行,任何函数都可以是构造函数也可以是普通函数,凡是通过new来执行的函数都是构造函数,凡是通过()执行的函数都是普通函数
//构造函数方式执行
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");
//普通函数凡是执行
Person("Greg", 27, "Doctor"); // 添加到 window
window.sayName(); //"Greg"
// 在另一个对象的作用域中调用
var o = new Object();
Person.call(o, "Kristen", 25, "Nurse");
o.sayName(); //"Kristen"
构造函数问题
每次执行构造函数都会创建一个实例,每个实例中都会创建一个sayName函数对象,如果这个sayName函数对象是一个共享的对象那就不行了,这个时候可以使用原型
原型
在函数对象中有个属性prototype,这个属性指向该函数的原型对象,而原型对象中有个属性constructor指向函数对象,当使用函数new实例时,实例中实际保存一个无法访问的属性[[pototype]]指向原型。通过函数new出来的实例都共享原型中的属性和方法
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();
person2.sayName(); //"Nicholas"
alert(person1.sayName == person2.sayName); //true
最终创建对象可以使用原型+构造函数的方式,将实例特有的属性和方法设置在构造函数中,将实例共享的属性和方法设置在原型中
继承
继承的常用方式有两种:
1.原型链+借用构造函数方式
2.寄生组合式继承
原型链+借用构造函数方式
原型链
原型链就是把一个构造函数的实例设置成另一个构造函数的原型,依次类推就形成了原型链
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function(){
return this.property;
};
function SubType(){
this.subproperty = false;
}
//继承了 SuperType
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function (){
return this.subproperty;
};
var instance = new SubType();
alert(instance.getSuperValue()); //true
继承链图片:
借用构造函数
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"
所谓借用构造函数就是在子类构造函数中调用父类的构造函数,调用时的执行环境是子类实例,这样就实现了子类继承了父类属性,而不是共享父类属性,由上面列子可以看出instance1和instance2中分别含有各自的colors属性(相当于继承),而不是共享父类的colors属性
最终写法
function Person(name,age,sex) {
this.name=name;
this.age=age;
this.sex=sex;
}
function Student(name,age,sex,school) {
Person.call(this,name,age,sex);
this.school=school;
}
Student.prototype=new Perso1();
var s1=new Student('zhagnsan',11,'nan','北京大学');
console.log([s1]);
寄生组合式继承
以上方式创建继承时person构造函数会被调用两次,第一次是 Student.prototype=new Person(); 第二次是Person.call(this,name,age,sex);解决这个问题的方式就是使用寄生组合式继承,说白了就是另找一个Object对象成为Person的实例,它的原型是Person的原型,通过这个思路可以创建一个函数来生成某个构造函数原型的实例,如:
function createInstance(objPrototype) { //传入一个对象
function F(){} //创建一个构造函数
F.prototype=objPrototype; //使构造函数的原型是传入的对象
return new F(); //返回这个对象
}
//通过上面这个函数可以快速创建一个某个对象为原型的实例
基于上面的方法,ECMAScript 5 通过新增 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 Person(name,age,sex) {
this.name=name;
this.age=age;
this.sex=sex;
}
function Student(name,age,sex,school) {
Person.call(this,name,age,sex);
this.school=school;
}
function initExtendsPrototype(superPrototype) {
var o=Object.create(superPrototype);
Student.prototype=o;
o.constructor=Student;
}
initExtendsPrototype(Person.prototype);
var s1=new Student('lisi',12,'nan','清华大学');
console.log(!s1.hasOwnProperty('name') && 'name' in s1);
console.log([s1]);
原理图: