This article mainly to talk about object-oriented and JS __proto__
, ptototype
and 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.puppyAge
is 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 prototype
property, in which the above loading method, the instantiated to give the object instance. We want myPuppy can speak, you need to Puppy.prototype
add a method to speak.
Puppy.prototype.say = function() {
console.log("汪汪汪"); }
Examples of using the new keyword has generated class prototype
attributes and methods, we are Puppy.prototype
on 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 say
method of it, we look to him to print out, and did not say ah this object, which is come from?
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
.
If you visit the property Puppy.prototype
does not exist, it will continue to go Puppy.prototype.__proto__
on to find, in fact, this time to find Object.prototype
, and Object.prototype
then further on to find it did not, it is null, this is actually the prototype chain .
constructor
We are talking generally refers to the class constructor prototype.constructor
. prototype.constructor
Is a reserved attribute on the prototype, the class attribute points to the function itself, for indicating the current class constructor.
Since it prototype.constructor
is 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.constructor
only modified this indicator only, and does not modify the real constructor.
Some friends say I print myPuppy2.constructor
also have value ah, that constructor
is 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 hasOwnProperty
look to know:
Above we have in fact made it clear that prototype
, __proto__
, constructor
the relationship between several persons, the following draw a picture to look more intuitive:
Static method
We know there are a lot of object-oriented concept of static methods, such as direct Java is adding a static
keyword 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, append
as 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 ajax
with 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
总结
最后来个总结,其实前面小节的标题就是核心了,我们再来总结下:
- JS中的函数可以作为函数使用,也可以作为类使用
- 作为类使用的函数实例化时需要使用new
- 为了让函数具有类的功能,函数都具有
prototype
属性。 - 为了让实例化出来的对象能够访问到
prototype
上的属性和方法,实例对象的__proto__
指向了类的prototype
。所以prototype
是函数的属性,不是对象的。对象拥有的是__proto__
,是用来查找prototype
的。 prototype.constructor
It points to the constructor, which is a function of the class itself. Change the pointer does not change the constructor.- The object itself is not
constructor
property, you have access to is the prototype chainprototype.constructor
. - Functions themselves are objects, also
__proto__
, he points to the JS built-in objectFunction
prototypeFunction.prototype
. So you can callfunc.call
,func.apply
these methods, you actually callFunction.prototype.call
andFunction.prototype.apply
. prototype
Itself is an object, so he has__proto__
, pointing to his parentprototype
.__proto__
Andprototype
this point constitutes a chain of JS prototype chain. The final point of the prototype chain isObject
the prototype.Object
The above prototype chain is null, i.e.Object.prototype.__proto__ === null
.- Also note that
Function.__proto__ === Function.prototype
because JS prototype all functions areFunction.prototype
, which means that all functions areFunction
examples.Function
Function itself can be used as a ----Function()
, so he is alsoFunction
an instance. There are similarObject
,Array
and so on, they can also be used as a function:Object()
,Array()
. So is the prototype of their ownFunction.prototype
, that isObject.__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: