js面向对象基础
javascript
在new xxx
的时刻发生了什么?
人人在一样平常的js
开辟中,会经常见到类似于下面的代码块:
function Cat(name,color){
this.name = name;
this.color = color;
}
Cat.prototype.sayName = function(){
console.log('this cat name is:'+this.name);
}
Cat.prototype.sayAge = function(){
console.log('this cat age is:'+this.age);
}
var cat = new Cat('大黄','黄色');
到这边的话,我们就胜利建立了一个Cat
类的对象cat
。(注意下,虽然这边我们依然谈类,然则因为JS是一门鸭子范例的言语,只需具有某个类肯定的特征,比方某些要领、属性,那末我们就以为这个对象就是这个类的)
那末在cat对象的建立历程当中,又发生了那些事情呢?建立一个对象,平常会阅历以下几个步骤:
建立一个空对象
o
令
o
的原型指向函数的原型在新建立的对象
o
上实行函数,即function.apply(o,arguments)
返回对象o
经由以上四个步骤我们就能够创实例化一个Cat范例的对象。
这里说个人人比较轻易失足的处所,就是function优惠返回值的状况。比方:
function F(name){
this.name = name;
return "F";
}
这边假如实行var f = new F("warjiang")
;js
在new F('warjiang')
的时刻回疏忽F的返回值。f末了是一个对象,而不是"F"
.实行效果以下:
什么时刻用prototype
,什么时刻用this
?
接着上面的例子,我们在定义Cat
类的时刻,关于name,color
这些属性的定义是采纳的this
的体式格局。而关于sayName,sayAge
的定义是采纳的prototype
的体式格局。那末什么时刻该用this,什么时刻该用prototype呢?在说这个题目之前,我想从新再提一下==
,===
。置信每一个jser都应当晓得这个两个等号与三个等号的区分,两个等号只比较数值大小,不关心范例;三个等号不仅比较值大小,还要比较范例是不是一致。下面我们先看个例子:
// number范例
var num1 = 22;
var num2 = 22;
console.log(num1 == num2);//true
console.log(num1 === num2);//true
// string范例
var str1 = "hello world";
var str2 = "hello world";
console.log(str1 == str2)//true
console.log(str1 === str2)//true
// 对象
var student1 = {
name:"tony",
age:22
};
var student2 = {
name:"warjiang",
age:22
}
console.log(student1 == student2);//false
console.log(student1 === student2);//false
// Date范例
var d1 = new Date('2017-3-15 10:23:00');
var d2 = new Date('2017-3-15 10:23:00');
console.log(d1 == d2);//false
console.log(d1 === d2);//false;
// 自定义范例
function Cat(name,color){
this.name = name;
this.color = color;
}
Cat.prototype.sayName = function(){
console.log("my name is " + this.name);
}
var cat1 = new Cat("大黄","黄色");
var cat2 = new Cat("大黄","黄色");
console.log(cat1 == cat2);//false
console.log(cat1 === cat2);//false
经由历程上面的例子,我们能够总结出,在js中除了基础范例bool,string,number,undefined,null
是根据value的体式格局举行相称的比较以外,其他的对象(不管是自定义的照样JS内置的)在做比较的时刻都是比较两个对象的内存地址,假如两个对象的内存地址不相称,则示意两个对象就是不相称的。所以才会涌现上面例子中的,纵然连个对象中一切的属性都相称,依然会涌现两个对象不相称的状况。
ok回到正题上了,有了这边等号比较的基础以后,我们在来看看之前的题目,什么时刻应当运用prototype,什么时刻应当运用this。再来个?
function Cat(name,color){
this.name = name;
this.color = color;
this.sayColor = function(){
console.log(this.color);
}
}
Cat.prototype.sayName = function(){
console.log("my name is:"+this.name);
}
var cat1 = new Cat("大黄","黄色");
var cat2 = new Cat("明白","白色");
console.log(cat1.sayColor == cat2.sayColor);//false
console.log(cat1.sayName == cat2.sayName);//true
上面例子中两个Cat范例的对象cat1
,cat2
在内存中是什么状况呢。
经由历程上面的内存图,我们能够晓得cat1
的sayColor
与cat2
的sayColor
要领离别指向两块内存地址,所以cat1.sayColor==cat2.sayColor
为false
,而cat1.sayName
与cat2.sayName
都是来源于prototype
对象上的sayName
要领,他们的内存地址实际上是一个,故而cat1.sayName == cat2.sayName
要领为true
。
经由历程上面的两个例子,我们不难看出,假如我们运用this的话,则this上的属性、要领都是属于对象自身的,平常能够用于私有要领、属性,而假如运用prototype的话,prototype上的属性、要领在各个类对象上面同享,平常能够用于共有要领、属性。
因而关于一些共有的要领、属性,我们能够放在prototype上面,而关于一些私有的要领、属性,我们能够放在prototype上
下面的话,我再补充一个分外的例子
function Cat(name,color){
this.name = name;
this.color = color;
}
Cat.prototype.type = "猫科动物";
Cat.prototype.sayType = function(){
console.log(this.type);
}
var cat1 = new Cat("大黄","黄色");
var cat2 = new Cat("明白","白色");
cat1.sayType();//猫科动物
cat1.type = "狗科动物";
cat2.sayType();//猫科动物
cat1.sayType();//狗科动物
这边的话,能够有些同学会以为cat1.type = “狗科动物”这句话,修改的是prototype中的type,然则实际上cat1.type=”狗科动物”,只是在cat1对象上面增加了一个type属性,值为”狗科动物”,而原型上面的type并没有收到影响。我们能够在掌握台中检察cat1与cat2的细致以下:
假如想修改prototype中的type的话,能够把cat1.type
修改为cat1.__proto__.type = "狗科动物"
(这边也要注意下,__proto__
并非一切浏览器都支撑);此时实行效果就像人人想的那样离别为猫科动物,狗科动物,狗科动物
。
JavaScript的继续
谈到js的继续,起首先了解下js中原型链。每一个js对象都有一个prototype的属性,这个属性会指向一个新的对象,这个新的对象也会有一个prototype的属性。如许一直到Object.
这边轻微提下,有些人能够会问浏览器中__proto__
与prototype
的区分,能够理解为__proto__
是chrome、firefox
这些浏览器对prototype
的一种完成、表现,东西照样一个东西。原型链我也举个例子
var o = {
name:'warjiang',
age:24
}
var b = {
name:'bb'
}
b.__proto__ = o;
console.log(b.name);//bb
console.log(b.age);//24
console.log(b.__proto__.name);//warjiang
讲完原型链,我们就最先最先讲讲继续。这边的话,我看也有人说用属性拷贝或者是要领借用这类体式格局来完成继续,不过我不是异常承认。这边我主要用讲原型来完成继续。
用原型链完成继续,就是让子类的prototype指向父类的prototype,比方下面如许:
function Parent(){
this.type = "parent";
}
Parent.prototype.sayType = function(){
console.log(this.type);
}
function Child(){
}
Child.prototype = Parent.prototype;
Child.prototype.constructor = Child;
然则假如如许写会有一个题目,就是我们在修改子类prototype的constructor的同时也修改了父类的prototype的constructor,这类状况是不允许的。那末处理的思绪平常就是两种,一个是经由历程new Parent()来处理,我们晓得new Parent()的历程帮我们制造一个prototype指向Parent的prototype的对象(记作o,o.prototype=parent.prototype),这个时刻,让子类prototype指向o这个对象的时刻,Child.prototype->o
,o.prototype->Parent.prototype
,如许一个原型链就这么链接起来了,同时这个时刻假如去修改Child.prototype.constructor为Child的时刻,相称于实行o.constructor = Child
,并不会对Parent.prototype
形成影响。这类做法的表现形式以下:
function Parent(){
this.type = "parent";
}
Parent.prototype.sayType = function(){
console.log(this.type);
}
function Child(){
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;
此时假如我们去new Child()对象的时刻,我们能够以下的继续图
剖析Child.prototype=new Parent();
这个历程我们会发明,new Parent()实在对Parent的prototype起到庇护的作用,因而我们完全能够经由历程一个空对象来完成如许的功用,以下:
function Parent(){
this.type = "parent";
}
Parent.prototype.sayType = function(){
console.log(this.type);
}
var f = function(){}
f.prototype = Parent.prototype;
function Child(){
}
Child.prototype = new f();
Child.prototype.constructor = Child;
这么做的优点在于new f()的历程相关于new Parent()的历程越发轻量,越发勤俭内存。