js继承详解
2015-07-27 15:55
561 查看
this
this表示当前对象,如果在全局作用范围内使用this,则指代当前页面对象window; 如果在函数中使用this,则this指代什么是根据运行时此函数在什么对象上被调用。 我们还可以使用apply和call两个全局方法来改变函数中this的具体指向。prototype原型对象
所有函数都有一个prototype属性,当一个函数被定义的时候,prototype属性会被自动创建和初始化。prototype属性的初始值是一个对象,而这个对象只带一个属性,而这个属性名为constructor,即构造函数。在prototype对象中,还可以为其添加其他的属性。给这个原型对象添加的任何属性,都会成为被构造函数所初始化的对4000
象的属性。
看一个很有趣的现象
我们知道每个函数都有一个默认的属性prototype,而这个prototype的constructor默认指向这个函数。function Person(name) { this.name = name; }; Person.prototype.getName = function() { return this.name; }; var p = new Person("jay"); console.log(p.constructor === Person); // true console.log(Person.prototype.constructor === Person); // true
当时当我们重新定义函数的prototype时(注意:和上例的区别,这里不是修改而是覆盖), constructor的行为就有点奇怪了,如下示例:
function Person(name) { this.name = name; }; Person.prototype = { getName: function() { return this.name; } }; var p = new Person("jay"); console.log(p.constructor === Person); // false console.log(Person.prototype.constructor === Person); // false console.log(p.constructor.prototype.constructor === Person); // false
为什么呢?
原来是因为覆盖Person.prototype时,等价于进行如下代码操作:
Person.prototype = new Object({ getName: function() { return this.name; } });
而constructor始终指向创建自身的构造函数,所以此时Person.prototype.constructor === Object,即是:
function Person(name) { this.name = name; }; Person.prototype = { getName: function() { return this.name; } }; var p = new Person("ZhangSan"); console.log(p.constructor === Object); // true console.log(Person.prototype.constructor === Object); // true console.log(p.constructor.prototype.constructor === Object); // true
怎么修正这种问题呢?方法也很简单,重新覆盖Person.prototype.constructor即可:
function Person(name) { this.name = name; }; Person.prototype = { getName: function() { return this.name; } }; Person.prototype.constructor = Person; var p = new Person("ZhangSan"); console.log(p.constructor === Person); // true console.log(Person.prototype.constructor === Person); // true console.log(p.constructor.prototype.constructor === Person); // true
首先我们先利用组合模式创建一个 Person对象。
// 构造函数 function Person(name) { this.name = name; this.hobby = ["sing","dance"]; } // 定义Person的原型,原型中的属性可以被自定义对象引用 Person.prototype = { contructor: Person, getName: function() { return this.name; }, getHobby: function() { return this.hobby; } };
原型链
下面的例子创建了一个学生类Student,它从Person继承了原型prototype中的所有属性。function Student(name, school) { this.name = name; this.school = school; } Student.prototype = new Person(); Student.prototype.getSchool = function() { return this.school; }; var student1 = new Student("Taylor","Berklee College of Music"); console.log("姓名:"+student1.getName()+"学校:"+student1.getSchool()); student1.hobby.pop(); console.log(student1.getHobby()); var student2 = new Student("Jane","Harvard University"); console.log(student2.getHobby());//实例修改了对象里的变量
以上原型链继承很粗糙,存在的问题:
1.引用值类型的问题。父亲Person的实例属性变成了儿子Student的原型属性。所以所有的儿子对象的实例都共享这个hobby属性,即使指针的关系。所以通过儿子Student实例修改父亲Person的引用型变量,会直接因想到父亲对象Person的变量值。
2.Student的构造函数没法调用父类Person的构造函数。
构造函数继承方式
function Person(name) { this.name = name; this.hobby = ["sing","dance"]; } function Student(name){ Person.call(this,name); } var student1 = new Student("Taylor"); console.log(student1.hobby);//["sing", "dance"] student1.hobby.pop(); console.log(student1.hobby);//["sing"] var student2 = new Student("Jane"); console.log(student2.hobby);//["sing", "dance"]
原理:在儿子Student实例的环境下调用了父亲Person构造函数,这样每个儿子实例都会有一个自己的hobby副本。利用构造函数继承的方法,可以在儿子构造函数中像父亲构造函数传递参数。
缺点:对象的方法都只能在构造函数中定义,因此不能进行函数的复用。而且父亲对象的原型对象中定义的方法,对儿子来说是不可见的。所以该继承方法很少单独使用。
组合继承
思路:采用原型链实现对原型属性和方法的继承,借用构造函数来实现对实例属性的继承。即:通过在原型上定义方法实现函数的复用,并且保证每个实例都有自己的属性// 构造函数 function Person(name) { this.name = name; this.hobby = ["sing","dance"]; } // 定义Person的原型,原型中的属性可以被自定义对象引用 Person.prototype = { contructor: Person, getName: function() { return this.name; }, getHobby: function() { return this.hobby; } }; function Student(name,number){ Person.call(this,name);//第二次调用父对象的构造函数 this.number = number; } Student.prototype = new Person(); Student.prototype.constructor = Student; Student.prototype.geNumber = function(){ return this.number; }; var student1 = new Student("Taylor","1");//第一次调用父对象的构造函数 student1.hobby.pop(); console.log(student1.getHobby());//["sing"] console.log(student1.getName()); console.log(student1.geNumber()); var student2 = new Student("Swift","2"); student2.hobby.push("swim"); console.log(student2.getHobby());//["sing", "dance", "swim"] console.log(student2.getName()); console.log(student2.geNumber());
组合继承优点:避免了原型链和构造函数继承的缺陷,融合了各自的优点,是JS中最常用的继承模式。
缺点:
无论什么情况下,都会调用两次父对象的构造函数。第一次是在创建儿子对象实例的时候,第二次是在儿子构造函数的内部。
第一次:是在创建儿子对象实例的时候,此时儿子对象原型Student.prototype会得到两个属性name和hobby,他们都是父亲Person的实例属性。
第二次:是在儿子Student的构造函数内部。这次又在新对象上创建了name属性和hobby属性,并覆盖第一次的两个同名属性。
所以下面,我们采用寄生式组合继承的方式,解决上面出现的问题。
寄生式组合继承
本质:借用构造函数来继承属性,通过原型链的混成形式来继承方法。基本思路:其实上我们想要的就是父亲Person的原型对象的一个副本而已。所以,本质上,我们采用寄生式继承来继承父亲Person的原型对象,然后再把结果赋予子对象的原型。
基本模式
function inheritPrototype(Parent,child){ var prototype = Object(Parent.prototype);//创建父亲原型对象副本 prototype.constructor = child;//为副本增加constructor属性 child.prototype = prototype;//赋予子类型原型 }
下面采用寄生式组合继承:
// 定义Person的原型,原型中的属性可以被自定义对象引用 Person.prototype = { contructor:Person, getName: function() { return this.name; }, getHobby: function() { return this.hobby; } }; function Student(name,number){ Person.call(this,name); this.number = number; } inheritPrototype(Person,Student); Student.prototype.geNumber = function(){ return this.number; }; var student1 = new Student("Taylor","1"); student1.hobby.pop(); console.log(student1.getHobby());//["sing"] console.log(student1.getName()); console.log(student1.geNumber()); var student2 = new Student("Swift","2"); student2.hobby.push("swim"); console.log(student2.getHobby());//["sing", "dance", "swim"] console.log(student2.getName()); console.log(student2.geNumber());
寄生式组合继承是基于类型继承最有效的方式。
相关文章推荐
- JQuery1——基础($对象,选择器,对象转换)
- Android学习笔记(二九):嵌入浏览器
- Android java 与 javascript互访(相互调用)的方法例子
- Python动态类型的学习---引用的理解
- JavaScript演示排序算法
- javascript实现10进制转为N进制数
- 2019年开发人员应该学习的8个JavaScript框架
- HTML中的script标签研究
- 异步流程控制:7 行代码学会 co 模块
- JavaScript拆分字符串时产生空字符的原因
- IE8开发人员工具教程(二)
- Mootools 1.2教程 函数
- autoit InputBox 函数
- 土人系列AS入门教程 -- 对象篇
- 文件遍历排序函数
- 在flex中执行一个javascript方法的简单方式
- Flex结合JavaScript读取本地路径的方法
- PostgreSQL教程(三):表的继承和分区表详解
- C#托管堆对象实例包含内容分析