首页 web前端 js教程 JavaScript程序中实现继承特性的方式(图文教程)

JavaScript程序中实现继承特性的方式(图文教程)

May 21, 2018 am 09:33 AM
javascript js

JavaScript是一门强行声称面向对象的语言,而继承是面向对象的一大主要特性,下面是我给大家整理的,有兴趣的同学可以去看看。

概述JavaScript的所有对象,都有自己的继承链。也就是说,每个对象都继承另一个对象,该对象称为“原型”(prototype)对象。只有null除外,它没有自己的原型对象。

原型对象的重要性在于,如果A对象是B对象的原型,那么B对象可以拿到A对象的所有属性和方法。Object.getPrototypof方法用于获取当前对象的原型对象。

var p = Object.getPrototypeOf(obj);
登录后复制

上面代码中,对象p就是对象obj的原型对象。

Object.create方法用于生成一个新的对象,继承指定对象。

var obj = Object.create(p);
登录后复制

上面代码中,新生成的obj对象的原型就是对象p。

非标准的__proto__属性(前后各两个下划线),可以改写某个对象的原型对象。但是,应该尽量少用这个属性,而是用Object.getPrototypeof()和Object.setPrototypeOf(),进行原型对象的读写操作。

var obj = {};
var p = {};

obj.__proto__ = p;
Object.getPrototypeOf(obj) === p // true
登录后复制

上面代码通过__proto__属性,将p对象设为obj对象的原型。

下面是一个实际的例子。

var a = {x: 1};
var b = {__proto__: a};
b.x // 1
登录后复制

上面代码中,b对象通过__proto__属性,将自己的原型对象设为a对象,因此b对象可以拿到a对象的所有属性和方法。b对象本身并没有x属性,但是JavaScript引擎通过__proto__属性,找到它的原型对象a,然后读取a的x属性。

new命令通过构造函数新建实例对象,实质就是将实例对象的原型绑定构造函数的prototype属性,然后在实例对象上执行构造函数。

var o = new Foo();

// 等同于
var o = new Object();
o.__proto__ = Foo.prototype;
Foo.call(o);
登录后复制

原型对象自己的__proto__属性,也可以指向其他对象,从而一级一级地形成“原型链”(prototype chain)。

var a = { x: 1 };
var b = { __proto__: a };
var c = { __proto__: b };

c.x // 1
登录后复制

需要注意的是,一级级向上,在原型链寻找某个属性,对性能是有影响的。所寻找的属性在越上层的原型对象,对性能的影响越大。如果寻找某个不存在的属性,将会遍历整个原型链。

this的动作指向不管this在哪里定义,使用的时候,它总是指向当前对象,而不是原型对象。

var o = {
 a: 2,
 m: function(b) {
  return this.a + 1;
 }
};

var p = Object.create(o);
p.a = 12;

p.m() // 13
登录后复制

上面代码中,p对象的m方法来自它的原型对象o。这时,m方法内部的this对象,不指向o,而是指向p。

构造函数的继承这个小节介绍,如何让一个构造函数,继承另一个构造函数。

假定有一个Shape构造函数。

function Shape() {
 this.x = 0;
 this.y = 0;
}

Shape.prototype.move = function (x, y) {
 this.x += x;
 this.y += y;
 console.info('Shape moved.');
};
Rectangle构造函数继承Shape。

function Rectangle() {
 Shape.call(this); // 调用父类构造函数
}
// 另一种写法
function Rectangle() {
 this.base = Shape;
 this.base();
}

// 子类继承父类的方法
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;

var rect = new Rectangle();

rect instanceof Rectangle // true
rect instanceof Shape // true

rect.move(1, 1) // 'Shape moved.'
登录后复制

上面代码表示,构造函数的继承分成两部分,一部分是子类调用父类的构造方法,另一部分是子类的原型指向父类的原型。

上面代码中,子类是整体继承父类。有时,只需要单个方法的继承,这时可以采用下面的写法。

ClassB.prototype.print = function() {
 ClassA.prototype.print.call(this);
 // some code
}
登录后复制

上面代码中,子类B的print方法先调用父类A的print方法,再部署自己的代码。这就等于继承了父类A的print方法。

