1.理解对象
1.1.new 操作符 + Object 创建对象
var person = new Object();
person.name = 'lvxin';
person.age = 25,
person.job = 'UI development';
person.sayName = function() {
console.log(this); // Object--> {name: 'lvxin', age: 25, job: 'UI development', sayName:function}
console.log(this == person); //true
console.log(this.name); //lvxin
}
console.log(person.sayName());
1.2.字面式创建对象
var person = {
name: 'lvxin',
age: 25,
job: 'UI Development',
sayName: function() {
console.log(this); // Object--> {name: 'lvxin', age: 25, job: 'UI development', sayName:function}
console.log(this == person); // true
console.log(this.name); //lvxin
}
}
console.log(person.sayName());
可扩展this指向问题。
apply和call的用法和区别
以上两种方法在使用同一接口创建多个对象时,会产生大量重复代码,为了解决此问题,工厂模式被开发。
2.创建对象
2.1.工厂模式
- 无法确定对象的类型,每次都是调用Object
- 创建多个对象之间没有关联
function createPerson(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 = createPerson('lvxin',18,'web');
var person2 = createPerson('xuyuqiu',20,'web');
console.log(typeof person1);//Object 只能知道是Object类型,不知道是其他具体得Person
console.log(typeof person2);//Object
console.log(person1 === person2);//false
console.log(person1.sayName === person2.sayName)//false
console.log(person1 instanceof Object); //true
工厂模式解决了重复实例化多个对象的问题,但没有解决对象识别的问题(但是工厂模式却无从识别对象的类型,因为全部都是Object,不像Date、Array等,本例中,得到的都是o对象,对象的类型都是Object,因此出现了构造函数模式)。
2.2.构造函数模式
对比工厂模式有以下不同之处:
- 没有显式地创建对象
- 直接将属性和方法赋给了 this 对象
- 没有 return 语句
以此方法调用构造函数步骤 {
1. 创建一个新对象
2. 将构造函数的作用域赋给新对象(将this指向这个新对象)
3. 执行构造函数代码(为这个新对象添加属性)
4. 返回新对象
}
function Person(name,age,job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = function() {
console.log(this.name);
}
}
var person1 = new Person('lvxin',18,'web');
var person2 = new Person('xuyuqiu',20,'web');
console.log(person1.constructor == Person);//true
console.log(person2.constructor == Person);//true
// 创建所有的对象都是Object实例,因为对象均继承Object
console.log(person1 instanceof Person);//true
console.log(person1 instanceof Object);//true
console.log(person2 instanceof Person);//true
console.log(person2 instanceof Object);//true
问题: 多个实例重复创建方法,无法共享,多个实例都有sayName方法,但均不是同一个Function的实例。
- 每一个实例都要重新创建一遍,person1和person2都有sayName的方法,但是是两个Function实例。
- sayName方法定义到外部来,就是全局的,那么person1和person2就是同一个方法了。
解决方法:
function Person(name,age,job) {
this.name = name;
this.age = age;
this.job = job;
//this.sayName = function() {
// console.log(this.name);
//};
//this.sayName = new Function("console.log(this.name)");
this.sayName = sayName;
}
function sayName() {
console.log(this.name);
}
var person1 = new Person('lvxin',18,'web');
var person2 = new Person('xuyuqiu',20,'web');
console.log(person1.sayName == person2.sayName);//true
2.3.原型模式
2.3.1.原型模式
- 每个函数(构造函数)都有一个prototype(原型)属性
- prototype(原型)属性是一个指针,指向一个对象
- prototype(原型)属性的这个对象包括所有特定类型(自己定义的构造函数)的所有实例(new自己定定义的函数)的属性和方法。
//空的构造函数
function Person () {
}
//所有Person的实例共享这些属性和方法
Person.prototype.age = 18;
Person.prototype.name = "lvxin";
Person.prototype.jos = "web";
Person.prototype.sayName = function() {
console.log(this.name);
}
var person1 = new Person();
var person2 = new Person();
console.log(person1.sayName());//lvxin
console.log(person2.sayName());//lvxin
console.log(person1.sayName() == person2.sayName());
2.3.2.理解原型对象
- 所有原型都会自动获得一个constructor(构造函数属性),Person.prototype.constructor.
- Person(构造函数)的prototype属性指向Person Prototype(Person的原型)
- Person Prototype有构造函数(constructor)指向Person。
- person1有prototype属性指向Person Prototype,但没有自己的构造函数。
- isPrototypeOf(),返回true/false,判断实例是否拥有某个原型
- Object.getPrototypeOf(),返回定义原型的字符名称(Preson.prototpe),得到实例的原型名称
function Person() {
}
Person.prototype.name = 'lvxin';
Person.prototype.age = 18;
Person.prototype.job = 'web';
Person.prototype.sayName = function() {
console.log(this.name);
}
var person1 = new Person();
var person2 = new Person();
console.log(Person.prototype.isPrototypeOf(person1));//true
console.log(Person.prototype.isPrototypeOf(person2));//true
console.log(Object.getPrototypeOf(person1) == Person.prototype);//true
console.log(Object.getPrototypeOf(person1).name);
- 寻找属性和方法的过程:从对象实例本身开始,再看原型对象,再看object对象
- 不能用对象实例重写原型的值,实例和原型属性同名,实例会屏蔽原型的值。
- 可以用 delete删除实例的属性
- hasOwnPrototype(),返回true/false,判断是否有自己的原型属性,即实例有自己的属性。
person1.name = "xuyuqiu";
console.log(person1.name);//xuyuqiu 实例值
console.log(person2.name);//lvxin 原型值
delete person1.name;
console.log(person1.name);//lvxin 原型值
person1.name = "duanyujie";
console.log(person1.hasOwnPrototype(name));//true;
console.log(person2.hasOwnPrototype(name));//false;
delete person1.name;
console.log(person1.hasOwnPrototype(name));//false;
2.3.3.原型与in操作符
/**
* 1.in用法:判断属性是否存在
* 2.in 无论实例自己的属性还是实例用原型的属性,都返回true
* 3.通过 in返回true和hasOwnPrototype()返回false,判断是原型上的属性
* 4.hasPrototypeProperty(实例,属性),返回true/false,判断是否是原型上的属性。
*/
function Person() {
}
Person.prototype.name = 'lvxin';
Person.prototype.age = 18;
Person.prototype.job = 'web';
Person.prototype.sayName = function() {
console.log(this.name);
}
var person1 = new Person();
var person2 = new Person();
person1.name = "xuyuqiu";
console.log('name' in person1);//true;
console.log('name' in person2);//true;
console.log(person2.hasOwnProperty(name));//false 判断person2.name 来自原型
//console.log(hasPrototypeProperty(person1,'name'));//false;
//console.log(hasPrototypeProperty(person2,'name'));//true;
/**
* 1.Object.keys(对象),返回数组,找到对象的可以枚举的属性名
* 2.Object.getOwnPropertyNames(对象),返回数组,找到对象的可以枚举或者不可枚举的属性名
*/
var keysArr = Object.keys(Person.prototype);//[name,age,job,sayName];
console.log(keysArr);
person1.name = "duanyujie";
person1.age = 20;
//实例没有的自生属性
var person1KeysArr = Object.keys(person1);//[name,age]
console.log(person1KeysArr);
//constructor 不可枚举
var keys = Object.getOwnPropertyNames(Person.prototype);//[constructor,name,age,job,sayName];
console.log(keys);
2.3.4.更简单的原型语法
/**
* `1.创建一个函数同时会创建一个prototype对象
* 2.赋值整个对象是重构一个对象,一个一个赋值是修改本来的值
*/
function Person() {
}
Person.prototype = {
name:'lvxin',
age:18,
job:"web",
sayName:function() {
console.log(this.name);
}
}
var person1 = new Person();
console.log(person1 instanceof Person);//true;
console.log(person1 instanceof Object);//true;
console.log(person1.constructor instanceof Person);//false;
console.log(person1.constructor instanceof Object);//true;
console.log(Object.getOwnPropertyDescriptor(Person.prototype,"constructor"));//undefined
/**
* 1.修改实例的constructor指向有两种方法
* 1-1:直接写个属性constructor:Person
* 1-2:Object.defineProperty(对象,属性名,修改的列值)
*/
function Person2() {
}
Person2.prototype = {
constructor:Person2,
name:'lvxin',
age:18,
job:"web",
sayName:function() {
console.log(this.name);
}
}
var person2 = new Person2();
console.log(person2 instanceof Person2);//true;
console.log(person2 instanceof Object);//true;
console.log(person2.constructor instanceof Person2);//false
console.log(person2.constructor instanceof Object);//true;
console.log(Object.getOwnPropertyDescriptor(Person2.prototype,"constructor"));//enumerable:true
Object.defineProperty(Person2.prototype,"constructor",{
enumerable:false,
value:Person2
});
console.log(person2.constructor instanceof Person2);//false;
console.log(Object.getOwnPropertyDescriptor(Person2.prototype,"constructor"));//enumerable:false
console.log(person2.constructor instanceof Person2);//f alse;
2.3.5.原型的动态性
/**
* 1.动态性:先实例化,之后还可以加属性和方法,仍然可以访问到
* 2.把原型修改成另一个对象,切断了现有的原型和之前已经存在的实例对象。
* 3.创建一个构造函数,即有一个构造属性
*/
function Person() {
}
var person = new Person();
Person.prototype.sayHi = function() {
console.log("say Hi");
}
person.sayHi();//say Hi
console.log(person.sayHi());//undefined 无返回值
function Person2() {
}
Person2.prototype = {
name:'lvxin',
age:18,
job:"web",
sayName:function() {
console.log(this.name);
}
}
var person2 = new Person();
person2.sayName();//error person2实例和新的原型Person2.prototype没有关系,person2实例和原来的原型有关
2.3.6.原型对象的原型
/**
* 1.所有原生引用类型(object,Array,String)为原型对象的原型
*
*/
console.log(typeof Array.prototype.sort);//function
console.log(typeof String.prototype.substring);//function
//判断字符是否存在
String.prototype.startsWith = function (text) {
console.log(this.indexOf(text));//0
return this.indexOf(text) == 0;
};
var msg = "Hello world!";
console.log(msg.startsWith('Hello'));//true
2.3.7.原型对象的问题
/**
* 1.实例取得相同的属性值
* 2.实例1修改了值,实例2值也会变
*/
function Person () {
}
Person.prototype = {
constructor:Person,
name:'lvxin',
age:18,
job:'web',
friends:['xuyuqiu','duanyujie'],
sayName:function() {
console.log(this.name);
}
};
var person1 = new Person();
var person2 = new Person();
person1.friends.push('lizhiying');//
console.log(person1.friends);//['xuyuqiu','duanyujie','lizhiying']
console.log(person2.friends);//['xuyuqiu','duanyujie','lizhiying']
console.log(person1.friends === person2.friends);//true
2.4.组合使用构造函数模式和原型模式
function Person (name,age,job) {
this.name = name;
this.age = age;
this.job = job;
this.friends = ['xuyuqiu','duanyujie'];
}
Person.prototype = {
constructor:Person,
sayName:function() {
console.log(this.name);
}
}
//指向新的原型
var person1 = new Person('lvxin',18,'web');
var person2 = new Person('zhengling',19,'test');
person1.friends.push('lizhiying');//
console.log(person1.friends);//['xuyuqiu','duanyujie','lizhiying']
console.log(person2.friends);//['xuyuqiu','duanyujie']
console.log(person1.friends === person2.friends);//false
console.log(person1.sayName === person2.sayName);//true
2.5.动态原型模式
/**
* 判断是否初始化构造函数
*/
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('xuyuqiu',18,'web');
friend.sayName();
2.6.寄生构造函数模式
/**
* 1.概念:创建一个函数,封装对象代码,返回新建的代码
* 2.不建议用这个方法
*/
function Person (name,age,job) {
var o = new Object();//var value = new Array();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function() {
console.log(this.name);
}
return o;
}
var friend = new Person('xuyuqiu',19,'web');//xuyuqiu
friend.sayName();//xuyuqiu
2.7.稳妥构造函数模式
/**
* 1.没有公共的属性,方法也不引用this对象,返回对象
* 2特点:
* 方法不用this引用
* 实例不用new来创造
* 3.安全性高,外部只可以通过调用内部函数,来访问属性的值
*/
function Person(name,age,job) {
//没有属性
var o = new Object();
o.sayName = function() {
//不用this
console.log(name);
}
return o;
}
//不用new
var friend = Person('lvxin',18,'web');
friend.sayName();
3.继承
3.1.原型链
3.1.1.原型链
/**
* 1.定义:每个构造函数都有个prototype属性,指向原型对象,
* 原型对象constructor属性,指向构造函数,实例指向原型对象,具有所有原型对象的方法和属性。
* 2.原型链:(原型和实例之间的一个链条),第二个构造函数的原型对象为第一个构造函数的实例。
*/
//定义父的构造函数(超类型函数)
function SuperType() {
this.prototype = true;
}
SuperType.prototype.getSuperValue = function() {
//console.log(this.prototype);
return this.prototype;
};
//定义子类的构造函数(子类型函数)
function SubType() {
this.subprototype = false;
}
//继承
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function() {
//console.log(this.subprototype);
return this.subprototype;
};
var instance = new SubType();
console.log(instance.prototype);//flase;
console.log(instance.getSuperValue());//true;
console.log(instance.getSubValue());//flase;
console.log(instance.toString());
3.1.2.别忘记默认的原型
//定义父的构造函数(超类型函数)
function SuperType() {
this.prototype = true;
}
SuperType.prototype.getSuperValue = function() {
//console.log(this.prototype);
return this.prototype;
};
//定义子类的构造函数(子类型函数)
function SubType() {
this.subprototype = false;
}
//继承
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function() {
//console.log(this.subprototype);
return this.subprototype;
};
var instance = new SubType();
console.log(instance.prototype);//flase;
console.log(instance.getSuperValue());//true;
console.log(instance.getSubValue());//flase;
/**
* 所有函数的默认原型对象是Object,所以实例都会
* toString();
* hasOwnPrototype();
* isPrototypeOf();
* toLocalString();
*/
console.log(instance.toString());//输出 是一个对象,SubType和SuperTpye都没有toString()方法
3.1.3.确定原型和实例的关系
//定义父的构造函数(超类型函数)
function SuperType() {
this.prototype = true;
}
SuperType.prototype.getSuperValue = function() {
//console.log(this.prototype);
return this.prototype;
};
//定义子类的构造函数(子类型函数)
function SubType() {
this.subprototype = false;
}
//继承
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function() {
//console.log(this.subprototype);
return this.subprototype;
};
var instance = new SubType();
console.log(instance instanceof Object);//true;
console.log(instance instanceof SuperType);//true;
console.log(instance instanceof SubType);//true;
/**
* 原型.isPrototypeOf(实例):判断实例是否是原型上的
*/
console.log(Object.prototype.isPrototypeOf(instance));//true
console.log(SuperType.prototype.isPrototypeOf(instance));//true
console.log(SubType.prototype.isPrototypeOf(instance));//true
3.1.4.谨慎的定义方法
/**
* 1.先继承父的属性和方法,再去写子类的属性和方法,可以重写父类的方法和属性。
* 2.通过原型链继承,不能用字变量的方式创建原型。
*/
function SuperType() {
this.prototype = true;
};
SuperType.prototype.getSuperValue = function() {
return this.prototype;
};
function SubType() {
this.subprototype = false;
}
//继承
SubType.prototype = new SuperType();
//重写父的方法
SubType.prototype.getSuperValue = function() {
return false;
}
//增加新方法
SubType.prototype.getSubValue = function() {
return this.subprototype;
}
var instance = new SubType();
console.log(instance.getSuperValue());//false;先找子的同名函数
/**
* 1.先继承父的属性和方法,再去写子类的属性和方法,可以重写父类的方法和属性。
* 2.通过原型链继承,不能用字变量的方式创建原型。
*/
function SuperType1() {
this.prototype = true;
};
SuperType1.prototype.getSuperValue1 = function() {
return this.prototype;
};
function SubType1() {
this.subprototype = false;
}
//继承
SubType1.prototype = new SuperType1();
//用字变量的方式
SubType1.prototype = {
getSubValue:function() {
return this.subprototype;
},
};
var instance = new SubType1();
console.log(instance.getSuperValue1());//is no has function 字变量会使继承无效,子中没有父的方法。
3.1.5.原型链的问题
/**
* 1.所有方法和属性都全部共享,一改全改值
*/
function SuperType() {
this.color = ['blue','yellow','red'];
}
function SubType() {
}
SubType.prototype = new SuperType();
var instance1 = new SubType();
var instance2 = new SubType();
console.log(instance1.color);//['blue','yellow','red','white']
console.log(instance2.color);//['blue','yellow','red','white']
instance1.color.push('white');
console.log(instance1.color);//['blue','yellow','red','white']
console.log(instance2.color);//['blue','yellow','red','white']
3.2.借用构造函数
/**
* 1.在子类构造函数中调用父类的构造函数
* 2.可以传递参数给父类
* 3.子类新的方法写在调用父类的之后。
*/
function SuperType() {
this.colors = ['red','bule','yello'];
}
function SubType() {
//继承
SuperType.call(this);
this.age = 29;
}
var instance1 = new SubType();
var instance2 = new SubType();
instance1.colors.push('white');
console.log(instance1.colors);//['blue','yellow','red','white']
console.log(instance1.age);//29
console.log(instance2.colors);//['blue','yellow','red']
console.log(instance2.age);//29
function SuperType1(name) {
this.colors = ['red','bule','yello'];
this.name = name;
}
function SubType1() {
//继承
SuperType1.call(this,'lvxin');
this.age = 29;
}
var instance11 = new SubType1();
var instance21 = new SubType1();
console.log(instance11.name);//lvxin
console.log(instance21.name);//lxin
console.log(instance11.age);//29
console.log(instance21.age);//29
console.log(instance11.colors);//['red','bule','yello','white']
instance11.colors.push('white');
console.log(instance21.colors);//['red','bule','yello']
3.3.组合继承
/**
* 1.原型链和借用构造来实现对实例属性的继承
*/
function SuperType(name) {
this.colors = ['red','blue','green'];
this.name = name;
}
SuperType.prototype.sayName = function() {
return 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() {
return this.age;
}
var instance1 = new SubType('lvxin',18);
instance1.colors.push('white'); //['red','blue','green','white']
console.log(instance1.colors);
console.log(instance1.sayName());//lvxin
console.log(instance1.sayAge());//18
var instance2 = new SubType('xuyuqiu',19);
console.log(instance2.colors);//['red','blue','green']
console.log(instance2.sayName());//xuyuqiu
console.log(instance2.sayAge());//19
3.4.原型式继承
/**
* 1.值都共享
* 2.Object.create(对象)方法也可以用来创建一个实例。
*/
console.log("-------------------6.3.4原型式继承-----------------------");
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
var person = {
name:'lvxin',
friends:['duanyujie','xuyuqiu']
};
//var anotherPerson = new object(person);
var anotherPerson = Object.create(person);
anotherPerson.name = 'lizhiying';
anotherPerson.friends.push('liufan');
//var yetAnotherPerson = new object(person);
var yetAnotherPerson = Object.create(person);
yetAnotherPerson.name = 'xufengfeng';
yetAnotherPerson.friends.push('zhengling');
console.log(person.friends);//['duanyujie','xuyuqiu','liufan','zhenglin']
console.log(person.name);//xufengfeng
3.5.寄生式继承
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
function createAnother(original) {
var clone = object(original);
clone.sayHi = function() {
return 'hi';
}
return clone;
}
var person = {
name:'lvxin',
friends:['duanyujie','xuyuqiu']
};
var anotherPerson = createAnother(person);
console.log(anotherPerson.sayHi());//hi
3.6.寄生组合式继承
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
function inheritPrototype(subType,superType) {
var prototype = object(subType.prototype);
prototype.constructor = subType;
subType.prototype = prototype;
}
function SuperType(name) {
this.colors = ['red','blue','green'];
this.name = name;
}
SuperType.prototype.sayName = function() {
return this.name;
};
function SubType(name,age) {
SuperType.call(this,name);
this.age = age;
}
inheritPrototype(SubType,SuperType);
SubType.prototype.sayAge = function() {
return this.age;
}