浅谈Javascript中的对象和继承
Javascript是一门函数式编程语言,Javascript当中函数是核心,在Javascript中函数也是对象,函数对象在创建的时候会被添加属性和方法。
在Javascript中函数对象有两种调用方式,一种是new关键字的调用,另一种是没有new关键字的调用,前者会返回一个对象,后者会返回return语句中的内容。
functionObj(name){ this.name=name; returnname; }
用new关键字来调用如下:
varobj=newObj('张三')//返回{'name':'张三'}
不用new关键字调用如下:
varobj=Obj('张三')//返回'张三'
说完了js当中的对象和调用方式,那让我们来理解下什么是对象。
什么是对象?
对象是类的一个实例(对象可不是女朋友),有状态和行为。例如:一个电脑就是一个对象,它的状态有:大小、颜色、品牌等,他的行为有:播视频、听音乐、聊天等。
而类是对象的抽象,可以理解为类是一个模板,它来描述一类对象的状态和行为。软件对象也有状态和行为,软件对象的状态就是属性,行为就是方法。在软件开发中,在方法中可以操作对象的内部状态,对象的相互调用也是通过方法来完成。
类既然可以理解为一个模板,我们通过一个Person的简单例子来理解下:
functionPerson(name,age,sex){ this.name=name; this.age=age; this.sex=sex; run=function(){ console.log('Run') } }
在这个类中name,age,sex为这个类的属性,而run为这个类的方法;Person的职责是构造对象,进行对象的初始化。
接下来我们看下在js中如何声明并调用对象。
对象的创建(多种方法)
块级对象
varperson={ name:'王小端coder', age:29 } console.log(person.name);//王小端coder
构造函数---系统自带
varobj=newObject(); obj.name='王小端coder' console.log(obj.name);//王小端coder
系统自带的对象有:Object、Array、Number、Boolean、Date等
构造函数---自定义
functionObj(name){ this.name=name; } varobj=newObj('王小端coder'); console.log(obj.name);//王小端coder
对象的增删改查
增:为对象添加一个属性
varperson={}; person.name='王小端coder'; console.log(person.name);
删:通过delete操作符删除一个对象的属性
varperson={ name:'王小端coder' }; console.log(person.name);//王小端coder deleteperson.name; console.log(person.name);//undefined
改:直接通过赋值来修改一个对象的属性
varperson={ name:'王小端coder' }; console.log(person.name);//王小端coder person.name='王小端JS' console.log(person.name);//王小端JS
查:查询对象的属性,有两种方法
varperson={ name:'王小端coder' }; console.log(person['name']);//第一种方法 console.log(person.name);//第二种方法
原型的定义
原型是function对象的一个属性,它定义了构造函数制造出的对象的公共祖先。通过构造函数产生的对象,可以继承该原型的属性和方法,原型也是对象。
functionPerson(name,age){ this.name=name; this.age=age; } Person.prototype={ eat:function(food){ console.log('eat'+food); }, sleep:function(){ console.log('sleeping'); } } varperson=newPerson('王小端coder',29); console.log(person.name);//王小端coder person.eat('apple')//eatapple
我们定义了一个Person构造函数,而属于Person构造对象共有的方法,则定义到了Person原型上。
查看构造函数原型的接口:隐士属性__proto__(其实我们能够访问原型的属性,或者说继承原型,靠的就是__proto__属性连接着构造函数和原型,可以说没有__proto__属性的存在,就无法实现原型的继承)直接通过new操作符创建的对象访问__proto__属性即可查看到原型。
原型链
原型链就是将一个个原型串联起来,形成一条原型继承的链子。Child继承Parent,Parent继承GrandParent,而GrandParent没有自定义原型,所以默认为原型链的最顶端newObject();
对象的继承(多种方式)
创建的子类将继承超类的所有属性和方法,包括构造函数及方法的实现。记住,所有属性和方法都是公用的,因此子类可直接访问这些方法。子类还可添加超类中没有的新属性和方法,也可以覆盖超类的属性和方法。和其他功能一样,JavaScript实现继承的方式不止一种。这是因为JavaScript中的继承机制并不是明确规定的,而是通过模仿实现的。这意味着所有的继承细节并非完全由解释程序处理。作为开发者,你有权决定最适用的继承方式。
我们先定义一个父类
//定义一个父类 functionPerson(name){ this.name=name; this.sleep=function(){ console.log('朕正在睡觉!'); } } //在父类原型上面增加一个方法 Person.prototype.eat=function(food){ console.log('朕现在吃的食物是:'+food); }
下面为您介绍几种具体的继承方式:
原型链继承
//定义一个婴儿来继承人的父类 functionBaby(){ } Baby.prototype=newPerson(); Baby.prototype.name='小端'; varbaby=newBaby() console.log(baby.name);//小端 baby.eat('milk');//朕现在吃的食物是:milk
优点:简单、易于实现;父类新增的原型方法和属性子类都能访问到;
缺点:无法实现多继承;来自原型的对象的所有属性被所有实例共享;如果为子类增加属性和方法,无法放倒构造器中;创建子类实例时无法向父类构造函数传参;
构造继承
//定义一个婴儿来继承人的父类 functionBaby(name){ Person.call(this); this.name=name||'Coder' } letbaby=newBaby(); console.log(baby.name);//Coder baby.sleep();//朕正在睡觉! console.log(babyinstanceofPerson);//false console.log(babyinstanceofBaby);//true
优点:可以实现多继承;创建子类时可以向父类传递参数;子类可以共享父类引用的属性;
缺点:实例不是父类的实例;只能继承父类的实例属性和方法;无法实现函数复用,每个子类都有父类实例函数的副本,影响性能;
实例继承
functionBaby(name){ varinstance=newPerson(); instance.name=name||'Coder'; returninstance; } letbaby=newBaby(); console.log(baby.name);//Coder baby.sleep()//朕正在睡觉!
优点:不限制调用方式,返回的对象具有相同的效果;
缺点:不支持多继承;实例是父类的实例,不是子类的实例;
拷贝继承
//定义一个婴儿来继承人的父类 functionBaby(name){ varperson=newPerson(); for(letpinperson){ Baby.prototype[p]=person[p]; } Baby.prototype.name=name||'Coder'; } letbaby=newBaby(); console.log(baby.name);//Coder baby.sleep()//朕正在睡觉!
优点:支持多继承;
缺点:效率较低,内存占用高;无法获取父类不可枚举的方法;
注意:不可枚举的方法是不能使用forin访问到的方法
组合继承
//定义一个婴儿来继承人的父类 functionBaby(name){ Person.call(this); this.name=name||'Coder'; } Baby.prototype=newPerson(); Baby.prototype.constructor=Baby; letbaby=newBaby(); console.log(baby.name);//Coder baby.eat('milk');//朕现在吃的食物是:milk
优点:不存在引用属性共享问题,可以传参,函数可复用;
缺点:调用两次构造函数,生成了两份实例;
寄生组合继承
//定义一个婴儿来继承人的父类 functionBaby(name){ Person.call(this); this.name=name||'Coder'; } (function(){ letSuper=function(){}; Super.prototype=Person.prototype; Baby.prototype=newSuper(); })(); letbaby=newBaby(); console.log(baby.name);//Coder baby.eat('milk');//朕现在吃的食物是:milk
优点:整合了以上几种继承的优点;
缺点:实现复杂;
ES6的class
ES6提供了更接近传统语言的写法,引入了Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。基本上,ES6的class可以看作只是一个语法糖,它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
可以像如下定义:
classPerson{ constructor(name){ this.name=name; } sleep(){ console.log('朕正在睡觉!'); } eat(food){ console.log('朕现在吃的食物是:'+food); } } letperson=newPerson('小端'); console.log(person.name);//小端 person.eat('milk');//朕现在吃的食物是:milk
ES6class的继承
//定义一个婴儿来继承人的父类 classBabyextendsPerson{ constructor(name){ super(name); } } letbaby=newBaby('小端'); console.log(baby.name);//小端 baby.eat('milk');//朕现在吃的食物是:milk
我们可以看到Class可以通过extends关键字实现继承,这比ES5的通过修改原型链实现继承,要清晰和方便很多。
我之后会单独用一章来详细讲ES6中的Class和继承。
以上所述是小编给大家介绍的Javascript中的对象和继承详解整合,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对毛票票网站的支持!