__proto__属性__proto__属性指向当前对象的原型对象,即构造函数的prototype属性。

var obj = new Object();

obj.__proto__ === Object.prototype
// true
obj.__proto__ === obj.constructor.prototype
// true
登录后复制

上面代码首先新建了一个对象obj,它的__proto__属性,指向构造函数(Object或obj.constructor)的prototype属性。所以,两者比较以后,返回true。

因此,获取实例对象obj的原型对象,有三种方法。

  • obj.__proto__

  • obj.constructor.prototype

  • Object.getPrototypeOf(obj)

上面三种方法之中,前两种都不是很可靠。最新的ES6标准规定,__proto__属性只有浏览器才需要部署,其他环境可以不部署。而obj.constructor.prototype在手动改变原型对象时,可能会失效。

var P = function () {};
var p = new P();

var C = function () {};
C.prototype = p;
var c = new C();

c.constructor.prototype === p // false
登录后复制

上面代码中,C构造函数的原型对象被改成了p,结果c.constructor.prototype就失真了。所以,在改变原型对象时,一般要同时设置constructor属性。

C.prototype = p;
C.prototype.constructor = C;

c.constructor.prototype === p // true
登录后复制

所以,推荐使用第三种Object.getPrototypeOf方法,获取原型对象。该方法的用法如下。

var o = new Object();

Object.getPrototypeOf(o) === Object.prototype
// true
登录后复制

可以使用Object.getPrototypeOf方法,检查浏览器是否支持__proto__属性,老式浏览器不支持这个属性。

Object.getPrototypeOf({ __proto__: null }) === null
登录后复制

上面代码将一个对象的__proto__属性设为null,然后使用Object.getPrototypeOf方法获取这个对象的原型,判断是否等于null。如果当前环境支持__proto__属性,两者的比较结果应该是true。

有了__proto__属性,就可以很方便得设置实例对象的原型了。假定有三个对象machine、vehicle和car,其中machine是vehicle的原型,vehicle又是car的原型,只要两行代码就可以设置。

vehicle.__proto__ = machine;
car.__proto__ = vehicle;
登录后复制

下面是一个实例,通过__proto__属性与constructor.prototype属性两种方法,分别读取定义在原型对象上的属性。

Array.prototype.p = 'abc';
var a = new Array();

a.__proto__.p // abc
a.constructor.prototype.p // abc
登录后复制

显然,__proto__看上去更简洁一些。

通过构造函数生成实例对象时,实例对象的__proto__属性自动指向构造函数的prototype对象。

var f = function (){};
var a = {};

f.prototype = a;
var o = new f();

o.__proto__ === a
// true
登录后复制

属性的继承属性分成两种。一种是对象自身的原生属性,另一种是继承自原型的继承属性。

对象的原生属性对象本身的所有属性,可以用Object.getOwnPropertyNames方法获得。

Object.getOwnPropertyNames(Date)
// ["parse", "arguments", "UTC", "caller", "name", "prototype", "now", "length"]
登录后复制

对象本身的属性之中,有的是可以枚举的(enumerable),有的是不可以枚举的。只获取那些可以枚举的属性,使用Object.keys方法。

Object.keys(Date) // []
hasOwnProperty()
登录后复制

hasOwnProperty方法返回一个布尔值,用于判断某个属性定义在对象自身,还是定义在原型链上。

Date.hasOwnProperty('length')
// true

Date.hasOwnProperty('toString')
// false
登录后复制

hasOwnProperty方法是JavaScript之中唯一一个处理对象属性时,不会遍历原型链的方法。

对象的继承属性用Object.create方法创造的对象,会继承所有原型对象的属性。

var proto = { p1: 123 };
var o = Object.create(proto);

o.p1 // 123
o.hasOwnProperty("p1") // false
登录后复制

获取所有属性判断一个对象是否具有某个属性(不管是自身的还是继承的),使用in运算符。

"length" in Date // true
"toString" in Date // true
登录后复制

获得对象的所有可枚举属性(不管是自身的还是继承的),可以使用for-in循环。

