js中实现继承的几种方式
首先我们了解,js中的继承是主要是由原型链实现的。那么什么是原型链呢?
由于每个实例中都有一个指向原型对象的指针,如果一个对象的原型对象,是另一个构造函数的实例,这个对象的原型对象就会指向另一个对象的原型对象,如此循环,就行成了原型链。
在了解原型链之后,我们还需要了解属性搜索机制,所谓的属性搜索机制,就是当我们访问对象上的一个属性时,我们如何找到这个属性值。首先,我们现在当前实例中查找该属性,如果找到了,返回该值,否则,通过__proto__找到原型对象,在原型对象中进行搜索,如果找到,返回该值,否则,继续向上进行搜索,直到找到该属性,或者在原型链中没有找到,返回undefined。
根据《javascript高级程序设计》中,可以有六种继承方式,下面我们一一来介绍:
1. 原型链
// 父亲类
function Parent() {
this.value = 'value';
}
Parent.prototype.sayHi = function() {
console.log('Hi');
}
// 儿子类
function Child() { }
// 改变儿子的prototype属性为父亲的实例
Child.prototype = new Parent(); var child = new Child();
// 首先现在child实例上进行查找,未找到,
// 然后找到原型对象(Parent类的一个实例),在进行查找,未找到,
// 在根据__proto__进行找到原型,发现sayHi方法。 // 实现了Child继承
child.sayHi();
但是这种继承方式存在一个问题,那就是引用类型属性共享。
// 父亲类
function Parent() {
this.color = ['pink', 'red'];
} // 儿子类
function Child() { }
Child.prototype = new Parent(); var child1 = new Child();
var child2 = new Child();
// 先输出child1和child2种color的值
console.log(child1.color); // ["pink", "red"]
console.log(child2.color); // ["pink", "red"] // 在child1的color数组添加white
child1.color.push('white');
console.log(child1.color); // ["pink", "red", "white"]
// child1上的改动,child2也会受到影响
console.log(child2.color); // ["pink", "red", "white"]
它存在第二个问题,就是无法向父类种传参。
2. 借用构造函数
在这里,我们借用call函数可以改变函数作用域的特性,在子类中调用父类构造函数,复制父类的属性。此时没调用一次子类,复制一次。此时,每个实例都有自己的属性,不共享。同时我们可以通过call函数给父类传递参数。
2.1 解决引用类型共享问题
// 父亲类
function Parent(name) {
this.name = name;
this.color = ['pink', 'red'];
} // 儿子类
function Child() {
Parent.call(this); // 定义自己的属性
this.value = 'test';
} var child1 = new Child();
var child2 = new Child(); // 先输出child1和child2种color的值
console.log(child1.color); // ["pink", "red"]
console.log(child2.color); // ["pink", "red"] // 在child1的color数组添加white
child1.color.push('white');
console.log(child1.color); // ["pink", "red", "white"]
// child1上的改动,child2并没有受到影响
console.log(child2.color); // ["pink", "red"]
2.2 解决传参数问题
// 父亲类
function Parent(name) {
this.name = name;
this.color = ['pink', 'red'];
} // 儿子类
function Child(name) {
Parent.call(this, name); // 定义自己的属性
this.value = 'test';
} var child = new Child('qq');
// 将qq传递给Parent
console.log(child.name); // qq
当时,上述方法也存在一个问题,共享的方法都在构造函数中定义,无法达到函数复用的效果。
3. 组合继承
根据上述两种方式,我们可以扬长避短,将需要共享的属性使用原型链继承的方法继承,将实例特有的属性,用借用构造函数的方式继承。
// 父亲类
function Parent() {
this.color = ['pink', 'red'];
}
Parent.prototype.sayHi = function() {
console.log('Hi');
} // 儿子类
function Child() {
// 借用构造函数继承
Parent.call(this); // 下面可以自己定义需要的属性
}
// 原型链继承
Child.prototype = new Parent(); var child1 = new Child();
var child2 = new Child(); // 每个实例特有的属性
// 先输出child1和child2种color的值
console.log(child1.color); // ["pink", "red"]
console.log(child2.color); // ["pink", "red"] // 在child1的color数组添加white
child1.color.push('white');
console.log(child1.color); // ["pink", "red", "white"]
// child1上的改动,child2并没有受到影响
console.log(child2.color); // ["pink", "red"] // 每个实例共享的属性
child1.sayHi(); // Hi
child2.sayHi(); // Hi
上述方法,虽然综合了原型链和借用构造函数的优点,达到了我们想要的结果,但是它存在一个问题。就是创建一次实例时,两次调用了父类构造函数。
// 父亲类
function Parent() {
this.color = ['pink', 'red'];
}
Parent.prototype.sayHi = function() {
console.log('Hi');
} // 儿子类
function Child() {
Parent.call(this); // 第二次调用构造函数:在新对象上创建一个color属性
}
Child.prototype = new Parent(); // 第一次调用构造函数Child.prototype将会得到一个color属性,屏蔽了原型中的color属性。
因此,出现了寄生组合式继承。在了解之前,我们先了解一下什么是寄生式继承。
4. 寄生式继承
同工厂模式类似,将我们需要继承的函数进行封装,然后进行某种增强,在返回对象。
function Parent() {
this.color = ['pink', 'red'];
}
function createAnother(o) {
// 获得当前对象的一个克隆
var another = new Object(o);
// 增强对象
o.sayHi = function() {
console.log('Hi');
}
// 返回对象
return another;
}
5. 寄生组合式继承
// 创建只继承原型对象的函数
function inheritPrototype(parent, child) {
// 创建一个原型对象副本
var prototype = new Object(parent.prototype);
// 设置constructor属性
prototype.constructor = child;
child.prototype = prototype;
} // 父亲类
function Parent() {
this.color = ['pink', 'red'];
}
Parent.prototype.sayHi = function() {
console.log('Hi');
} // 儿子类
function Child() {
Parent.call(this);
} inheritPrototype(Parent, Child);
6. 原型式继承
思想:基于已有的对象创建对象。
function createAnother(o) {
// 创建一个临时构造函数
function F() {
}
// 将传入的对象作为它的原型
F.prototype = o;
// 返回一个实例
return new F();
}
js中实现继承的几种方式的更多相关文章
- js中原型继承的三种方式
- JavaScript学习12 JS中定义对象的几种方式【转】
avaScript学习12 JS中定义对象的几种方式 转自: http://www.cnblogs.com/mengdd/p/3697255.html JavaScript中没有类的概念,只有对象. ...
- JavaScript学习12 JS中定义对象的几种方式
JavaScript学习12 JS中定义对象的几种方式 JavaScript中没有类的概念,只有对象. 在JavaScript中定义对象可以采用以下几种方式: 1.基于已有对象扩充其属性和方法 2.工 ...
- JS中事件绑定的三种方式
以下是搜集的在JS中事件绑定的三种方式. 1. HTML onclick attribute <button type="button" id="upl ...
- js中声明Number的五种方式
转载自:http://www.jb51.net/article/34191.htm <!DOCTYPE html> <html> <head> <meta c ...
- javascript中实现继承的几种方式
javascript中实现继承的几种方式 1.借用构造函数实现继承 function Parent1(){ this.name = "parent1" } function Chi ...
- JS中检测数据类型的几种方式及优缺点【转】
1.typeof 用来检测数据类型的运算符 typeof value 返回值首先是一个字符串,其次里面包含了对应的数据类型,例如:"number"."string&quo ...
- JS中检测数据类型的几种方式及优缺点
1.typeof 用来检测数据类型的运算符 typeof value 返回值首先是一个字符串,其次里面包含了对应的数据类型,例如:"number"."string&quo ...
- JS中检测数据类型的四种方式及每个方式的优缺点
//1.typeof 用来检测数据类型的运算符 //->typeof value //->返回值首先是一个字符串,其次里面包含了对应的数据类型,例如:"number". ...
随机推荐
- ssh无法远程登陆别的机器
ssh无法远程登陆别的机器,提示报错: ssh: symbol lookup error: ssh: undefined symbol: EVP_aes_128_ctr 解决方法如下: 给相应配置文件 ...
- JDBC连接数据库(二)——连接池
参考博客:http://www.cnblogs.com/xdp-gacl/p/4002804.html 一. 应用程序直接建立数据库连接模型 应用程序直接每次访问数据库时,都建立创建一个数据库的链接, ...
- css之line-height
行内框盒子模型1."内容区域"(content area)2."内联盒子"(inline boxes)3."行框盒子"(line boxes ...
- Lucky7(容斥原理)
Problem Description When ?? was born, seven crows flew in and stopped beside him. In its childhood, ...
- MongoDB【第一篇】安装
第一步:准备 1. 操作系统 CentOS-7-x86_64-Everything-1511 2. MongoDB 版本 mongodb-linux-x86_64-rhel70-3.4.2 3. 设置 ...
- 百度网盘免费扩容 免费扩容到2048G
您可以用手机端扩容 http://yun.baidu.com/1t 免费扩容到2048G
- 读书笔记 effective c++ Item 25 实现一个不抛出异常的swap
1. swap如此重要 Swap是一个非常有趣的函数,最初作为STL的一部分来介绍,它已然变成了异常安全编程的中流砥柱(Item 29),也是在拷贝中应对自我赋值的一种普通机制(Item 11).Sw ...
- SpringMvc的运行流程
一.先用文字描述 1.用户发送请求到DispatchServlet 2.DispatchServlet根据请求路径查询具体的Handler 3.HandlerMapping返回一个HandlerExc ...
- 《类型编程晋级——shapeless类库使用指南》前言及第一章翻译
从年初开始进行此项工作,我和合作伙伴包亮付出了大量而艰辛的劳动,现基本翻译完毕,有出版意向,如果有意向欢迎联系,不甚感激!也欢迎各位博友对此翻译提出意见建议以及指导如何出版,在此谢过! 前言 时间回到 ...
- 理解margin负值
效果 上和左方的margin负值使元素向上和左方向移动,如果该元素position不是absolute或fixed,这还会导致之后的元素也向上,左移 下和右方的margin负值会缩小下和右方的空间,使 ...