传统面向对象语言:继承是类与类之间的关系。
而在js中由于es6之前没有类的概念,所以继承是对象与对象之间的关系。
在js中,继承就是指使一个对象有权去访问另一个对象的能力。
比如:比如对象a能够访问对象b的成员(属性和方法),那么就说对象a继承于对象b;
继承的实现方式有很多,常用的有:1.原型继承、2.类式继承、3.组合继承、4.extend方法
1、原型继承:
这里在说原型继承之前我们先来谈谈js中什么是原型,以及提到原型不得不要先来说说的js对象的创建方式。
问题一:对象的创建方式
//方法一:Object构造函数方式 var Person = new Object(); Person.name = “shuaige”; Person.age = 24; Person.sing = function() { console.log(‘twinkle twinkle little star’); } //这种方式的弊端是不能作为创建对象的模版,不能用new进行构造心对象
//方法二:对象字面量方法创建(字面量创建对象的方式优点就是解析的非常快) var Obj = { name : “shuaige”, age : 24, //定义方法 sing : function() { console.log(‘twinkle twinkle little star’); } } //调用对象中的方法 Obj.sing(); //查看对象中的属性 console.log(Obj.name);
//方法三:构造函数的方式创建对象(这里会提到构造函数存在的问题) function Person(name, age) {//构造函数的创建过程:1.创建this = {};2.给this赋值;3.返回this this.name = name; this.age = age; this.sing = function() { console.log(‘twinkle twinkle little star’); } //这里其实是有个隐式的返回的:return this; //构造函数中可以显示的指定return语句,如果返回的数据类型为基本数据类型或者null以及undefiend值都会被忽略,依旧返回的是构造函数的实例对象,但是返回的类型为引用类型,就不会忽略! } //实例化 var shuaige = new Person(“shuaige”, 24); var meinv = new Person(“meinv”, 24); //调用对象中的方法 shuaige.sing(); //查看对象中的属性 console.log(obj.name); //问题:shuaige和meinv两个实例对象的sing方法是一个方法么 Console.log(shuaige.sing == meinv.sing);//false //这就是构造函数方法创建对象的弊端,每个方法都要在每个实例对象中重新创建一遍,如果方法特别多,那么就会占用很多不必要的内存
为了解决构造函数创建对象中存在的问题因此而引出了原型!
Js中的原型:为了解决同类对象数据共享问题!
那么到底什么是原型?——->原型是指函数的prototype属性所引用的对象(所有函数都有prototype属性,Function除外)
改造以上构造函数方式创建对象的栗子如下:
function Person(name) {//构造函数Person this.name = name; } Person.prototype.sing = function() { console.log("twinkle twinkle little star"); } var p1 = new Person("xiaoming"); var p2 = new Person("xiaohong"); console.log(p1.sing == p2.sing);//true 原型由此解决了数据共享的问题 Person.prototype.address = "北京”;//定义公共属性address console.log(p1.address);//北京 console.log(p2.address);//北京 p1.address = "上海”;//添加私有属性 console.log(p1.address);//上海,自身有address属性,获取自身的address console.log(p2.address);//北京,自身没有address属性,去原型上面去找 p1.prototype.lacation = “a”//报错 can not set property location of undefined 因为实例对象是无权修改原型上的属性和方法的 //若想改变原型上的属性和方法要通过以下方式 Person.prototype.address = “西安” console.log(p1.address);//西安 console.log(p2.address);//西安
以上其实就是一种原型继承的方式,p1和p2都可以访问Person.prototype对象的属性和方法,所以在这里可以说是p1和p2都继承于Person.prototype;
2、类式继承(运用call方法改变this指向)
function Person(name, age) { this.name = name; This.age = age; this.say = function() { console.log(“hello”); } } function Student(name, age, id) { Person.call(this, name, age);//利用call方法能够改变this的指向,在此处调用Person构造函数,实现的是给Student实例对象添加以及赋值属性的操作 this.id = id; this.study = function () {//构造函数的弊端,在自身上面添加公共的方法,这就需要下面的组合继承方式来规避 console.log(“弟子不必不如师”); } } function Teacher(name, age, pay) { Person.call(this, name, age); this.pay = pay; this.teach = function () { console.log(“师不必不如弟子”); } } var st1 = new Student(“xiaoming”, 18); var tc1 = new Teacher(“mr Li”, 28);
3、组合继承,使用原型和类的组合方式实现继承
当Student和Teacher各自都有自己的公共方法时,可将方法添加到它们的原型上。
function Person(name, age) { this.name = name; This.age = age; this.say = function() { console.log(“hello”); } } function Student(name, age, id) { Person.call(this, name, age); this.id = id } Student.study = function () { //构造函数student的所有实例对象的公共方法 console.log(“弟子不必不如师”); } function Teacher(name, age, pay) { Person.call(this, name, age); this.pay = pay; } Teacher.prototype.teach = function() { //构造函数Teacher的所有实例对象的公共方法 console.log(“师不必不如弟子”); } var st1 = new Student(“xiaoming”, 18); var tc1 = new Teacher(“mr Li”, 28);
4、extend方法实现继承
这里extend方法是从jq中抽取出来的,es6中有一个同样功能的方法Object.assign();
function A(name) { this.name = name; } function B(sex) { this.sex = sex; this.sleep = function () { console.log("kun kun kun~~~") } } B.prototype.age = 20; var a = new A("xiaoming"); var b = new B("boy”) //extend方法思路:遍历parent对象中的属性以及方法并将其添加到child对象中去,从而实现继承,即child继承于parent,可以访问parent对象中的属性和方法 function extend (child, parent) { for (var pro in parent) { if (parent.hasOwnProperty(pro)) {//若没有这一行代码,parent对象原型上的属性和方法也会被遍历添加 child[pro] = parent[pro]; } } } extend(a, b); a.sleep();//kunkunkun~~~ console.log(a.age);//undefined;
关于创建对象的几种方式以及原型原型原型链相关知识会在日后总结。
想要做好一件事先要能够沉浸其中吧,静心做事,沉浸在代码的世界里,这也是一种享受,愿看到文章的朋友也会每天有收获,每天进步一点点。