一.定义属性
三.继承
//对象Object中的属性(property)有4个Attribute:
//分别为[Configurable]:能否通过delete删除该property
//[Enumerable]:能否通过for-in遍历该property
//[Writable]:能否修改property的值
//[Value]:默认为undefinded.当age="11"时,[Value]即为11
//通过Object.defineProperty()修改默认property的4种attribute
var myPerson={};
//调用该方法时,若不指定,默认都是false
Object.defineProperty(myPerson,"age",{
configurable:false,
enumerable:false,
writable:false,
value:"zxx"
});
console.log(myPerson.age);
//删除age属性.当指定configurable为false时.删除无效.
delete myPerson.age;
console.log(myPerson.age);
//当指定enumerable为false时不可迭代
for(prop in myPerson){
console.log(prop+":"+myPerson[prop]);
}
myPerson.age="1122";
//当指定writable为false时,不可修改
console.log(myPerson.age);
二.创建对象
//(1)
var person=new Object();
person.name="zc";
person.age=1;
person.sayHello=function(){
console.log(person.name);
}
//(2)字面量方式
var people={
name:"zx",
age:11,
sayHello:function(){
console.log(this.name);
}
};
//(3)
var man={};
Object.defineProperty(man,"name",{
writable:false,
value:"zx"
});
console.log(man.name);
man.name=1;//修改无效,因为writable为false
console.log(man.name);
console.log("--getter,setter--");
//定义多个属性
//(4)工厂模式.缺点,无法得知获取的对象的具体类型
function createPerson(name,age,job){
var o=new Object();
o.name=name;
o.age=age;
o.sayName=function(){
console.log(this.name);
}
return o;//返回Person对象
}
var person1=createPerson("zx",11,"jj");
//(5)构造函数模式,构造器.缺点每次new创建的都是新的对象
//任何函数,只要通过new操作符来调用,都可以作为构造函数
//所谓"构造函数",其实就是一个普通函数,但是内部使用了this变量。
//对构造函数使用new运算符,就能生成实例,并且this变量会绑定在实例对象上。
//缺点:每个方法都要在 每个实例上重新创建一遍
function Person(name,age,job){
this.name=name;
this.age=age;
//其实相当于this.sayName=new Function(){console.log(this.name);}
this.sayName=function(){
console.log(this.name);
}
}
var person2=new Person("zx",11,"sf");
console.log(person2.constructor==Person);//true
console.log(person2 instanceof Person);
//(6)创建原型对象
//每个函数都有一个prototype属性,该属性是一个指针,指向一个对象,
//该对象的用途是包含可以由特定类型的 所有实例共享的属性和方法
//原型可以让所有实例对象共享 该原型 包含的属性和方法
//缺点:对于基本数据类型的原型属性,可以通过在对象中添加同名属性来屏蔽
//但是对于 引用类型的 原型属性,问题突出
function Teacher(){
//prototype属性指向函数的 原型对象
//定义原型属性的第一种方式
Teacher.prototype.name="ZXX";
};
//如果此时先创建一个对象,再完全重写原型
var oldTeacher=new Teacher();
//定义原型属性的第二种方式
//这种方式定义原型,相当于重写了prototype函数.
//此时Teacher.prototype.constructor不再指向Teacher函数,而是指向Object构造函数
Teacher.prototype={
//可以直接指定constructor属性,但这样导致constructor变得可以枚举
//constructor:Teacher,
name:"nick",
age:25,
job:"he",
sayName:function(){
console.log(this.name);
}
};
//使用这种方式,重设原型中的constructor属性,可以设置为不可枚举
Object.defineProperty(Teacher.prototype,"constructor",{
enumerable:false,
value:Teacher
});
//由于重写原型对象(相当于new了) 发生 在 创建该对象之后,
//该对象中隐含的[[prototype]]属性指向的还是之前的原型对象,所以报错,找不到该方法
//若不完全重写原型对象,而是只向原型对象中添加某个方法,不会发生这种情况
//oldTeacher.sayName();
//所有原型对象都拥有一个constructor属性
console.log(Teacher.prototype.constructor);
var teacher1=new Teacher();
console.log(Teacher.prototype.isPrototypeOf(teacher1));//true
console.log(Object.getPrototypeOf(teacher1)==Teacher.prototype);//true
//读取对象的某个属性的流程:实例对象(person1有sayName属性吗)->原型对象(person1的原型有sayName属性吗)
//teacher1对应的原型为Teacher
console.log(Object.getPrototypeOf(teacher1));
//实例可以覆盖原型中的属性.让原型中的属性失效.(但是不会修改原型中的属性值)
teacher1.name="newNickccc";
console.log(teacher1.name);//newNickccc
//delete 可以删除对象实例中的属性,却不能删除原型中属性
delete teacher1.name;
console.log(teacher1.name);//nick
//hasOwnProperty检测一个属性来自实例还是来自prototype
console.log(teacher1.hasOwnProperty("name"));//
//in,只要一个属性可以访问,无论是实例还是来自prototype均返回true
console.log("name" in teacher1);//true
//同时使用hasOwnProperty()和in操作符,可以确定该属性到底存在于对象还是原型中
function myHasPrototypeProperty(obj,name){
return !obj.hasOwnProperty(name)&&(name in obj);
}
//keys:返回实例中所有 可枚举 的属性的字符串数组
console.log(Object.keys(teacher1));
//无论实例属性是否可以枚举,都可以获取,传入的是对象
console.log(Object.getOwnPropertyNames(teacher1));
//原生引用类型(Object,Array,String等)都是在其构造函数的原型上定义了方法.
console.log(typeof Array.prototype.sort);//function
//若Array.prototype中有sort()方法
//可修改,或添加方法
String.prototype.zc=function(){
console.log("这是自定义zcString中的方法");
}
var strz="zc";
strz.zc();
//(7)构造函数模式 与 原型模式结合
//构造函数定义每个实例私有的属性
function Student(name,age){
this.name=name;
this.age=age;
this.frineds=["a","b"];
}
//定义实例共有的属性
Student.prototype={
constructor:Student,
sayName:function(){
console.log(this.name);
}
}
var stu1=new Student("zxx",23);
var stu2=new Student("zax",24);
stu1.frineds.push("c");
console.log(stu1.frineds);
console.log(stu2.frineds);
console.log(stu1.frineds==stu2.frineds);//false
console.log(stu1.sayName==stu2.sayName);//true
三.继承
//(1)原型链
//缺点:仅仅使用原型继承,依然是会导致引用类型属性 共享 的 问题
function SuperType(){
this.name="super类型";
age=111;
}
SuperType.prototype.getSuperValue=function(){
return this.name;
}
function SubType(){
this.subName="sub类型";
}
//继承SuperType.!!!!继承实现的本质是 重写原型对象 为 SuperType的实例对象
//即 原来存在于SuperType的实例中的所有属性和方法,现在也存在于SubType.prototype中了
//此时若调用子类型中的某个方法,会按照subType->superType(此时即为suType的原型)->superType.prototype->Object->Object.prototype寻找
//不能使用字面量方式实现继承,因为这样相当于重写了prototype函数
SubType.prototype=new SuperType();
//在确立了继承关系后,定义子类型自己的getSubValue方法。这样就在继承了SuperType的属性和方法的基础上又添加了一个新方法
SubType.prototype.getSubValue=function(){
return this.subName;
}
//重写父类中的getSuperValue方法.子类的prototype指向的是父类的实例
SubType.prototype.getSuperValue=function(){
return "zx子类重写后的方法";
}
//子类型调用重写后的方法
var sub=new SubType();
console.log(sub.getSuperValue());//zx子类重写后的方法
//父类型调用重写后的方法
var super2=new SuperType();
console.log(super2.getSuperValue());//输出仍然 是 super中的方法
//创建子类型
var instance=new SubType();
console.log(instance.getSuperValue());
console.log(instance.getSubValue());
console.log(instance instanceof SubType);//true
console.log(instance instanceof SuperType);//true
console.log(instance instanceof Object);//true
console.log(instance.name);
//继承(2)借用构造器 call (),优点是可以向父类构造方法传参
//实现:在子类型构造函数内部调用超类构造函数.
function Sup(name){
this.name=name;
this.colors=["red","blue","green"];
}
function Sub(){
//优点:可在子类型创建时向父类型的构造方法中传递参数
//问题:方法都定义在构造函数中,无法复用.
//且在超类中定义的方法,对子类型不可见(因为只在子类型的构造函数中调用了父类型的构造函数,其他地方,子类型没有父类型的指针).
Sup.call(this,"xiaoz");
}
var instance1=new Sub();
instance1.colors.push("black");
//子类型可以直接调用父类中的属性
console.log(instance1.colors);
var instance2=new Sub();
console.log(instance2.colors+instance2.name);
//!!!继承(3)组合继承 即 原型链+借用构造函数
console.log("--组合继承--");
//使用原型链实现对原型 属性 和 方法 的 继承.借用构造函数 实现 对 实例属性的继承
function SuperClass(name){
this.name=name;
this.colors=["red","blue","green"];
}
SuperClass.prototype.sayName=function(){
console.log(this.name);
}
//子类
function SubClass(name,age){
SuperClass.call(this,name);//继承父类中的属性
this.age=age;
}
//继承方法
SubClass.prototype=new SuperClass();
//定义子类方法
SubClass.prototype.sayAge=function(){
console.log(this.age);
}
//子类对象1
var s1=new SubClass("nick",22);
s1.colors.push("black");
//[red","blue","green","black"]
console.log(s1.colors);//由于调用构造器,属性均为每个实例私有的.互不影响
//方法定义在了原型链中,为所有实例共享,相当于java中的只有一份方法
s1.sayName();
s1.sayAge();
//子类对象2
var s2=new SubClass("nzdd",44);
//[red","blue","green"]
console.log(s2.colors);
s2.sayName();
s2.sayAge();
console.log(s1 instanceof SubClass);
console.log(s1 instanceof SuperClass);
console.log(s1 instanceof Object);
console.log(s2 instanceof SubClass);
console.log(s2 instanceof SuperClass);
console.log(s2 instanceof Object);
//继承(4) 原型式继承
function createObject(o){
//创建临时性的构造函数F
function F(){}
//将传入的参数o作为该构造函数的原型
F.prototype=o;
//返回这个临时类型作为一个新的实例
return new F();
}
var person4={
name:"zxxx",
friends:["selly","tom","kitty"]
};
//返回的anotherPerson对象以person4对象为原型.
//所以anotherPerson的prototype中包含 person4中的属性
var anotherPerson=createObject(person4);
anotherPerson.name="Greg";
anotherPerson.friends.push("Rob");
var yetPerson=createObject(person4);
yetPerson.name="Linda";
yetPerson.friends.push("Brr");
//为何string类型值没变,Array类型值改变了??!!
//!!!!原型中所有引用类型的变量为所有实例共享,基本类型则为私有
console.log(person4.name);//"zxxx"
console.log(person4.friends);//["selly", "tom", "kitty", "Rob", "Brr"]
//ECMAScript5通过新增Object.create(作为新对象原型的对象,作为新对象额外属性的对象-可选) 规范化了原型式继承
var person41={
name:"zxxx",
friends:["selly","tom","kitty"]
};
//[1]传入一个参数
var anotherPerson1=Object.create(person41);
anotherPerson1.name="Greg";
anotherPerson1.friends.push("Rob");
var yetPerson1=Object.create(person41);
yetPerson1.name="Linda";
yetPerson1.friends.push("Brr");
console.log(person41.name);//"zxxx"
console.log(person41.friends);//["selly", "tom", "kitty", "Rob", "Brr"]
//[2]传入两个参数
var anotherPerson2=Object.create(person41,{
name:{
value:"Grrrrr"
}
});