| |
|
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
| -> JavaScript知识库 -> JavaScript继承的实现方式:原型语言对象继承对象原理剖析 -> 正文阅读 |
|
|
[JavaScript知识库]JavaScript继承的实现方式:原型语言对象继承对象原理剖析 |
|
面向对象编程:继承、封装、多态。 对象的继承:A 对象通过继承 B 对象,就能直接拥有 B 对象的所有属性和方法。这对于代码的复用是非常有用的。 在经典的面向对象语言中,您可能倾向于定义类对象,然后您可以简单地定义哪些类继承哪些类(参考C++ inheritance里的一些简单的例子),JavaScript使用了另一套实现方式,继承的对象函数并不是通过复制而来,而是通过原型链继承 回顾《再谈javascriptjs原型与原型链及继承相关问题》
JavaScript里面没有类这个概念,es6中class虽然很像类,但实际上只是es5上语法糖而已 function?People(name){
??//属性
??this.name??=?name?||?Annie
??//实例方法
??this.sleep=function(){
????console.log(this.name?+?'正在睡觉')
??}
}
//原型方法
People.prototype.eat?=?function(food){
??console.log(this.name?+?'正在吃:'?+?food);
}
JavaScript的基础方式,首推的就是原型继承 原型链继承父类的实例作为子类的原型 function?Woman(){?
????this.name=?"SubType";?//?子类属性
}
//?如果此处有Woman的原型对象上的方法,由于原型重定向,下面的代码会覆盖此方法
Woman.prototype=?new?People();//?重写原型对象,代之以一个新类型的实例
//?这里实例化一个?People时,?实际上执行了两步
//?1,新创建的对象复制了父类构造函数内的所有属性及方法
//?2,并将原型?__proto__?指向了父类的原型对象
Woman.prototype.name?=?'haixia';//子原型的属性
Woman.prototype.name?=?()=>{};//子原型方法
let?womanObj?=?new?Woman();
原型链继承优点:
原型链继承缺点:
解释原型重定向:?Woman.prototype= new People();
原型继承经典笔试题function?Parent?()?{
??this.a?=?1;
??this.b?=?[1,?2,?this.a];
??this.c?=?{demo:?5};
??this.show?=?function?()?{
????console.log(this.a?+?'?'?+?this.c.demo?+?':'?+?this.b?+?'\n');
??};
}
function?Child?()?{
??this.a?=?2;
??this.change?=?function?()?{
????this.b.push(this.a);
????this.a?=?this.b.length;
????this.c.demo?=?this.a++;
??};
}
Child.prototype?=?new?Parent();
var?parent?=?new?Parent();
var?child1?=?new?Child();
var?child2?=?new?Child();
child1.a?=?11;
child2.a?=?12;
child1.change();
child2.change();
parent.show();
child1.show();
child2.show();
思考原型公用问题 经典继承function?extendObj(obj)?{
??if?(Object.create)?{
????return?Object.create(obj)
??}?else?{
????function?F?()?{?}?
????F.prototype?=?obj;?
????return?new?F()
??}
}
var?obj?=?{?name:?"smd",?age:?26,?sayHi:?function?()?{?}?}
var?newObj?=?createObj(obj)
/*?Extend?Function?*/??
function?extend(subClass,superClass){??
????var?Func?=?function(){}?;??
????Func.prototype?=?superClass.prototype?;??
????subClass.prototype?=?new?Func()?;??
????subClass.prototype.constructor?=?subClass?;??
}?;
newObj继承了obj的属性和方法,但是同样出现了共享父类中引用类型属性的问题 实例继承(原型式继承)function?Wonman(name){
??let?instance?=?new?People();
??instance.name?=?name?||?'wangxiaoxia';
??return?instance;
}
let?wonmanObj?=?new?Wonman();
//?父类
function?People?(name)?{
??this.colors?=?["red",?"blue",?"green"];
??this.name?=?name;?//?父类属性
}
People.prototype.sayName?=?function?()?{?//?父类原型方法
??return?this.name;
};
/**?第一步?*/
//?子类,通过?call?继承父类的实例属性和方法,不能继承原型属性/方法
function?Woman?(name,?subName)?{
??People.call(this,?name);?//?调用?People?的构造函数,并向其传参?
??this.subName?=?subName;
}
/**?第二步?*/
//?解决?call?无法继承父类原型属性/方法的问题
//?Object.create?方法接受传入一个作为新创建对象的原型的对象,创建一个拥有指定原型和若干个指定属性的对象
//?通过这种方法指定的任何属性都会覆盖原型对象上的同名属性
Woman.prototype?=?Object.create(People.prototype,?{
??constructor:?{?//?注意指定?Woman.prototype.constructor?=?Woman
????value:?Woman,
????enumerable:?false,
????writable:?true,
????configurable:?true
??},
??run?:?{
????value:?function(){?//?override
??????People.prototype.run.apply(this,?arguments);
??????//?call?super
??????//?...
????},
????enumerable:?true,
????configurable:?true,
????writable:?true
??}
})
/**?第三步?*/
//?最后:解决?Woman.prototype.constructor?===?People?的问题
//?这里,在上一步已经指定,这里不需要再操作
//Woman.prototype.constructor?=?Woman;
var?instance?=?new?Woman('An',?'sistenAn')
实例继承优点:
实例继承缺点:
拷贝继承function?Wonman?(name)?{
??let?instance?=?new?People();
??for?(var?p?in?instance)?{
????Wonman.prototype[p]?=?instance[p];
??}
??Wonman.prototype.name=name||'Tom'
}
let?wonmanObj?=?new?Wonman();
特点:
缺点:
对象冒充继承function?Woman(name,?age)?{
??//3行关键代码?此三行用于获取父类的成员及方法
??//用子类的this去冒充父类的this,实现继承
??//父类People中的this.name、sleep,分别成为了子类的成员、子类的方法
??this.method?=?People;
??//接收子类的参数?传给父类
??this.method(name);
??//删除父类
??delete?this.method;
??//此后的this均指子类
??this.age?=?age;
??this.sayWorld?=?function()?{
????alert(age);
??}
}
因为对象冒充的留下,才有call apply的兴起 借用构造函数继承(伪造对象、经典继承)复制父类的实例属性给子类
function?Woman(name){
?//继承了People,子类的this传给父类
??People.call(this);?//People.call(this,'zhoulujun');?
??this.name?=?name?||?'andy'
}
let?womanObj?=?new?Woman();
通过这种调用,把父类构造函数的this指向为子类实例化对象引用,从而导致父类执行的时候父类里面的属性都会被挂载到子类的实例上去。 但是通过这种方式,父类原型上的东西是没法继承的,因此函数复用也就无从谈起 Woman无法继承Parent的原型对象,并没有真正的实现继承(部分继承) 借用构造函数继承优点:
借用构造函数继承缺点:
组合式继承调用父类构造函数,继承父类的属性,通过将父类实例作为子类原型,实现函数复用 function?Woman(name,age){
??People.call(this,name,age)
}
Woman.prototype?=?new?People();
Woman.prototype.constructor?=?Woman;
let?wonmanObj?=?new?Woman(ren,27);
wonmanObj.eat();
缺点:
优点:
寄生组合继承通过寄生的方式来修复组合式继承的不足,完美的实现继承 function?Woman(name,age){
??//继承父类属性
??People.call(this,name,age)
}
//继承父类方法,可以简化为:Woman.prototype?=?Object.create(People.prototype);
(function(){
??//?创建空类
??function?Super?(){};
??Super.prototype?=?People.prototype;
??//父类的实例作为子类的原型
??Woman.prototype?=?new?Super();
})();
//修复构造函数指向问题
Woman.prototype.constructor?=?Woman;
let?womanObj?=?new?Woman();
其实还是有两次执行 es6继承//class?相当于es5中构造函数
//class中定义方法时,前后不能加function,全部定义在class的protopyte属性中
//class中定义的所有方法是不可枚举的
//class中只能定义方法,不能定义对象,变量等
//class和方法内默认都是严格模式
//es5中constructor为隐式属性
class?People{
??constructor(name='wang',age='27'){
????this.name?=?name;
????this.age?=?age;
??}
??eat(){
????console.log(`${this.name}?${this.age}?eat?food`)
??}
}
//?继承父类属性,super关键字,它在这里表示父类的构造函数,用来新建父类的this对象。
//?子类必须在constructor方法中调用super方法,否则新建实例时会报错。如果子类没有定义constructor方法,这个方法会被默认添加,不管有没有显式定义,任何一个子类都有constructor方法。
class?Woman?extends?People{?
???constructor(name?=?'ren',age?=?'27'){?
?????super(name,?age);?
???}?
????eat(){?
?????//继承父类方法
??????super.eat()?
????}?
}?
let?wonmanObj=new?Woman('xiaoxiami');?
wonmanObj.eat();
ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。 ES6 的继承机制完全不同,实质是先创造父类的实例对象this(所以必须先调用super方法),然后再用子类的构造函数修改this。 /**
?*?继承
?*?@param?{*}?subClass?子类
?*?@param?{*}?superClass?父类
?*/
function?_inherits(subClass,?superClass)?{
??//?类型检测
??if?(!superClass?||?typeof?superClass?!==?"function"?)?{
????throw?new?TypeError("Super?expression?must?either?be?null?or?a?function,?not?"?+typeof?superClass);
??}
??/**
???*?Object.create?接受两个参数
???*?指定原型创建对象
???*?@param?{*}?目标原型
???*?@param?{*}?添加属性
???*/
??subClass.prototype?=?Object.create(superClass?&&?superClass.prototype,?{
????constructor:?{
??????value:?subClass,?//?subClass.prototype.constructor?指向?subClass
??????enumerable:?false,?//?constructor?不可枚举
??????writable:?true,
??????configurable:?true
????}
??});
??/**
???*?Object.setPrototypeOf?方法
???*?设置子类的?__proto__?属性指向父类
???*?@param?{*}?子类
???*?@param?{*}?父类
???*/
??if?(superClass)?{
????//?设置子类的__proto__?让?Child?能访问父类静态属性
????Object.setPrototypeOf
????????Object.setPrototypeOf(subClass,?superClass)
??????:?(subClass.__proto__?=?superClass);
??}
}
参考文章: JavaScript深入之继承的多种方式和优缺点 #16 https://github.com/mqyqingfeng/Blog/issues/16 JavaScript 中的继承 https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Objects/Inheritance JavaScript常见的六种继承方式 https://segmentfault.com/a/1190000016708006 js继承的几种方式?https://zhuanlan.zhihu.com/p/37735247 深入浅出js实现继承的7种方式?https://cloud.tencent.com/developer/article/1536957 前端面试必备之JS继承方式总结?https://www.imooc.com/article/20162 转载本站文章《JavaScript继承的实现方式:原型语言对象继承对象原理剖析》, |
|
|
| JavaScript知识库 最新文章 |
| ES6的相关知识点 |
| react 函数式组件 & react其他一些总结 |
| Vue基础超详细 |
| 前端JS也可以连点成线(Vue中运用 AntVG6) |
| Vue事件处理的基本使用 |
| Vue后台项目的记录 (一) |
| 前后端分离vue跨域,devServer配置proxy代理 |
| TypeScript |
| 初识vuex |
| vue项目安装包指令收集 |
|
|
| 上一篇文章 下一篇文章 查看所有文章 |
|
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
| 360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年11日历 | -2025/11/29 4:56:54- |
|
| 网站联系: qq:121756557 email:121756557@qq.com IT数码 |