说起原型和原型链接,着实让我这个前端菜鸟胡搞了好一阵子。虽然有点绕口的缘故,但是更多的还是自己比较浮躁带来的后果,这一块据说是前端的基础,看了很多遍才差不多有点头目。分享一下我领悟到的武林秘籍,希望能给您带来一点启迪,如果存在任何问题,请及时指正我,谢谢。😊😊😊😊
一、 浅谈数据属性和访问器属性
1. 创建对象:
通常创建对象一般都会有两种方法:
//利用object来创建对象
var person = new Person();
person.name = "klivitam";
person.age = 23;
person.sayName = function(){
alert(this.name)
}
复制代码
// 对象字面量法,推荐使用这种方法
var person = {
name:"klivitam",
age:23,
sayName: function(){
alert(this.name);
}
}
复制代码
2. 属性类型
在javascript中,对象的属性一共分为两种:数据属性和访问器属性。
- 数据属性 configurable :表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或能否把属性修改为访问器属性,默认为true enumerable :表示能否通过for-in循环返回属性 writable :表示能否修改属性的值 value :包含该属性的数据值。默认为undefined 数据属性包含一个数据值的位置,在这个位置可以读取和写入值。以上就是描述数据值行为的四个特性。 okey,可能全凭着口述概念无法了解这个意思,现在就实操代码吧。
// "use strict"
var worker = {}
Object.defineProperty(worker, "job", {
writable: false,
value: "码农"
})
console.log(worker.job)
worker.job = "教师"
console.log(worker.job)
复制代码
当把writable的属性改成true的时候,
从上面的代码可以看出来writable是用来控制是否能修改属性的值。另外当writable为false的时候,并且使用严格模式下,会发生:
// "use strict"
var worker = {}
Object.defineProperty(worker, "job", {
// writable: false,
configurable:false,
value: "码农"
})
console.log(worker.job)
delete(worker.job)
console.log(worker.job)
复制代码
当configurable为false的时候,使用delete方法会失效,并且在严格模式下,delete会报错。同理改成true的时候,则为undefined,说明删除成功了。
注意:当value的值没初始化的时候,默认放置undefined
至于最后一个我觉得就没必要代码进行演示了,同理可得。
- 访问器属性 configurable :表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或能否把属性修改为访问器属性,默认为false enumerable :表示能否通过for-in循环返回属性,默认为false Get :在读取属性时调用的函数,默认值为undefined Set :在写入属性时调用的函数,默认值为undefined 访问器属性不包含数据值,包含的是一对get和set方法,在读写访问器属性时,就是通过这两个方法来进行操作处理的。并且访问器属性不能直接定义,要通过Object.defineProperty()这个方法来定义。 直接上代码吧:
var worker = {
_job:"码农",
age: 23
}
Object.defineProperty(worker,"job",{
get:function(){
return this._job;
},
set:function(newJob){
if(newJob!==this._job){
this._job = newJob;
this.age ++
}
}
})
console.log(Object.getOwnPropertyDescriptor(worker,"job"));
console.log(worker.job)
worker.job = "教师"
console.log(worker.job)
console.log("更换职业就变老一年,5555~:"+worker.age)
复制代码
##二、 js设计模式 ####1、 工厂模式 工厂模式是一个很基础的一个模式吧,反正我在学java (android)的时候经常会遇到这种模式,主要是抽象了创建对象的具体过程。具体的代码如下:
//屌丝程序员,只能偶尔意淫一哈 = l =,别喷我
function addBeatiGrilWx(name,age,job){
var gril = new Object();
gril.name = name;
gril.age = age;
gril.job = job;
gril.sayHi = function(){
console.log("hi! " + this.name)
};
return gril
}
var lyf = addBeatiGrilWx("liuyifei",18,"actor");
lyf.sayHi();
console.log(lyf);
复制代码
效果如下
function BeautiGril(name,age,photo){
this.name = name;
this.age = age;
this.photo = photo;
this.sayHi = function(){
console.log("hi! "+this.name)
}
}
var lyf = new BeautiGril("liuyifei",18,"baidu");
lyf.sayHi();
console.log(lyf)
console.log(lyf.constructor == BeautiGril)
console.log(lyf instanceof BeautiGril)
console.log(lyf instanceof Object)
复制代码
具体的效果如下:
function BeautiGril(name,age,photo){
this.name = name;
this.age = age;
this.photo = photo;
}
function sayHi(){
console.log("hi! "+this.name)
}
复制代码
那么相当新建了一个全局方法,这样岂不是更加的没有必要了么?此时就要引入到原型模式了。
3. 原型模式
我们创建的函数都有prototype(原型)属性,这个属性是指针,指向一个对象,而这个对象的用途是 包含由特定类型的所有实例所共享的属性和方法 ,使用原型对象就可以让所有实例对象均包含这些属性及方法。
function Worker(){}
Worker.prototype.name = "programmer";
Worker.prototype.work = "programming...";
Worker.prototype.heartSound = function(){
console.log(this.name+" want rest,but he still "+this.work)
}
var xiaoZhang = new Worker();
xiaoZhang.heartSound();
var xiaoWang = new Worker();
xiaoWang.heartSound();
console.log(xiaoZhang.heartSound ==xiaoWang.heartSound)
复制代码
这里我还是说一下,我将heartSound()方法和所有的属性直接添加到了Woker的原型属性中,然后通过new创建对象,在原型模式中这些属性和方法对于所有的实例是共享的。 但是这里存在有点问题--那就是并不是所有的worker都是程序员。这就引发了最后一种模式的混用。
3. 原型模式+构造器模式
这个模式在我的理解上来说,主要是为了避免单独用原型模式所带来弊病,就拿上一份代码来说,并不是所有的worker都是程序员,如果想一个医生想去复用这个类的时候,就必须改变其原型上的值,如果改变其原型的值,那么整个都会乱套了。于是我想到构造器模式
function Worker(name,work){
this.name = name;
this.work = work;
}
Worker.prototype.heartSound = function(){
console.log(this.name+" want rest,but he still "+this.work)
}
var xiaozhang = new Worker("programmer","programmer....");
xiaozhang.heartSound(); // programmer want rest,but he still programmer....
//医生也需要休息
var xiaomei = new Worker("doc","sos");
xiaomei.heartSound(); // doc want rest,but he still sos
复制代码
##三、 原型链 前面也差不多谈到了 原型 这个概念,什么叫原型呢?其实我有一个不太好,但是又很恰当的例子来描述这些个概念(看嗯哼家小狗、小猫想到的):
- 小狗是小狗妈妈生的、小猫是小猫妈妈生的。小狗和小猫被称为对象的实例,狗妈妈、猫妈妈被称为对象的原型
- 狗妈妈和狗爸爸能通过交配生出一大堆小狗出来,其中交配就被称为构造函数
- 狗妈妈有很多狗宝宝,但是狗宝宝却只有一个狗妈妈,这可以被称之为原型的唯一性
- 我们可以通过狗宝宝找到狗妈妈,狗妈妈也可以找到狗外婆,以此类推 这就是相当于原型链
- 大家都知道狗有很多品种,很多品种里面也有发育好的,发育差的、胖的瘦的...例如胖的泰迪也是泰迪=>泰迪也是狗=>狗=>哺乳动物=>动物=>生物。总之一切的一切都有一个起点,这条链的终点将会被指向同一处,这就好比原型链最终指向null
- 小泰迪生下来之后,它的样貌会跟泰迪妈妈大同小异,这就类比于原型的继承。
- 小泰迪的主人领养小泰迪之后将其打扮成另外的模样,这就类比于对象属性可以覆盖原型属性。但是小泰迪的模样并不会改变小泰迪弟弟的模样,这就类比于对象属性的改变不会影响原型的改变。
其实有了上面的一个基本的了解之后,我们再来一步一步写代码就会比较容易了。
{
function Dog(name){
this.name = name
}
Dog.prototype.action = function(){
console.log(this.name+" wang..");
}
let xiaogou1 = new Dog("xiaogou1");
xiaogou1.action(); // xiaogou1 wang..
let xiaogou2 = new Dog("xiaogou2")
xiaogou2.action(); // xiaogou2 wang..
let xiaogou3 = new Dog("xiaogou3");
xiaogou3.action(); // xiaogou3 wang..
}
复制代码
如上面所示 xiaogou1、xiaogou2、xiaogou3被称为对象实例而Dog被称为这群小狗的原型。可以通过构造方法来创建出1,2,3三只小狗。
{
function Cat(name){
this.name = name;
}
Cat.prototype.action=function(){
console.log(this.name+" miao!!!")
}
function Dog(name){
this.name = name
}
Dog.prototype.action = function