创建对象
对象字面量形式创建对象
var colors = {
//...
};
复制代码
工厂模式
function Person(name,age,job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function (){
console.log(this.name);
}
return o;
};
var person1 = Person('李雪峰','26','web前端开发');
var person2 = Person('hq','25','摄影师');
复制代码
构造函数模式
function Animal(name,color){
this.name = name;
this.color = color;
thia.sayName = function(){
console.log(this.name);
}
}
var dog = new Animal('旺财','黑色')
var dog2 = new Animal('阿拉斯加','黑白相间')
复制代码
原型模式
function Animal(){};
Animal.prototype.name = "旺财";
Animal.prototype.age = 3;
Animal.prototype.sayName = function(){
console.log(this.name);
};
var dog = new Animal();
dog.sayName(); //旺财
var dog2 = new Animal();
dog2.sayName(); //旺财
console.log(dog.sayName === dog2.sayName) //true
复制代码
组合使用构造函数模式和原型模式
function Animal(name,age,color){
this.name = name;
this.age = age;
this.color = color;
this.friends = ['旺旺','旺财','二哈']
}
Animal.prototype.sayName = function(){
console.log('狗狗的名字叫:'+this.name);
}
var dog = new Animal('二狗子','3','黑色');
dog.friends.push('陈二狗'); //陈二狗被黑的最惨的一次
console.log(dog.friends)
console.log(dog.sayName())
var dog2 = new Animal('狗腿子','10','五颜六色');
dog2.friends.push('走狗');
console.log(dog2.friends);
console.log(dog2.sayName())
console.log(dog.friends == dog2.friends) //false
console.log(dog.sayName == dog2.sayName) //true
复制代码
动态原型模式
function Person(name,age,job){
//属性
this.name = name;
this.age = age;
this.job = job;
//方法
if(typeof this.sayName != 'function'){
Person.prototype.sayName = function(){
console.log(this.name);
}
}
}
var friend = new Person('李雪峰','26','web前端开发');
friend.sayName();
复制代码
寄生构造函数模式
function Person(name,age,job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
console.log(this.name);
}
return o;
}
// 比工厂模式多了个new调用, 并且把Person叫做了构造函数
var friend = new Person('李学峰','26','web前端开发');
friend.sayName();
// 应用
function SpecialArray(){
//创建数组
var values = new Array();
//添加值
values.push.apply(values,arguments);
//添加方法
values.toPipedString = function(){
return this.join('||');
};
//返回数组
return values;
}
var colors = new SpecialArray('red','pink','orange','blue');
console.log(colors.toPipedString()); //red||pink||orange||blue
复制代码
稳妥构造函数模式
- 变量Person中保存的是一个稳妥对象,而除了调用sayName()
- 没有别的方式可以访问其数据成员.
function Person(name,age,job){
// 创建要返回的对象
var o = new Object();
//可以在这里定义私有变量和函数
o.sayName = function(){
console.log(name);
}
return o;
}
var friend = Person('lxf','111','web');
friend.sayName();
复制代码
继承
- 接口继承和实现继承.
- ECMAScript只支持实现继承,而且其实现继承的主要是依靠原型链来实现的
- 原型链的概念
原型链的概念: 其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法
这里要先说一下: 构造函数/原型/实例的关系
每个构造函数都有一个
原型对象
,原型对象都包含一个指向构造函数的指针 constructor
,而实例
都包含一个指向原型对象的内部指针[[proto]](__proto__)
.
假如我们让
原型对象
等于另一个类型的实例
,结果就是,此时的原型对象
将包含一个指向另一个另一个原型的指针
假如另一个
原型
又是另一个类型
的实例,那么上述关系依然成立,如此层层递进,就构成了实例与原型的链条
. 这就是所谓原型链的基本概念
//实现一个原型链
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();
console.log(instance.getSuperValue)
复制代码
- 要注意这里
instance.constructor
现在指向的是SuperType
,这是因为原来SubType.prototype 中的constructor 被重写
的缘故 实际上
,不是SubType的原型的 constructor 属性被重写了,而是 SubType 的原型指向了另一个对象 SuperType 的原型
,而这个原型对象的 constructor 属性指向的是 SuperType
引用红宝书中的图片说明
通过实现原型链,本质上扩展了前面介绍的原型搜索机制,
当以读取模式访问一个实例属性时
首先会在实例
中搜索该属性.
如果,没有找到该属性,则会继续搜索实例的原型.
在通过原型链实现继承的情况下,搜索过程就得沿着
原型链继续向上
.
上边的例子中的搜索顺序
- 搜索实例
- 搜索SubType.prototype
- 搜索SuperType.prototype
- 如果找不到属性或方法的情况下,搜索过程总是要一环一环的前行到原型链额末端才会停下来
引用红宝书中的图片说明
确定原型和实例的关系
第一种:
- 使用
instanceof
操作符
这个操作符来测试实例与原型中出现过的构造函数,结果就会返回true
console.log(instance instanceof Object); //true
console.log(instance instanceof SubType); //true
console.log(instance instanceof SuperType); //true
复制代码
由于原型链的关系,可以输 instance 是 Object | SuperType | SubType 中任何类型的实例
第二种:
isPrototypeOf()方法
同样,只要是
原型链中出现过的原型
,都可以说是该原型链所派生的实例的原型
console.log(Object.isPrototypeOf(instance)); // true
console.log(SuperType.isPrototypeOf(instance)); // true
console.log(SubType.isPrototypeOf(instance)); // true
复制代码
原型链的问题
- 一个是通过原型来实现继承时,包含引用类型值的原型,会共享
- 一个是: 在创建子类型的实例时,不能向超类型的构造函数中传递参数
借用构造函数
- apply() 和 call()
function SuperType(nane){
this.name = name;
this.colors = ['red','pink','orange','green'];
}
function SubType(nane,age){
SuperType.call(this); // 不传递参数的情况
//SuperType.call(this,name); //传递参数
//SuperType.apply(this,arguments); // 传递参数
// 实例属性
this.age = age;
}
var instance1 = new SubType();
instance1.colors.push('blue');
console.log(instance1.colors);
var instance2 = new SubType();
console.log(instance2.colors)
复制代码
- 问题
方法都在构造函数中定义,一次函数复用就无从谈起.
组合继承
function SuperType(name){
this.name = name;
this.colors = ['red','blue','pink'];
}
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 = SubType; // 构造函数回指
SubType.prototype.sayAge = function (){
console.log(this.age);
}
var instane1 = new SubType('lixuefeng','26');
instane1.colors.push('green','222');
console.log(instane1.sayName())
console.log(instane1.sayAge())
console.log(instane1.colors)
var instane2 = new SubType('zhangyu','23');
instane2.colors.push('122','#fff');
console.log(instane2.sayName())
console.log(instane2.sayAge())
console.log(instane2.colors)
复制代码
组合继承避免了原型链和借用构造函数的缺陷,融合了他们的优点,
instanceof
和isPrototypeOf()
也能够用于识别组合继承创建的对象
原型式继承
function object(o){
function F(){};
F.prototype = o;
return new F();
}
// 应用
var person = {
name : 'lixuefeng',
friends:['red','pink','blue']
};
var anotherPerson = object(person);
anotherPerson.name = 'Greg';
anotherPerson.friends.push('Rob');
var yetAnotherPerson = object(person);
yetAnotherPerson.name = '你你你';
yetAnotherPerson.friends.push('#fff');
console.log(person.friends)
复制代码
- ECMAScript5 通过
Object.create()
方法规范化了原型式继承. - 这个方法接受连个参数:
- 一个用作新对象原型的对象
- (可选的)一个为新对象定义额外属性的对象.
- 在传入一个参数的情况下,
Object.create()
与object()
方法的行为相同
一个参数的时候
var person = {
name : 'lixuefeng',
friends:['red','pink','blue']
};
var anotherPerson = Object.create(person);
anotherPerson.name = 'Greg';
anotherPerson.friends.push('Rob');
var yetAnotherPerson = Object.create(person);
yetAnotherPerson.name = '你你你';
yetAnotherPerson.friends.push('#fff');
console.log(person.friends)
复制代码
两个个参数的时候
var person = {
name : 'lixuefeng',
friends:['red','pink','blue']
};
var anotherPerson = Object.create(person,{
name:{
value:'<<离人愁>> ==> 李元杰'
}
});
console.log(anotherPerson.name)
复制代码
寄生式继承
function createAnother(original){
var clone = Object.create(original); // 通过调用函数创建一个新对象 返回一个传入对象的新的实例赋值给 clone
clone.sayHi = function(){ // 以某种方式来增强这个对象
console.log('hi');
};
return clone; // 返回这个对象
}
var person = {
name : 'xxname',
friends:['red','blue','van']
};
var anotherPerson = createAnother(person);
anotherPerson.sayHi(); // hi
anotherPerson.friends.push('11','sss')
console.log(anotherPerson.friends)
console.log(anotherPerson.name)
复制代码
寄生组合式继承
- 构造函数模式+原型模式继承的缺点
function SuperType(name){
this.name = name;
this.colors = ['red','blue','pink','orange'];
}
SuperType.prototype.sayName = function(){
console.log(this.name);
}
function SubType(name,age){
SuperType.call(this,name); // 这里调用一次 SuperType()
//SuperType.apply(this,arguments)
this.age = age;
}
SubType.prototype = new SuperType(); // 这里调用一次 SuperType()
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function (){
console.log(this.age);
}
var instance =new SubType('小明','100');
instance.colors.push('#333');
console.log(instance.sayName());
console.log(instance.sayAge());
console.log(instance.colors);
var instance2 = new SubType('小蓝','122312');
instance2.colors.push('#fff','#ff0000');
console.log(instance2.sayName());
console.log(instance2.sayAge());
console.log(instance2.colors);
复制代码
-
在第一次调用
SuperType
构造函数时,SubType.prototype
会得到两个属性:- name
- colors
- 他们都是
SuperType
的实例属性,只不过现在位于SubType
的原型中
-
当调用SubType构造函数时,又会调用一次 SuperType构造函数,这一次又在新对象上创建了实例属性
name
和colors
于是,这两个属性就屏蔽了原型中的两个同名属性
寄生构造模式可以解决这个问题
function inheritPrototype(subType,superType){
var prototype = Object.create(superType.prototype); // 创建对象
prototype.constructor = subType; // 增强对象
subType.prototype = prototype; // 指定对象
}
function SuperType(name){
this.name = name;
this.colors = ['red','blue','pink','orange'];
}
SuperType.prototype.sayName = function(){
console.log(this.name);
}
function SubType(name,age){
SuperType.call(this,name); // 这里调用一次 SuperType()
//SuperType.apply(this,arguments)
this.age = age;
}
//SubType.prototype = new SuperType(); // 这里调用一次 SuperType()
//SubType.prototype.constructor = SubType;
inheritPrototype(SubType,SuperType);
SubType.prototype.sayAge = function (){
console.log(this.age);
}
var instance =new SubType('小明','100');
instance.colors.push('#333');
console.log(instance.sayName());
console.log(instance.sayAge());
console.log(instance.colors);
var instance2 = new SubType('小蓝','122312');
instance2.colors.push('#fff','#ff0000');
console.log(instance2.sayName());
console.log(instance2.sayAge());
console.log(instance2.colors);
复制代码
这个例子只调用了一次
SuperType
构造函数,并且因此避免了在SubType.prototype
上面创建不必要的,多余的属性。于此同时,原型链还能保持不变。
因此,还能够正常使用
instanceof
和isPrototypeOf()
寄生组合式继承是引用类型最理想的继承范式
记录理解,有不对的地方,欢迎指出