个性化阅读
专注于IT技术分析

JavaScript面向对象的程序设计及原型链的理解运用全解【重点】

相信大家都听说过在JavaScript中“一切皆对象”,可是对象又是什么呢,怎么理解对象呢?

object面向对象

一、对象是什么

对象又叫面向对象(Object-Oriented,OO),可以创建任意多个属性和方法,以键值的形式存在,键就是对象属性或者方法的名字,值就是对象属性的值或者方法体,可以是函数或者是其他数据类型的数据。属性有它自己的特性,是否为可改写或者可循环等,可以通过一个方法defineProperty( )方法设置。不过并不是所有浏览器都支持,而且现在也很少见人用这个方法去设置对象的属性了,我们一般都可以直接设置。比如说改写对象的值也是直接改写。

例如:

var person = {//定义一个对象
name:"mm",
age:18,
job:"teacher",
}
person.job = "lawyer";//重设对象属性值
person.sex = "男";//增加对象属性
console.log(person.job);//lawyer 修改后的属性值
console.log(person.sex);//男 能访问到新增的属性
console.log(person)//输出整个对象

二、创建对象

1、构造函数

如果要创建多个person对象,是不是要写多遍?如果用上面的方法这样创建是的,那有什么办法避免写重复的代码?有的哦亲。JavaScript中有种叫做工厂模式的方法,就是大批量的生产对象。可是JavaScript又嫌弃说生产得到的对象不知道它的类型是什么。所以现在大家都是用构造函数模式,说是构造函数其实和普通的函数没啥区别,就是调用的时候需要“new”一下,不过它里面的方法和属性都指向这个构造函数自己,所以创建出来的对象其实就是这个方法的实例。保证了对象的类型。相当于是把一个对象封装进一个方法里面了。

书写:在构造函数中,因为借鉴其他OO的语言,所以函数名的第一个字母习惯大写,创建的属性对象都赋值给this对象,这里这个this指的是这个构造函数。

例如:

function newperson(name,age,sex){//工厂模式
var person = new Object;
person.name = name;
person.age = age;
person.sex = sex;
return person;
}
var person1 = newperson("luara",18,"女");//创建对象
var person2 = newperson("LL",25,"男");//创建对象
console.log(person1);//{name: "luara", age: 18, sex: "女"}

console.log("==================================");
function Clothes(color,price,address){//创建构造函数
this.color = color;
this.price = price;
this.address = address;
}
var clothes1 = new Clothes("red",250,"广东");//创建新的对象,把构造函数的作用域也就是this复制给新的对象,执行上面的函数体,返回一个新的函数。
console.log(clothes1);//Clothes {color: "red", price: 250, address: "广东"}

2、构造函数的存在问题

既然构造函数那么还用,那构造函数是不是完美的呢?当然不是完美的,而且存在的问题还不少。

(1)

首先来说一下定义在构造函数中的方法,因为每次新建一个对象,就”new”一个实例,实例对象可以用到构造函数中的方法没错,不过每次新建一个实例其实都创建了一个方法,因为只是那个方法名相同,函数是新建的,在批量创建实例的时候很废内存。

(2)

为了解决这个问题,我们可以把方法建在构造函数的外面,因为写在全局作用域中的方法中有个this指向函数的指针,方法之创建一次,实例可以执行任意次,并且确保了每个实例执行的都是同一个方法。可是,问题来了,外面的方法是构造函数的属性,并且只能被构造函数的实例调用,万一这个构造函数定义了很多这样的属性方法呢,都写在全局作用于中,除了构造函数的实例外,其他都不能调用,是不是不太科学了。

例如:

function Clothes(color,price,address){//创建构造函数
this.color = color;
this.price = price;
this.address = address;
this.saycolor = function(){//在构造函数内的函数方法
alert(this.color)
};
this.sayPrice = sayPrice;//函数方法写在构造函数外部
}
var clothes1 = new Clothes("red",250,"广东");//创建新的对象,把构造函数的作用域也就是this复制给新的对象,执行上面的函数体,返回一个新的函数。
var clothes2 = new Clothes("red",250,"广东");
console.log(clothes1);//Clothes {color: "red", price: 250, address: "广东"}

console.log(clothes1.saycolor == clothes2.saycolor);//false 不是用一个方法
function sayPrice(){//实例可以调用用一个方法,不过只能被实例调用
alert(this.price);
};
sayPrice()//undefined 这时候this指的是window
clothes1.sayPrice();//250 可以调用

