摘要:面向对象基础在的时候发生了什么大家在日常的开发中,会经常见到类似于下面的代码块大黄黄色到这边的话,我们就成功创建了一个类的对象。参考面向对象编程一封装面向对象编程二封装面向对象编程三非构造函数的继承
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__并不是所有浏览器都支持);此时执行结果就像大家想的那样分别为猫科动物,狗科动物,狗科动物。
谈到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()的过程更加轻量,更加节约内存。
参考Prototye inheritance
Javascript 面向对象编程(一):封装
Javascript 面向对象编程(二):封装
Javascript面向对象编程(三):非构造函数的继承
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/88197.html
摘要:面向对象三大特征继承性多态性封装性接口。第五阶段封装一个属于自己的框架框架封装基础事件流冒泡捕获事件对象事件框架选择框架。核心模块和对象全局对象,,,事件驱动,事件发射器加密解密,路径操作,序列化和反序列化文件流操作服务端与客户端。 第一阶段: HTML+CSS:HTML进阶、CSS进阶、div+css布局、HTML+css整站开发、 JavaScript基础:Js基础教程、js内置对...
摘要:面向对象三大特征继承性多态性封装性接口。第五阶段封装一个属于自己的框架框架封装基础事件流冒泡捕获事件对象事件框架选择框架。核心模块和对象全局对象,,,事件驱动,事件发射器加密解密,路径操作,序列化和反序列化文件流操作服务端与客户端。 第一阶段: HTML+CSS:HTML进阶、CSS进阶、div+css布局、HTML+css整站开发、 JavaScript基础:Js基础教程、js内置对...
摘要:对象对象的定义对象是由键值对组成的无序集合。创建对象两种方法方法一字面量方法方法二构造函数创建面向对象和面向过程的比较如果想要把大象放进冰箱。 1.对象 对象的定义 : 对象 是 由 键值对 组成的无序集合。 创建对象两种方法 : 方法一 : 字面量方法 var obj = {name: k}; 方法二 : new Object( ) 构造函数创建 var a = n...
摘要:对象对象的定义对象是由键值对组成的无序集合。创建对象两种方法方法一字面量方法方法二构造函数创建面向对象和面向过程的比较如果想要把大象放进冰箱。 1.对象 对象的定义 : 对象 是 由 键值对 组成的无序集合。 创建对象两种方法 : 方法一 : 字面量方法 var obj = {name: k}; 方法二 : new Object( ) 构造函数创建 var a = n...
阅读 2370·2021-09-22 15:41
阅读 1424·2021-08-19 10:54
阅读 1704·2019-08-23 15:11
阅读 3340·2019-08-23 10:23
阅读 1392·2019-08-22 16:28
阅读 773·2019-08-22 15:11
阅读 709·2019-08-22 14:53
阅读 674·2019-08-22 13:49