var o1 = {p1: 123};

var o2 = Object.create(o1,{
 p2: { value: "abc", enumerable: true }
});

for (p in o2) {console.info(p);}
// p2
// p1
登录后复制

为了在for...in循环中获得对象自身的属性,可以采用hasOwnProperty方法判断一下。

for ( var name in object ) {
 if ( object.hasOwnProperty(name) ) {
  /* loop code */
 }
}
登录后复制

获得对象的所有属性(不管是自身的还是继承的,以及是否可枚举),可以使用下面的函数。

function inheritedPropertyNames(obj) {
 var props = {};
 while(obj) {
  Object.getOwnPropertyNames(obj).forEach(function(p) {
   props[p] = true;
  });
  obj = Object.getPrototypeOf(obj);
 }
 return Object.getOwnPropertyNames(props);
}
登录后复制

用法如下:

inheritedPropertyNames(Date)
// ["caller", "constructor", "toString", "UTC", "call", "parse", "prototype", "__defineSetter__", "__lookupSetter__", "length", "arguments", "bind", "__lookupGetter__", "isPrototypeOf", "toLocaleString", "propertyIsEnumerable", "valueOf", "apply", "__defineGetter__", "name", "now", "hasOwnProperty"]
登录后复制

对象的拷贝如果要拷贝一个对象,需要做到下面两件事情。

确保拷贝后的对象,与原对象具有同样的prototype原型对象。
确保拷贝后的对象,与原对象具有同样的属性。
下面就是根据上面两点,编写的对象拷贝的函数。

function copyObject(orig) {
 var copy = Object.create(Object.getPrototypeOf(orig));
 copyOwnPropertiesFrom(copy, orig);
 return copy;
}

function copyOwnPropertiesFrom(target, source) {
 Object
 .getOwnPropertyNames(source)
 .forEach(function(propKey) {
  var desc = Object.getOwnPropertyDescriptor(source, propKey);
  Object.defineProperty(target, propKey, desc);
 });
 return target;
}
登录后复制

多重继承JavaScript不提供多重继承功能,即不允许一个对象同时继承多个对象。但是,可以通过变通方法,实现这个功能。

function M1(prop) {
 this.hello = prop;
}

function M2(prop) {
 this.world = prop;
}

function S(p1, p2) {
 this.base1 = M1;
 this.base1(p1);
 this.base2 = M2;
 this.base2(p2);
}
S.prototype = new M1();

var s = new S(111, 222);
s.hello // 111
s.world // 222
登录后复制

上面代码中,子类S同时继承了父类M1和M2。当然,从继承链来看,S只有一个父类M1,但是由于在S的实例上,同时执行M1和M2的构造函数,所以它同时继承了这两个类的方法。

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

详细介绍在JS中Map和ForEach的区别

js装饰设计模式学习心得(详细解答)

详解解读JS数值Number类型(图文教程)

以上是JavaScript程序中实现继承特性的方式(图文教程)的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

热门话题

Java教程
1662
14
CakePHP 教程
1419
52
Laravel 教程
1312
25
PHP教程
1262
29
C# 教程
1235
24
推荐:优秀JS开源人脸检测识别项目 推荐:优秀JS开源人脸检测识别项目 Apr 03, 2024 am 11:55 AM

人脸检测识别技术已经是一个比较成熟且应用广泛的技术。而目前最为广泛的互联网应用语言非JS莫属,在Web前端实现人脸检测识别相比后端的人脸识别有优势也有弱势。优势包括减少网络交互、实时识别,大大缩短了用户等待时间,提高了用户体验;弱势是:受到模型大小限制,其中准确率也有限。如何在web端使用js实现人脸检测呢?为了实现Web端人脸识别,需要熟悉相关的编程语言和技术,如JavaScript、HTML、CSS、WebRTC等。同时还需要掌握相关的计算机视觉和人工智能技术。值得注意的是,由于Web端的计

如何使用WebSocket和JavaScript实现在线语音识别系统 如何使用WebSocket和JavaScript实现在线语音识别系统 Dec 17, 2023 pm 02:54 PM

