标识符指变量,函数,属性的名字,或者函数的参数;
对象是引用类型的值,是引用类型的一个实例;引用类型是一种数据结构;
所有引用类型默认都继承自Object类型,所有对象都继承自Object(JS高程);
创建对象的方式
1.工厂模式
原理:在普通函数中创建一个对象,通过函数传参至对象内,返回对象;调用函数即可返回一个对象;
缺点:没有对象识别,及没有对象类型;
例子:
//1.工厂模式
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");
2.构造函数模式
原理:
使用new操作符调用构造函数;
将构造函数的作用域赋给新对象(this指向新对象);
构造函数内部通过参数和this为新对象添加属性;
返回新对象;
缺点:每个实例对象的方法不是同一个Function实例;若将方法定义为全局函数又无任何封装性可言;
例子:
//2.构造函数模式
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");
}
3.原型模式
原理:
定义构造函数
通过函数的prototype指针修改原型对象中的属性和方法
缺点:所有属性在原型对象中修改,导致所有属性被所有实例共享;
//3.原型模式
function Person(){
}
Person.prototype = {
name:"Nicholas",
age:29,
job:"Software Engineer",
sayName:function(){
alert(this.name);
}
};
var person1 = new Person;
var person2 = new Person;
person1.sayName();//"Nicholas"
person2.sayName();//"Nicholas"
alert(person1.sayName == person2.sayName)//true
4.组合使用构造函数模式和原型模式
原理:
实例属性在构造函数中定义
共享属性和方法在原型中定义
//组合使用构造函数模式和原型模式
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
5.动态原型模式
原理:在构造函数中初始化原型,且仅在必要的时候,即仅在构造函数中不存在此方法的时候,往原型中添加,保持了同时使用构造函数和原型的优点;
例子:
//5.动态原型模式
function Person (name,age,job){
//属性
this.name = name;
this.age = age;
this.job = job;
this.sayName = function() {
alert('Matthew');
}
//方法
if(typeof this.sayname != 'function') {
//若上面的sayName不存在,则在原型中添加此方法
Person.prototype.sayName = function() {
alert(this.name);
}
}
}
var friend = new Person('Nicholas',29,'Software Engineer');
friend.sayName();//'Matthew'
6.寄生构造函数模式
原理:
同工厂模式的函数相同;
调用时使用new通过构造函数创建对象;
由于在构造函数中返回了一个对象,因此构造函数本身不需要再返回实例;即重写了调用构造函数时返回的值;
因此,构造函数返回的对象与构造函数原型没有关系;
优点:
给已有的对象添加方法并返回,为新对象创建构造函数,比如创建一个具有额外方法的数组;
缺点:
返回的对象与构造函数原型没有任何关系,因为在构造函数中返回了对象重写调用构造函数时本应该返回的值;
例子:
function SpecialArray(){
//创建数组
var values = new Array();
//添加值
values.push.apply(values,arguments);
//添加方法
values.toPipedString = function(){
return this.join("|");
};
//返回数组
return values;
}
var colors = new SpecialArray("red","blue","green");
alert(colors.toPipedString());
7.稳妥构造函数模式
原理:
在函数中创建一个对象;
在函数中定义一个方法访问其中的属性;
返回对象;
调用时不使用new,通过普通函数返回对象;
优点:除在函数中定义的方法外,没有其他方法可以访问属性,安全;
例子:
function Person(name,age,job){
var o = new Object();
o.sayName = function(){
alert(name);
}
return o;
}
var friend = Person("Nicholas",29,"Software Engineer");
friend.sayName();//"Nicholas"
继承
1.原型链
通过将子引用类型的原型指向父引用类型的实例来继承;
//1.原型链继承
function SuperType() {
this.property = true;
}
SuperType.prototype.getSuperValue = function() {
return this.property;
}
function SubType() {
this.subproperty = false;
}
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function() {
return this.subproperty;
}
var instance = new SubType();
console.log(instance.getSuperValue());//true
缺点:
1.所有的子引用类型的实例都公用父引用类型中构造函数所定义的属性,因为子引用类型的原型就是父引用类型的实例,在上面的例子中,这个属性就是property
;
2.在创建子类型的实例时,没有办法在不影响所有实例对象的情况下,给父类型构造函数传递参数;
2.借用构造函数
通过call在子类型构造函数中调用父类型构造函数;
function SuperType(name) {
this.name = name;
}
function SubType() {
SuperType.call(this,'Nicholas');
//实例属性
this.age = 29;
}
var instance = new SubType();
console.log(instance.name);//'Nicholas'
console.log(instance.age);//29
缺点:
方法都在构造函数中定义,无法函数复用,而在原型中子类型又无权访问;
3.组合继承
即将原型链继承和借用构造函数组合到一起;
属性使用借用构造函数,方法使用原型链;
//3.组合继承
function SuperType(name) {
this.name = name;
this.colors = ['red','blue','green'];
}
SuperType.prototype.sayName = function() {
console.log(this.name);
}
function SubType(name,age) {
SuperType.call(this,name);
this.age = age;
}
SubType.prototype = new SuperType();
SubType.prototype.constructor = SuperType;
SubType.prototype.sayAge = function() {
console.log(this.age);
}
var instance1 = new SubType('Nicholas',29);
instance1.colors.push('black');
console.log(instance1.colors);//'red,blue,green,black'
instance1.sayName();//'Nicholas'
instance1.sayAge();//29
var instance2 = new SubType('Greg',27);
console.log(instance2.colors);//'red,blue,green'
instance2.sayName();//'Greg'
instance2.sayAge();//27
4.原型式继承
原型式继承和原型链继承的主要区别就是,不使用构造函数(其实是通过函数new),通过一个简单的函数object
实现(ES5已有此方法Object.create()
);比较轻便;
//4.原型式继承
function object(o) {
function F(){};
F.prototype = o;
return new F();
}
var person = {
name:'Nicholas',
friends:['Shelby','Court','Van']
}
var anotherPerson = object(person);
anotherPerson.name = 'Greg';
anotherPerson.friends.push('Rob');
console.log(person.friends);//'Shelby,Court,Van,Rob'
我们修改实例的name
和friends
,在person
里会修改friends
但是name
没有任何影响,这是因为friends
是引用类型的值,和原型链的继承一样,原型上引用类型的值(方法、数组等)修改会在所有的实例中反映出来,所以方法(引用类型)能够共用;
但是name
是基本类型的值,基本类型值无法修改,每次都是重新创建变量(会在内存中新分配位置),赋值的同时就相当于在实例上创建属性了。
var anotherPerson = object(person);
console.log(anotherPerson.name); // Nicholas
anotherPerson.name = 'Greg';
anotherPerson.friends.push('Rob');
console.log(person.name); // Nicholas
console.log(person.friends); //'Shelby,Court,Van,Rob'
缺点:
所有通过此函数继承的对象都共用原对象属性;和原型链继承相同;
5.寄生式继承
在原型式继承上进行了增强;除了拥有原对象上的方法和属性,还可以自定义自己的方法和属性;
//5.寄生式继承
function createObj(o) {
var clone = Object.create(o);
clone.sayHi = function() {
console.log('hi');
}
return clone;
}
var person = {
name:'Nicholas',
friends:['Shelby','Court','Van']
}
var anotherPerson = createObj(person);
anotherPerson.sayHi();//'hi'
缺点:
方法不能复用,降低效率,和构造函数类似;
6.寄生组合式继承
组合式继承会在SubType.prototype = new SuperType();
和SuperType.call(this,name);
时分别第一次和第二次调用SuperType()
构造函数,会降低效率;因此,使用寄生组合式继承:
function SuperType(name){
this.name = name;
this.colors = ['red','blue','green'];
}
SuperType.prototype.sayName = function() {
console.log(this.name);
}
//用此方法代替构造函数实例赋值的方法
function inheritPrototype(SubType,SuperType) {
var prototype = Object(SuperType.prototype);
prototype.constructor = SubType;
SubType.prototype = prototype;
}
inheritPrototype(SubType,SubperType);
SubType.prototype.sayAge = function() {
console.log(this.age);
}
总结
构造对象的方法,普遍使用组合构造函数和原型链模式或动态原型模式,继承,则普遍使用组合式继承或寄生组合式继承
--整理自《Javasctipt高级程序设计》