1. There are no real classes in JS!
JavaScript is different from class-oriented languages in that it does not have classes as an abstract model for objects. JavaScript only has objects, and there is no real class . JS just takes advantage of a special feature of the function-all functions will have a public and non-enumerable attribute by default named prototype, which will point to another object to simulate the behavior of the class.
It should be noted that if the built-in bind function is used to generate a hard-bound function, the function has no prototype attribute, and the prototype of the target function will replace the prototype of the hard-bound function. instanceof or new on such a function is equivalent to directly using the target function.
function Animal() {};
console.log(Animal.prototype);// {}
In JS, new Animal()
looks like it instantiates the Animal
class, but in fact it is not.
const a = new Animal();
console.log(Object.getPrototypeOf(a) === Animal.prototype);// true
In a class-oriented language, a class can be copied (or instantiated) multiple times. Instantiating a class means "copying the behavior of the class into a physical object", and this process is repeated for each new instance.
But in JS, there is no similar copy mechanism. You cannot create multiple instances of a class, you can only create multiple objects. Their " prototype " related to the same object. However, by default, copying is not performed, so these objects will not completely lose contact, they are related to each other. new Animal()
will generate a new object (we call it a
), the internal link of this new object " prototype " associated with the Animal.prototype
object. We did not initialize a class. In fact, we did not copy any behavior from the "class" to an object, just let the two objects relate to each other.
2. What is the constructor in JS?
function Animal() {};
const a = new Animal();
There is no real class in JS, but when I see these two lines of code, I still think that Animal
is a class. Why is this? In my opinion, one reason lies in the presence of the new operator. In class-oriented languages, the new operator is required. Another reason is that in new Animal()
in, Animal
the Called particularly such like is called when the class is instantiated class called by the constructor .
But in fact, Animal
is no different from other functions in your program. The function itself is not a constructor, but when we add the new
keyword in front of the ordinary function call, it will turn the function call into a "constructor call" ( new will hijack all ordinary functions And call it in the form of a constructed object).
Simply put, in JS, "constructor" can be interpreted as a function called using the new operator. However, what we need to know is that functions in JS are not constructors, only when new is used, the function call will become constructor call .
3. "Class-oriented" in JS
function Animal(name) {
this.name = name;
}
Animal.prototype.sayName = function () {
console.log(this.name);
};
const dog = new Animal('dog');
const cat = new Animal('cat');
dog.sayName(); // dog
cat.sayName(); // cat
console.log(dog.constructor); // [Function: Animal]
this.name = name
adds a name attribute to each object through the implicit binding of , which is a bit like a data value encapsulated by a class instance.Animal.prototype.sayName = ...
will add a property (function) to theAnimal.prototype
During the creation process,dog
andcat
), the internal link of this new object " prototype " will be associated withAnimal.prototype
. Whendog
andcat
can not be foundsayName
, it willAnimal.prototype
found on.Note that,
dog.constructor
pointingAnimal
function, sodog
ofconstructor
property, seems to represent thedog
by whom constructed. But in fact, this is only seem so becausedog
itself does notconstructor
property,constructor
property andsayName
, same asAnimal.prototype
property, this property anddog
(orcat
no connection between).For
Animal.prototype
,constructor
is onlyAnimal
function is declared (it is not enumerable, but it can be changed),Animal.prototype.constructor = 'animal'; console.log(dog.constructor); // 'animal'
When changing the
Animal.prototype
, the orientation of theconstructor
attribute also becomes confusing. This is because thefish
attribute does not exist onconstructor
, soconstructor
Animal.prototype
(that is,{}
) is searched, but{}
also does not have theconstructor
attribute, so it will continue to findObject.prototype
. This object has theconstructor
, which points to the built-in functionObject
Animal.prototype = {}; const fish = new Animal(); console.log(fish.constructor); // [Function: Object]
Of course, we can manually specify the constructor attribute.
Animal.prototype = {}; Object.defineProperty(Animal.prototype, 'constructor', { enumerable: false, // 不可枚举 writable: true, configurable: true, value: Animal, // 让 constructor 指向 Animal }); const fish = new Animal(); console.log(fish.constructor); // [Function: Animal]
All in all, the
constructor
is just a common attribute that may be changed. The reference ofdog.constructor
4. Inheritance
The prototype style of inherits :
function Animal(name) {
this.name = name;
}
Animal.prototype.sayName = function () {
console.log(this.name);
};
function Dog(name, color) {
Animal.call(this, name);
this.color = color;
}
// 创建了一个新的 Dog.prototype 对象并关联到 Animal.prototype
Dog.prototype = Object.create(Animal.prototype);
//Object.setPrototypeOf( Dog.prototype, Animal.prototype )
// 注意!现在 Dog.prototype.constructor 的指向已经变为了Animal
Dog.prototype.sayName = function () {
console.log('重写sayName');
//显式多态,调用Animal.prototype.sayName
Animal.prototype.sayName.call(this);
};
const teddy = new Dog('泰迪', '棕色');
teddy.sayName(); // 重写sayName 泰迪
When declaring Dog
, like all functions, Dog
will have a prototype
attribute pointing to the default object (assuming the object name is originObj
), but originObj
not what we want Foo.prototype
. So we created a new object and associated this new object to Foo.prototype
, discarding the default object originObj
. The above code is implemented Object.create
Of course, it can also be achieved by ES6's Object.setPrototypeOf
, Object.setPrototypeOf( Dog.prototype, Animal.prototype )
, this function is to modify originObj
, rather than abandon originObj
, and therefore through Object.setPrototypeOf
, Dog.prototype.constructor
point does not change.
We can use instanceof
to check the relationship between teddy
and Dog
or Animal
console.log(teddy instanceof Animal);
instanceof
left is a ordinary objects A , the right side is a function B , the operator checks B.prototype exists in A of " the prototype " chain.
If you want to check two ordinary objects relationship between, you can use isPrototypeOf
console.log(Animal.prototype.isPrototypeOf(teddy));
5.Class syntax in ES6
You might think that ES6's class syntax introduces a new "class" mechanism to JS, but that's not the case. The class is basically just a syntactic sugar of the " prototype "
class Animal {
constructor(name) {
this.name = name;
}
sayName() {
console.log(this.name);
}
}
class Dog extends Animal {
constructor(name, color) {
super(name);
this.color = color;
}
sayName() {
console.log('重写sayName');
//相对多态
super.sayName();
}
}
const teddy = new Dog('泰迪', 'brown');
teddy.sayName();
In addition to the better syntax, what other problems does ES6 solve?
- No longer quote the messy
prototype
. Dog
directly "inherited"Animal
, no longer need to passObject.create
to replace
For theprototype
object, there is no need to set__proto__
orObject.setPrototypeOf
.- Relative polymorphism can be achieved by
super
, so that any method can refer to the party with the same name in the upper layer of the prototype chain
Law. No need to use explicit polymorphism,Animal.prototype.sayName.call(this);
class
literal syntax cannot declare attributes (only methods can be declared) . It looks like this is a restriction, but it will exclude
Drop many bad situations, otherwise, the "instance" at the end of the prototype chain may be accidentally obtained
Properties elsewhere (these properties are implicitly "shared" by all "instances"). So, the class syntax is actually
Can help you avoid making mistakes .- You can
extends
, and even the built-in object (sub)type, such as
Array or RegExp. Without the class ..extends syntax, it is very difficult to achieve this.
Note that,
super
andthis
different,super
not dynamic binding. ThetestObj.sayName
below does not point to its current " prototype " objecttestParen
, but points toAnimal
.const testObj = { name: 'test', sayName: Dog.prototype.sayName, }; const testParen = { sayName() { console.log('testParen'); }, }; Object.setPrototypeOf(testObj, testParen); testObj.sayName();//重写sayName test
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。