如何使用WebSocket和JavaScript实现在线语音识别系统引言:随着科技的不断发展,语音识别技术已经成为了人工智能领域的重要组成部分。而基于WebSocket和JavaScript实现的在线语音识别系统,具备了低延迟、实时性和跨平台的特点,成为了一种被广泛应用的解决方案。本文将介绍如何使用WebSocket和JavaScript来实现在线语音识别系

WebSocket与JavaScript:实现实时监控系统的关键技术 WebSocket与JavaScript:实现实时监控系统的关键技术 Dec 17, 2023 pm 05:30 PM

WebSocket与JavaScript:实现实时监控系统的关键技术引言:随着互联网技术的快速发展,实时监控系统在各个领域中得到了广泛的应用。而实现实时监控的关键技术之一就是WebSocket与JavaScript的结合使用。本文将介绍WebSocket与JavaScript在实时监控系统中的应用,并给出代码示例,详细解释其实现原理。一、WebSocket技

股票分析必备工具:学习PHP和JS绘制蜡烛图的步骤 股票分析必备工具:学习PHP和JS绘制蜡烛图的步骤 Dec 17, 2023 pm 06:55 PM

股票分析必备工具:学习PHP和JS绘制蜡烛图的步骤,需要具体代码示例随着互联网和科技的快速发展,股票交易已经成为许多投资者的重要途径之一。而股票分析是投资者决策的重要一环,其中蜡烛图被广泛应用于技术分析中。学习如何使用PHP和JS绘制蜡烛图将为投资者提供更多直观的信息,帮助他们更好地做出决策。蜡烛图是一种以蜡烛形状来展示股票价格的技术图表。它展示了股票价格的

如何利用JavaScript和WebSocket实现实时在线点餐系统 如何利用JavaScript和WebSocket实现实时在线点餐系统 Dec 17, 2023 pm 12:09 PM

如何利用JavaScript和WebSocket实现实时在线点餐系统介绍:随着互联网的普及和技术的进步,越来越多的餐厅开始提供在线点餐服务。为了实现实时在线点餐系统,我们可以利用JavaScript和WebSocket技术。WebSocket是一种基于TCP协议的全双工通信协议,可以实现客户端与服务器的实时双向通信。在实时在线点餐系统中,当用户选择菜品并下单

如何使用WebSocket和JavaScript实现在线预约系统 如何使用WebSocket和JavaScript实现在线预约系统 Dec 17, 2023 am 09:39 AM

如何使用WebSocket和JavaScript实现在线预约系统在当今数字化的时代,越来越多的业务和服务都需要提供在线预约功能。而实现一个高效、实时的在线预约系统是至关重要的。本文将介绍如何使用WebSocket和JavaScript来实现一个在线预约系统,并提供具体的代码示例。一、什么是WebSocketWebSocket是一种在单个TCP连接上进行全双工

PHP与JS开发技巧:掌握绘制股票蜡烛图的方法 PHP与JS开发技巧:掌握绘制股票蜡烛图的方法 Dec 18, 2023 pm 03:39 PM

随着互联网金融的迅速发展,股票投资已经成为了越来越多人的选择。而在股票交易中,蜡烛图是一种常用的技术分析方法,它能够显示股票价格的变化趋势,帮助投资者做出更加精准的决策。本文将通过介绍PHP和JS的开发技巧,带领读者了解如何绘制股票蜡烛图,并提供具体的代码示例。一、了解股票蜡烛图在介绍如何绘制股票蜡烛图之前,我们首先需要了解一下什么是蜡烛图。蜡烛图是由日本人

JavaScript和WebSocket:打造高效的实时天气预报系统 JavaScript和WebSocket:打造高效的实时天气预报系统 Dec 17, 2023 pm 05:13 PM

JavaScript和WebSocket:打造高效的实时天气预报系统引言:如今,天气预报的准确性对于日常生活以及决策制定具有重要意义。随着技术的发展,我们可以通过实时获取天气数据来提供更准确可靠的天气预报。在本文中,我们将学习如何使用JavaScript和WebSocket技术,来构建一个高效的实时天气预报系统。本文将通过具体的代码示例来展示实现的过程。We

See all articles