javascript原型
要理解原型我们要先问自己几个问题,1.什么是原型对象?2.为什么要用原型?带着这些问题有助于我们更好的去理解原型。
1.什么是原型对象呢?我们创建的每个函数都有一个prototype属性,这个属性是一个指针,指向一个对象,即原型对象,这个对象可以共享属性和方法,所有同一个类的实例对象都可以访问原型对象。
用简单的白话可以这样理解:每个函数里都有prototype(包括构造函数),构造函数创建的实例对象里有__proto__原型
注意:当普通函数调用prototype是没有作用的。只有构造函数形式调用prototype时,它所创建的实例对象都有一个隐含的属性__proto__,实例的__proto__指向原型对象,看以下简单例子方便理解:
function Myclass(){
}
var mc=new Myclass()
var mc1=new Myclass()
console.log(Myclass.prototype == mc.__proto__ )
console.log(Myclass.prototype == mc1.__proto__ )
//返回结果都为true
此处先打断一下思路,大概了解含义就行,那我们为什么要去用这个原型对象呢?
(1)我们平时创建对象可以用Objcet构造函数或者对象字面量来创建,但是这样创建缺点很明显:使用同一个接口创建多个对象时,会产生大量的重复代码,如何解决呢?引入工厂模式
(2)通过工厂模式,我们就可以解决大量代码重复的问题,但工厂模式也有缺点,如下面的代码,因为都是通过Object创建的实例,结果都是Object,所以根本无法解决对象的识别问题,如何解决呢?引入构造函数
这个例子创建人和狗都是以Object为标识,无法区分
function createPerson(name,age,job){
var o = new Objecct();
o.name=name;
o.age=age;
o.job=job;
return o;
}
var person1=createPerson('swk',22,'kfc');
var person2=createPerson('csf',23,'mc');
function createDog(name,age,job){
var o = new Objecct();
o.name=name;
o.age=age;
o.job=job;
return o;
}
var dog1=createDog('swk',22,'kfc');
var dog2=createDog('csf',23,'mc');
(3)构造函数可以有效解决工厂模式的缺点,但是以下的构造函数还是不够完美,还是有缺点,以下例子person1和person2都会有say方法,构造函数每执行一次都会创建一个新的空间存储say函数,这样存储地址不同,person1.say == person2.say 结果为false,证明他们存储在不同的空间,但是作用却是一样的,这样就导致来内存空间大量浪费。所以我们可以完善一下构造函数
function Person(name,age){
this.name=name;
this.age=age;
this.say=function(){
console.log('haha')
}
}
var person1=new Person('yx',11);
var person2=new Person('uu',22);
function Dog(name,age){}
var dog=new Dog()
//用构造函数即可区分开不同的类型,用Person创建的就是person类下对象,用Dog创建的就说Dog类下对象
//我们也可以用instanceof操作符来检测结果
console.log(dog instanceof Person) //false
console.log(dog instanceof Dog)//true
console.log(dog instanceof Object)//true(所有对象都是Objcet后代)
(4)我们可以把构造函数内部的函数拿出来,放到全局里面,这样就只会占用一个空间,不会占用空间,对性能有来很大提升。但是问题又来了,这样会污染了全局作用域的命名空间,很不安全。如何解决呢?引入了原型对象
function Person(name,age){
this.name=name;
this.age=age;
}
function say(){
console.log('xixi')
}
var person1=new Person('yx',11);
var person2=new Person('xx',12);
(5)原型模式可以有效解决内存浪费和全局污染的问题,如下面例子,person1和penson2使用的say方法相同,在同一空间;也没有在全局里面定义方法,不会污染全局;
function Person(name,age){
this.name=name;
this.age=age;
}
Person.prototype.say=function(){
alter('xixi')
}
var person1=new Person();
var person2=new Person();
console.log(person1.say == person2.say)//true
到这里我们也应该清楚了为什么要去用原型的好处,它是由于一个个不同的问题,最终选择了原型这种比较好的方式来处理问题。
原型引出来以后,我们也可以简单的去理解原型链是什么了;
function Person(){}
var person1=new Person();
person1.name='yuyu'
console.log(person1.name);
console.log(person1.hasOwnPropery("name"));//true
//结果为yuyu,实例对象第一步会在自身上找属性,我们可以用hasOwnPropery来协助验证对象中是否含有这个属性
function Person(){}
var person1=new Person();
Person.prototype.name='yuyu1';
console.log(person1.name);
console.log(person1.hasOwnPropery("name"))//false
console.log(person1.__proto__.hasOwnPropery("name"))//true
//结果上yuyu1,自身上没有name属性,就会根据隐含的__proto__指向到原型对象去找name
function Person(){}
var person1=new Person();
Object.prototype.name='yuyu2'
console.log(person1.name)//yuyu2
console.log(person1.hasOwnPropery("name"))//false
console.log(person1.__proto__.hasOwnPropery("name"))//false
console.log(person1.__proto__.__proto__.hasOwnPropery("name"))//true
//结果是yuyu2,自身没有,去原型对象里找还是没有,(因为原型对象也是对象,对象就会有一个__proto__),就会根据__proto__继续找到Object,如歌Object还没有就会返回null
以上这个查找属性的过程就是一个原型链,到此原型和原型链也应该有个清楚的认识了。
同时在此补上一个简单的面试题:
创建对象的方法?
1、对象字面量 var obj={};
2、构造函数 var obj=new Object();
3、工厂模式创建
4、自定义构造函数创建
5、构造函数和原型模式创建
6、Object.create()创建