Home  >  Article  >  Web Front-end  >  How to implement inheritance features in JavaScript programs (graphic tutorial)

How to implement inheritance features in JavaScript programs (graphic tutorial)

亚连
亚连Original
2018-05-21 09:33:35844browse

JavaScript is a language that forcibly claims to be object-oriented, and inheritance is a major feature of object-oriented. Here is what I have compiled for you. Interested students can take a look.

OverviewAll objects in JavaScript have their own inheritance chain. That is, every object inherits another object, which is called a "prototype" object. Except for null, it does not have its own prototype object.

The importance of the prototype object is that if the A object is the prototype of the B object, then the B object can get all the properties and methods of the A object. The Object.getPrototypof method is used to obtain the prototype object of the current object.

var p = Object.getPrototypeOf(obj);

In the above code, object p is the prototype object of object obj.

The Object.create method is used to generate a new object and inherit the specified object.

var obj = Object.create(p);

In the above code, the prototype of the newly generated obj object is object p.

The non-standard __proto__ attribute (two underscores before and after) can overwrite the prototype object of an object. However, you should use this attribute as little as possible, and instead use Object.getPrototypeof() and Object.setPrototypeOf() to read and write prototype objects.

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

obj.__proto__ = p;
Object.getPrototypeOf(obj) === p // true

The above code sets the p object as the prototype of the obj object through the __proto__ attribute.

The following is a practical example.

var a = {x: 1};
var b = {__proto__: a};
b.x // 1

In the above code, the b object sets its prototype object to the a object through the __proto__ attribute, so the b object can get all the attributes and methods of the a object. The b object itself does not have an x ​​attribute, but the JavaScript engine finds its prototype object a through the __proto__ attribute, and then reads the x attribute of a.

The new command creates a new instance object through the constructor. The essence is to bind the prototype of the instance object to the prototype attribute of the constructor, and then execute the constructor on the instance object.

var o = new Foo();

// 等同于
var o = new Object();
o.__proto__ = Foo.prototype;
Foo.call(o);

The prototype object's own __proto__ attribute can also point to other objects, thereby forming a "prototype chain" level by level.

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

c.x // 1

It should be noted that searching for a certain attribute in the prototype chain one level up will have an impact on performance. The properties you are looking for are in the upper-level prototype object, the greater the impact on performance. If you look for a property that doesn't exist, the entire prototype chain will be traversed.

This action points to No matter where this is defined, when used, it always points to the current object, not the prototype object.

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

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

p.m() // 13

In the above code, the m method of the p object comes from its prototype object o. At this time, the this object inside the m method does not point to o, but to p.

Inheritance of ConstructorThis section introduces how to make one constructor inherit another constructor.

Assume there is a Shape constructor.

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.'

The above code indicates that the inheritance of the constructor is divided into two parts. One part is that the subclass calls the constructor method of the parent class, and the other part is that the prototype of the subclass points to the prototype of the parent class.

In the above code, the subclass inherits the parent class as a whole. Sometimes, only the inheritance of a single method is needed. In this case, the following writing method can be used.

ClassB.prototype.print = function() {
 ClassA.prototype.print.call(this);
 // some code
}

In the above code, the print method of subclass B first calls the print method of parent class A, and then deploys its own code. This is equivalent to inheriting the print method of parent class A.

__proto__ attribute __proto__ attribute points to the prototype object of the current object, that is, the prototype attribute of the constructor.

var obj = new Object();

obj.__proto__ === Object.prototype
// true
obj.__proto__ === obj.constructor.prototype
// true

The above code first creates a new object obj, and its __proto__ attribute points to the prototype attribute of the constructor (Object or obj.constructor). Therefore, after comparing the two, return true.

There are three methods to obtain the prototype object of the instance object obj.

  • obj.__proto__

  • obj.constructor.prototype

  • Object.getPrototypeOf(obj)

Among the above three methods, the first two are not very reliable. The latest ES6 standard stipulates that the __proto__ attribute only needs to be deployed in browsers, and does not need to be deployed in other environments. Obj.constructor.prototype may become invalid when manually changing the prototype object.

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

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

c.constructor.prototype === p // false

In the above code, the prototype object of the C constructor is changed to p, and as a result, c.constructor.prototype is distorted. Therefore, when changing the prototype object, you generally need to set the constructor attribute at the same time.

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

c.constructor.prototype === p // true

Therefore, it is recommended to use the third Object.getPrototypeOf method to obtain the prototype object. The method is used as follows.

var o = new Object();

Object.getPrototypeOf(o) === Object.prototype
// true

You can use the Object.getPrototypeOf method to check whether the browser supports the __proto__ attribute. Older browsers do not support this attribute.

Object.getPrototypeOf({ __proto__: null }) === null

The above code sets the __proto__ attribute of an object to null, and then uses the Object.getPrototypeOf method to obtain the prototype of the object and determine whether it is equal to null. If the current environment supports the __proto__ attribute, the comparison result between the two should be true.

With the __proto__ attribute, you can easily set the prototype of the instance object. Suppose there are three objects machine, vehicle and car, where machine is the prototype of vehicle, and vehicle is the prototype of car. It only takes two lines of code to set it up.

vehicle.__proto__ = machine;
car.__proto__ = vehicle;

The following is an example of reading the properties defined on the prototype object through two methods: __proto__ attribute and constructor.prototype attribute.

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

a.__proto__.p // abc
a.constructor.prototype.p // abc

Obviously, __proto__ looks more concise.

通过构造函数生成实例对象时,实例对象的__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类型(图文教程)

The above is the detailed content of How to implement inheritance features in JavaScript programs (graphic tutorial). For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn