Easy to understand object-oriented JS in principle and the way to get to know the prototype introduced __proto__

This article mainly to talk about object-oriented and JS  __proto__, ptototypeand construcator, these concepts are related, so to talk together.

Before we talk about this First is that class, to understand object-oriented friends should know, if I want to define a generic type I can use the class (class). For example, in java, we can define a class:

public class Puppy{
    int puppyAge; public Puppy(age){ puppyAge = age; } public void say() { System.out.println("汪汪汪"); } }

The code Puppy we define a class that has a property is puppyAge, i.e. the age of the dog, and a constructor Puppy () This constructor takes one argument, the age of the dog may be provided, in addition to a talking function say. This is a generic class, when we need a two year old puppy example is a direct write, while this example has a parent class method:

Puppy myPuppy = new Puppy( 2 );
myPuppy.say();     // 汪汪汪

But early JS ah no class keyword (say JS JS did not refer to the class keyword before ES6, mainly to help people understand the concept), JS to support object-oriented, uses a more tortuous way, which is leading we confuse the place, in fact we will in this way with the general object-oriented analogy it is very clear. Let us look at JS To support object-oriented issues which need to be addressed, both by what tortuous way to solve.

No class, instead of a function

First class keyword did not even JS, how to do it? Instead of using the function, JS most is the lack of function, function not only to perform common functions, but also when the class use. For example, we use JS to build a puppy class how to write it? Write a direct function on the line:

function Puppy() {}

This function can create an instance directly with the new keyword:

const myPuppy = new Puppy();

We also have a puppy example, but we do not have a constructor, you can not set the age of the dog ah.

The constructor function itself

As a class by itself is a function of a function, and he is the default constructor. We want to set an example of Puppy function of age, as long as he receives the parameters on the line.

function Puppy(age) {
  this.puppyAge = age; } // 实例化时可以传年龄参数了 const myPuppy = new Puppy(2);

Note that the above code this, this function which is used as a class instance always points to the object, i.e. myPuppy. The purpose of such a design is to allow the user to set the properties of an object by the constructor for instance, this time out to see the console myPuppy.puppyAgeis 2.

console.log(myPuppy.puppyAge);   // 输出是 2

Examples of the method with prototype

Above we realized the class and constructors, but class methods? Java version of the puppies can also "bark Wang" called it, JS version of how to do it? JS solution is added to the analysis method of a prototypeproperty, in which the above loading method, the instantiated to give the object instance. We want myPuppy can speak, you need to Puppy.prototypeadd a method to speak.

Puppy.prototype.say = function() {
  console.log("汪汪汪"); }

Examples of using the new keyword has generated class prototypeattributes and methods, we are Puppy.prototypeon to add a say method, myPuppy can speak, what I have to try:

myPuppy.say();    // 汪汪汪

Examples of methods used to find__proto__

That myPuppy how you can call the saymethod of it, we look to him to print out, and did not say ah this object, which is come from?

image-20200221180325943

This is the __proto__play, when you access an object property is not on, for example myPuppy.say, objects to __proto__find. __proto__The prototype is equal to the value of the parent class,  myPuppy.__proto__pointing Puppy.prototype.

image-20200221181132495

If you visit the property Puppy.prototypedoes not exist, it will continue to go Puppy.prototype.__proto__on to find, in fact, this time to find Object.prototype, and Object.prototypethen further on to find it did not, it is null, this is actually the prototype chain .

image-20200221181533277

constructor

We are talking generally refers to the class constructor prototype.constructor. prototype.constructorIs a reserved attribute on the prototype, the class attribute points to the function itself, for indicating the current class constructor.

image-20200221183238691

image-20200221182045545

Since it prototype.constructoris a pointer to a constructor, that we can not be modified by the constructor do it? Let's try to know. Let's modify this function and then create a new instance look at the results:

function Puppy(age) {
  this.puppyAge = age; } Puppy.prototype.constructor = function myConstructor(age) { this.puppyAge2 = age + 1; } const myPuppy2 = new Puppy(2); console.log(myPuppy2.puppyAge); // 输出是2

The example shows, we modify prototype.constructoronly modified this indicator only, and does not modify the real constructor.

Some friends say I print myPuppy2.constructoralso have value ah, that constructoris a property of the object itself is not it? Well, not exactly, you can print out the reason why this value is because when you print found myPuppy2 itself does not have this property, and went looking for the prototype chain, he found prototype.constructor. We can hasOwnPropertylook to know:

image-20200222152216426

Above we have in fact made it clear that prototype, __proto__, constructorthe relationship between several persons, the following draw a picture to look more intuitive:

image-20200222153906550

Static method

We know there are a lot of object-oriented concept of static methods, such as direct Java is adding a statickeyword will be able to define a method for the static method. JS define a static method is simpler, it directly as a function of the line attribute class:

Puppy.statciFunc = function() {    // statciFunc就是一个静态方法
  conlose.log('我是静态方法,this拿不到实例对象'); } Puppy.statciFunc(); // 直接通过类名调用

