摘要:的面向对象主要包含了两块创建对象继承。构造函数一般来说,我们可以这样定义构造函数构造函数的函数名常大写在这里,我们没有显示的创建对象,没有语句,却将属性和方法赋值给了。
面向对象是软件开发方法。面向对象的概念和应用已超越了程序设计和软件开发,扩展到如数据库系统、交互式界面、应用结构、应用平台、分布式系统、网络管理结构、CAD技术、人工智能等领域。面向对象是一种对现实世界理解和抽象的方法,是计算机编程技术发展到一定阶段后的产物。
JavaScript的面向对象主要包含了两块:1)创建对象 2)继承。接下来,我们将走进JS对象的世界,将依次带你深入了解函数、闭包、原型、原型链,并通过它们,最终实现创建对象和继承。
如果现在有一个纯牛奶,那么我们创建对象可以这样操作:
var milk ={ name:"纯牛奶", taste:"pure", price:4 }
那么如果现在有4种不同口味的牛奶呢?也创建四个不同的对象吗?milk1、milk2、milk3、milk4?若有一万种呢,是否也创建一万个对象?
显然,这是不合理的。这里,我们就要引入一个概念——工厂模式。
function createMilk(name,taste,price){ return { name:name, taste:taste, price:price } } var milk1 = createMilk("纯牛奶","pure",4); var milk2 = createMilk("有机奶","organic",4); var milk3 = createMilk("低脂奶","low-fat",4);
在这里,我们实用一个函数,传入产品名、口味、价格3个参数,返回一个对象。这样,我们只要调用这个函数,并传入不同的参数,即可构建不同的对象。这就是工厂模式。
工厂模式有优点也有缺点:
1)优点:简单易懂、常见且实用。
2)缺点:对于如何证明我是一个牛奶这个问题上,则无法证明。//这句如若无法理解的话,可以暂时忽略,继续往下看哦!
为了更好的讲解构造函数、原型、原型链等,建议你复习一下函数的一些基础知识,如果你已经对函数有深入的了解,可以选择跳过。戳这里:JS函数的一些基础知识
构造函数还记得我们之前在讲述工厂模式的缺点时,所说的那句“对于如何证明我是一个牛奶这个问题上,则无法证明。”吗?接下来,就是见证奇迹的时刻!
var obj = new Object(); var add = new Function("a","b","return a+b"); console.log(obj instanceof Object);//true console.log(add instanceof Function);//true
Object和Function都是原生的构造函数,在这里我们就可以使用instanceof来判断是否为它的一个实例——即:证明了我就是一个牛奶哦!!
既然有原生的构造函数,那么我们可以不可以也自己定义构造函数呢?答案是可以的。
一般来说,我们可以这样定义构造函数:
//构造函数的函数名常大写 //在这里,我们没有显示的创建对象,没有return语句,却将属性和方法赋值给了this。 function Milk(name,taste,price){ this.name = name; this.taste = taste; this.price = price; } //new操作符会默认的创建一个新对象,将function的this指向对象,然后将该对象赋值,对象就有了三个属性。 var milk1 = new Milk("纯牛奶","pure",4); console.log(milk1 instanceof Milk);//true
function Milk(name,taste,price){ this.name = name; this.taste = taste; this.price = price; this.say = function(){ console.log("Hello World"); }; } var milk1 = new Milk("纯牛奶","pure",4); var milk2 = new Milk("纯牛奶","pure",4);
假设我们创建了一个Milk的构造函数,里面除了属性还带有一个say的方法,当我们new了两个对象之后,两个对象milk1和milk2是否都包含了功能相同的say方法呢?
这就是构造函数的不足之处:功能相同的函数,重复声明消耗空间!看来,我们的路还没有走到终点。
原型是函数的一个属性,是一个对象。如果函数作为构造函数使用,那么这个构造函数的所有实例,都共享这个原型对象。
「注」之前我们回忆函数时,回忆到,函数有三个常见属性:name,length和prototype喔!如果遗忘,可以戳这里:JS函数的一些基础知识!
1)constructor
原型的constructor是一个对象,我们可以这样简单的验证一下:
Object.prototype.constructor === Object //true
2)读写
function Milk() {} Milk.prototype.name = "纯牛奶"; Milk.prototype.taste = "pure"; Milk.prototype.price = 4; Milk.prototype.say = function(){ console.log("Hello World"); }; var milk1 = new Milk();
运行的结果如下:
通过这种方式,可以解决内存问题。但也会因此而共享name,taste,price和say(),尤其是共享name,taste和price,会产生问题。
3)isPrototypeOf
我们可以通过isPrototypeOf来进行原型的判定,如下:
function Milk() {} Milk.prototype.name = "纯牛奶"; Milk.prototype.taste = "pure"; Milk.prototype.price = 4; Milk.prototype.say = function(){ console.log("Hello World"); }; var milk1 = new Milk(); console.log(Milk.prototype.isPrototypeOf(milk1));//true
原型是函数的一个属性,是一个对象。如果函数作为构造函数使用,那么这个构造函数的所有实例,都共享这个原型对象。
原型的不足,本质上是共享的缺陷。我们可以看如下一段代码:
var price = 10; var priceCopy = price; priceCopy = 20; console.log(price,priceCopy);//10,20
我们再看如下一段代码:
var taste = ["pure","organic"]; var tasteCopy = taste; tasteCopy.push("low fat"); console.log(taste,tasteCopy);//["pure", "organic", "low fat"],["pure", "organic", "low fat"]
由此我们可见:
对于基本类型,price和priceCopy是在内存中分别挖两块地方存储,因此,priceCopy的值改变的时候,并不影响price的值;
而对于引用类型,name和nameCopy是调用同一个引用,引用同一个数据,当我们在nameCopy添加一个名称的时候,引用的数据就添加了一个值,name也就因此而受到了影响,这就是数据的污染。
共享会污染数据类型,因此原型创建对象也会污染数据类型。我们看下面一段代码:
function Milk(){} Milk.prototype.taste = ["pure","organic"]; var m1 = new Milk(); var m2 = new Milk(); m2.taste.push("low fat"); console.log("m1",m1.taste);//["pure", "organic", "low fat"] console.log("m2",m2.taste);//["pure", "organic", "low fat"]
通过这段代码,我们可以清楚的了解到原型创建对象主要的不足具体表现在:
原型创建对象会产生共享的问题
不能再额外传递参数进去。
构造函数结合原型构造函数有一定的优缺点,原型也有一定的优缺点,如果我们把两者优点结合,将会是一种不错的创建对象的方式。我们看如下的代码:
function Milk(name,taste,price){//构造函数独享属性 this.name = name; this.taste = taste; this.price = price; } Milk.prototype.say = function(){//原型共享方法 console.log(this.name); } var m1 = new Milk("纯牛奶","pure","4"); m1.say();//纯牛奶
在这段代码中,我们使用构造函数来独享属性,以避免原型创建对象会产生的共享问题,当然,我们也使用原型共享方法,从而达到拒绝功能相同的函数导致的重复声明消耗空间问题。
构造函数结合原型的一些细节问题在学习来构造函数结合原型创建对象的基础之上,我们来关心一些细节性的问题,以便于我们深入了解构造函数结合原型。如,构造函数和原型上的属性是否会覆盖,优先顺序又是什么?再如,如何判断属性是在原型上还是在构造函数之上呢?
1)属性的覆盖
我们通过如下两段代码,总结关于构造函数结合原型的属性覆盖:
//1 function Milk(name,taste,price){//构造函数独享属性 this.name = name; this.taste = taste; this.price = price; } Milk.prototype.name = "牛奶"; Milk.prototype.say = function(){//原型共享方法 console.log(this.name); } var m1 = new Milk("纯牛奶","pure","4"); console.log(m1.name);//纯牛奶
//2 function Milk(name,taste,price){//构造函数独享属性 //this.name = name; this.taste = taste; this.price = price; } Milk.prototype.name = "牛奶"; Milk.prototype.say = function(){//原型共享方法 console.log(this.name); } var m1 = new Milk("纯牛奶","pure","4"); console.log(m1.name);//牛奶
从两端代码中,我们对比得知,实例上的属性会覆盖原型上的属性。即:会先在实例中查找,如没有,则再在原型上查找。
2)属性的判断
(1)in操作符
我们可以通过如下三段代码,总结关于in操作符的知识,即:只要对象里有值,即无论是在构造函数之上还是在原型之上均返回true,若都不在,则返回false。
//1 function Milk(name,taste,price){//构造函数独享属性 this.name = name; this.taste = taste; this.price = price; } Milk.prototype.say = function(){//原型共享方法 console.log(this.name); } var m1 = new Milk("纯牛奶","pure","4"); console.log("name" in m1);//ture
//2 function Milk(name,taste,price){//构造函数独享属性 //this.name = name; this.taste = taste; this.price = price; } Milk.prototype.name = "牛奶"; Milk.prototype.say = function(){//原型共享方法 console.log(this.name); } var m1 = new Milk("纯牛奶","pure","4"); console.log("name" in m1);//ture
//3 function Milk(name,taste,price){//构造函数独享属性 //this.name = name; this.taste = taste; this.price = price; } //Milk.prototype.name = "牛奶"; Milk.prototype.say = function(){//原型共享方法 console.log(this.name); } var m1 = new Milk("纯牛奶","pure","4"); console.log("name" in m1);//false
(2)hasOwnProperty
我们可以通过如下两段代码,总结关于in操作符的知识,即:判断是在实例上还是原型上,挂在实例上返回true,反之false。
//1 function Milk(name,taste,price){//构造函数独享属性 this.name = name; this.taste = taste; this.price = price; } //Milk.prototype.name = "牛奶"; Milk.prototype.say = function(){//原型共享方法 console.log(this.name); } var m1 = new Milk("纯牛奶","pure","4"); console.log(m1.hasOwnProperty("name"));//true
//2 function Milk(name,taste,price){//构造函数独享属性 //this.name = name; this.taste = taste; this.price = price; } Milk.prototype.name = "牛奶"; Milk.prototype.say = function(){//原型共享方法 console.log(this.name); } var m1 = new Milk("纯牛奶","pure","4"); console.log(m1.hasOwnProperty("name"));//false小结
在创建对象这个板块中,我们从工厂模式开始讲起,再到构造函数,接着到原型,最后到比较完善的构造函数结合原型,在接下来的继承板块中,我们将讲述原型链、继承以及最佳方式的相关知识,好好复习!
继承(更新中)当然,最最最最最后,如果您喜欢这片文章,可以疯狂点赞或者收藏喔!!?
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/93187.html
摘要:如果没有学习过计算机科学的程序员,当我们在处理一些问题时,比较熟悉的数据结构就是数组,数组无疑是一个很好的选择。 showImg(https://segmentfault.com/img/bVTSjt?w=400&h=300); 1、常见 CSS 布局方式详见: 一些常见的 CSS 布局方式梳理,涉及 Flex 布局、Grid 布局、圣杯布局、双飞翼布局等。http://cherryb...
摘要:如果没有学习过计算机科学的程序员,当我们在处理一些问题时,比较熟悉的数据结构就是数组,数组无疑是一个很好的选择。 showImg(https://segmentfault.com/img/bVTSjt?w=400&h=300); 1、常见 CSS 布局方式详见: 一些常见的 CSS 布局方式梳理,涉及 Flex 布局、Grid 布局、圣杯布局、双飞翼布局等。http://cherryb...
摘要:设计模式是以面向对象编程为基础的,的面向对象编程和传统的的面向对象编程有些差别,这让我一开始接触的时候感到十分痛苦,但是这只能靠自己慢慢积累慢慢思考。想继续了解设计模式必须要先搞懂面向对象编程,否则只会让你自己更痛苦。 JavaScript 中的构造函数 学习总结。知识只有分享才有存在的意义。 是时候替换你的 for 循环大法了~ 《小分享》JavaScript中数组的那些迭代方法~ ...
摘要:在规范中,引入了的概念。使用中的声明一个类,是非常简单的事。中面向对象实例化的背后原理,实际上就是原型对象。与区别理解上述原理后,还需要注意与属性的区别。实际上,在中,类继承的本质依旧是原型对象。 在 ES6 规范中,引入了 class 的概念。使得 JS 开发者终于告别了,直接使用原型对象模仿面向对象中的类和类继承时代。 但是JS 中并没有一个真正的 class 原始类型, clas...
阅读 3127·2021-11-04 16:09
阅读 3045·2021-09-23 11:49
阅读 3581·2021-09-09 09:33
阅读 3538·2021-08-18 10:22
阅读 2021·2019-08-30 15:55
阅读 3595·2019-08-30 15:53
阅读 2639·2019-08-28 18:08
阅读 870·2019-08-26 18:18