JS面向对象的理解

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.工厂模式

  1. 无法确定对象的类型,每次都是调用Object
  2. 创建多个对象之间没有关联
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.构造函数模式

对比工厂模式有以下不同之处:

  1. 没有显式地创建对象
  2. 直接将属性和方法赋给了 this 对象
  3. 没有 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的实例。

  1. 每一个实例都要重新创建一遍,person1和person2都有sayName的方法,但是是两个Function实例。
  2. 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.原型模式

  1. 每个函数(构造函数)都有一个prototype(原型)属性
  2. prototype(原型)属性是一个指针,指向一个对象
  3. 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.理解原型对象

  1. 所有原型都会自动获得一个constructor(构造函数属性),Person.prototype.constructor.
  2. Person(构造函数)的prototype属性指向Person Prototype(Person的原型)
  3. Person Prototype有构造函数(constructor)指向Person。
  4. person1有prototype属性指向Person Prototype,但没有自己的构造函数。
  5. isPrototypeOf(),返回true/false,判断实例是否拥有某个原型
  6. 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);
  1. 寻找属性和方法的过程:从对象实例本身开始,再看原型对象,再看object对象
  2. 不能用对象实例重写原型的值,实例和原型属性同名,实例会屏蔽原型的值。
  3. 可以用 delete删除实例的属性
  4. 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;
}
			

4.学习地址

【JS】创建对象的6种方式总结
GitHub代码

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值