3、解决问题

问题的解决办法就是要把属于对象的函数方法写在构造函数,在外面访问对象方法时候,领人反问到的是对象的同一个方法。我们在创建函数的时候都会有一个prototype原型属性,从字面上理解是新创建的对象实例的原来的原型对象。所以这个属性总是指向原型对象。有个prototype原型属性我们就可以把对象和方法都写在函数体里面封装起来了。不过创建的是一个空的构造函数,它的原型对象我们不知道名字,只能用prototype添加属性和方法。而新对象是构造函数的实例,所以也具有那些函数和方法。不过不设置的时候是不显示的。

怎么才能知道原型对象中有多少个属性和方法呢?我们可以运用Object.keys( )方法枚举,返回的是一个数组。

构造函数和对象

例如:

function Clothes(){//创建函数
};

//函数的原型对象属性;
Clothes.prototype.color = "red";
Clothes.prototype.price = 250;
Clothes.prototype.saycolor = function(){
alert(this.color);
};
var clothes1 = new Clothes();
clothes1.color = "blue";
console.log(clothes1);//blue 只有一个属性,因为创建的Clothes是空的函数,clothes1只是Clothes的一个实例,拥有Clothes的原型对象的方法。

//拥有全部原型对象的属性
var key = Object.keys(Clothes.prototype);//因为不知原型对象的名称是什么,所以用Object
console.log(key);//["color", "price", "saycolor"] 得到一个数组,元素是原型对象的属性

4、最简单的原型语法–constructor的运用

上面写法虽好,不过太多的重复了,我们可以把从复部分封装成一个对象,这个对象就是原型对象,不过封装成原型对象后constructor指针就会发生变化。不再指向构造函数了,而是指向原型对象(Object)。这样创建出来的实例不是构造函数的实例。这个不就简单了,在封装的原型独享中增加一个constructor属性指向构造函数不就得了。其实用什么名字作为原型对象的对象名都可以,用“Clothes.prototype”只是好标识,让人一看就明白是构造函数的原型对象。

重点是设置constructor属性。这个决定着这个对象是普通对象还是原型对象。

例如:

function Clothes(){//创建函数
};

Clothes.prototype = {//用Clothes.prototype字面量做原型对象名,目的是好表示,其实随意用一个也行
constructor : Clothes,//指向构造函数,成为原型对象,否则就是一个普通的对象
color:"red",
price:250,
saycolor:function(){
alert(this.color)
}
}
var clothes1 = new Clothes();
clothes1.color = "green";//Clothes {color: "green"}
console.log(clothes1)

5、构造函数和原型模式的结合

其实在实际使用过程中,上面的构造函数不可能是空的,可以在构造函数设置其他属性,然后在原型对象设置共享的对象和方法。为什么说共享,那是原型对象的特性,就是因为它有这个特性,所以才把方法放在原型属性里面,避免创建的对象实例用的不是同一个方法。我们在应用中,大多都是构造函数和原型模式相互结合,既简化了代码,又增强了性能。我们以后就这样用就对了。

例如:

function Clothes(address){//创建带参的构造函数
this.address = address;
};

Clothes.prototype = {//用Clothes.prototype字面量做原型对象名,目的是好表示,其实随意用一个也行
constructor : Clothes,//指向构造函数,成为原型对象,否则就是一个普通的对象
color:"red",
price:250,
saycolor:function(){
alert(this.color)
}
}

var clothes1 = new Clothes("深圳");//传入参数
clothes1.color = "green";//Clothes {color: "green"}
console.log(clothes1)

三、对象继承

进过上面解说的例子,相信你已经大概知道圆形对象是什么东西了,每创建一个函数,其实这个函数都是继承了原型对象的属性和方法的。再创建的实例,也会具有构造函数的属性和方法。只要使用到一个对象,都会具备他的属性和方法。

举个简单的例子:

function Man(name,age){
this.name = name;
this.age = age;
}
var man1 = new Man("小呵",15);
console.log(man1.name)//小呵 具备Man的属性和方法
赞(0) 打赏
未经允许不得转载:srcmini » JavaScript面向对象的程序设计及原型链的理解运用全解【重点】
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!

 

觉得文章有用就打赏一下文章作者

微信扫一扫打赏