Static and instance methods The main difference is an instance method can access the instance, can operate for instance, and static methods generally used for instance nothing to do with the operation. Both of these methods have in jQuery large number of applications, in jQuery $(selector)actually get is an instance of an object by the $(selector)method of operation is an instance method. For example $(selector).append(), it will go in this example DOM add a new element, he needs to know how the DOM instance operation, appendas an instance method, he would point to the inside of this example, DOM instance can operate through this. What method is suitable as a static way to do that? For example $.ajax, here ajaxwith DOM instance it does not matter, this does not need this, it can be directly mounted on the $ as a static method.

inherit

面向对象怎么能没有继承呢,根据前面所讲的知识,我们其实已经能够自己写一个继承了。所谓继承不就是子类能够继承父类的属性和方法吗?换句话说就是子类能够找到父类的prototype,最简单的方法就是子类原型的__proto__指向父类原型就行了。

function Parent() {}
function Child() {} Child.prototype.__proto__ = Parent.prototype; const obj = new Child(); console.log(obj instanceof Child ); // true console.log(obj instanceof Parent ); // true

上述继承方法只是让Child访问到了Parent原型链,但是没有执行Parent的构造函数:

function Parent() {
  this.parentAge = 50; } function Child() {} Child.prototype.__proto__ = Parent.prototype; const obj = new Child(); console.log(obj.parentAge); // undefined

为了解决这个问题,我们不能单纯的修改Child.prototype.__proto__指向,还需要用new执行下Parent的构造函数:

function Parent() {
  this.parentAge = 50; } function Child() {} Child.prototype.__proto__ = new Parent(); const obj = new Child(); console.log(obj.parentAge); // 50

上述方法会多一个__proto__层级,可以换成修改Child.prototype的指向来解决,注意将Child.prototype.constructor重置回来:

function Parent() {
  this.parentAge = 50; } function Child() {} Child.prototype = new Parent(); Child.prototype.constructor = Child; // 注意重置constructor const obj = new Child(); console.log(obj.parentAge); // 50

当然还有很多其他的继承方式,他们的原理都差不多,只是实现方式不一样,核心都是让子类拥有父类的方法和属性,感兴趣的朋友可以自行查阅。

自己实现一个new

结合上面讲的,我们知道new其实就是生成了一个对象,这个对象能够访问类的原型,知道了原理,我们就可以自己实现一个new了。

function myNew(func, ...args) {
  const obj = {}; // 新建一个空对象 func.call(obj, ...args); // 执行构造函数 obj.__proto__ = func.prototype; // 设置原型链 return obj; } function Puppy(age) { this.puppyAge = age; } Puppy.prototype.say = function() { console.log("汪汪汪"); } const myPuppy3 = myNew(Puppy, 2); console.log(myPuppy3.puppyAge); // 2 console.log(myPuppy3.say()); // 汪汪汪

自己实现一个instanceof

知道了原理,其实我们也知道了instanceof是干啥的。instanceof不就是检查一个对象是不是某个类的实例吗?换句话说就是检查一个对象的的原型链上有没有这个类的prototype,知道了这个我们就可以自己实现一个了:

function myInstanceof(targetObj, targetClass) {
  // 参数检查 if(!targetObj || !targetClass || !targetObj.__proto__ || !targetClass.prototype){ return false; } let current = targetObj; while(current) { // 一直往原型链上面找 if(current.__proto__ === targetClass.prototype) { return true; // 找到了返回true } current = current.__proto__; } return false; // 没找到返回false } // 用我们前面的继承实验下 function Parent() {} function Child() {} Child.prototype.__proto__ = Parent.prototype; const obj = new Child(); console.log(myInstanceof(obj, Child) ); // true console.log(myInstanceof(obj, Parent) ); // true console.log(myInstanceof({}, Parent) ); // false

总结

最后来个总结,其实前面小节的标题就是核心了,我们再来总结下:

  1. JS中的函数可以作为函数使用,也可以作为类使用
  2. 作为类使用的函数实例化时需要使用new
  3. 为了让函数具有类的功能,函数都具有prototype属性。
  4. 为了让实例化出来的对象能够访问到prototype上的属性和方法,实例对象的__proto__指向了类的prototype。所以prototype是函数的属性,不是对象的。对象拥有的是__proto__,是用来查找prototype的。
  5. prototype.constructorIt points to the constructor, which is a function of the class itself. Change the pointer does not change the constructor.
  6. The object itself is not constructorproperty, you have access to is the prototype chain prototype.constructor.
  7. Functions themselves are objects, also __proto__, he points to the JS built-in object Functionprototype Function.prototype. So you can call func.call, func.applythese methods, you actually call Function.prototype.calland Function.prototype.apply.
  8. prototypeItself is an object, so he has __proto__, pointing to his parent prototype. __proto__And prototypethis point constitutes a chain of JS prototype chain. The final point of the prototype chain is Objectthe prototype. ObjectThe above prototype chain is null, i.e. Object.prototype.__proto__ === null.
  9. Also note that Function.__proto__ === Function.prototypebecause JS prototype all functions are Function.prototype, which means that all functions are Functionexamples. FunctionFunction itself can be used as a ---- Function(), so he is also Functionan instance. There are similar Object, Arrayand so on, they can also be used as a function: Object()Array(). So is the prototype of their own Function.prototype, that is Object.__proto__ === Function.prototype. In other words, these may be new built-in objects are in fact a kind, just like our Puppy same category.

Then look at the complete picture:

image-20200222160832782

Guess you like

Origin www.cnblogs.com/itps/p/12356413.html