preface

In this article, we will try to comb through all the related objects, such as prototype, new, constructor, Instanceof, prototype chain, inheritance, class and so on

object

Before you can figure out what a prototype is, what a chain of prototypes is, what inheritance is, you have to figure out what an object is

An object in ECMAScript is simply a collection of data and functionality. Javascript Advanced Programming third edition

The redbook is pretty good at explaining objects

Object is a kind of data type. The data type in JS is divided into primitive type and reference type. Primitive type is also called basic type or value type.

Primitive type: Undefined, Null, Boolean, Number, String Reference type: Object

Null is a pointer to a Null object. Null is a pointer to a Null object. This is why object is returned when typeof NULL is used; Undefined was introduced in ECMA version 3 in order to formally distinguish between null object Pointers and uninitialized variables.

Es6 Symbol contains 7 types of data, including Array and Set Map.

An Array belongs to an Object, which is divided into:

  • Array
  • Function
  • Date
  • RegExp
  • Set
  • Map
  • .

Not letter you see

typeof Symbol()     // symbol
typeof []           // object
typeof new Date     // object
typeof new Set()    // object
typeof function(){} // function
Copy the code

Typeof function(){} returns function. This is because function is a subclass of object, but it implements a [[call]] method internally. To indicate that the object can be called, Typeof returns function if the object contains a [[call]] method. In other words, function is an actual object.

Now that you know what an object is, how do you get an object? Well, the first thing that comes to mind is you can declare an object by saying var O={}, you can declare an object by saying it literally, but this is just a shortcut that programmers call syntax sugar. Think about it, If you can define a RegExp, Array, or Object literal, you can also define new, which is the underlying implementation.

The Redbook explains two ways to define objects: new and literal:

  • New constructor
  • Object literals (short form to simplify object creation with a large number of attributes)

Since object is new, and that the new function is where, ha, ha, ha, function statement also there are two ways of “functional statement” and “statement” function expression, the difference is that the functional statement with the mechanism of promoting function, specific can consult before an article, and the essence of new also mentioned before, it is no longer stroke again.

You can refer to this article about the process of function promotion and you can refer to this article about the process of new

Know what the object is, where you come from, and then the way how to use object, is mainly refers to how to call the object properties, only through the “operator” and “square bracket syntax to invoke”, from the function to see no difference between the two methods, just the brackets grammatical function more rich, it supports the following three strengthening function:

  • Properties are accessed through variables
  • Attribute names contain characters that cause syntax errors
  • Attribute names use keywords or reserved words
var obj = {
  name: 'n',
  'var': 'v',
  'type name': 't'
}
var name = 'name'
obj[name]  // n
obj['var'] // v
obj['type name'] // t
Copy the code

Redbook recommends that we use the dot operator to access object properties except when we must access properties through variables

The prototype

New function (constructor)

Object is a collection of a set of data and function, the function is a subclass of the object, the function is also has properties such as the function. The function name, the name said function. The shape and length, length said function. The prototype is the prototype.

With the function fun () {}; For example, var obj = new fun(), fun is a function. When it is new, the new obj is an instance object. This function is new and we call it the constructor. Prototype === obj.__proto__

As an aside, when a function is new, the parentheses are used to pass arguments. If there are no arguments, the parentheses can be used to pass arguments. For example, in the example above, new fun is no different from new fun(). But the Redbook recommends that we write the little brackets after new function in any case.

Fun. prototype === obj.__proto__ === obj. Prototype is an object with a constructor property by default. Constructor points to the function itself, That is fun. Prototype. Constructor. = = = obj __proto__. The constructor = = = fun.

It is important to add that an instance has a __proto__ attribute because it is an object, which means that all objects have a __proto__ attribute.

New Object (common Object)

The relationship between custom functions, instance objects, and stereotypes was described above. Now let’s look at the relationship between objects declared by literals and stereotypes.

var obj1 = {a: 1}
var obj2 = new Object(); obj2.a = 1
Copy the code

Obj2 is derived from new Object, because the instance’s __proto__ is equal to the constructor’s prototype, So obj2. __proto__ = = = Object. The prototype obj2. __proto__. The constructor = = = Object. The prototype. The constructor = = = Object;

Obj1 is declared as a literal syntax sugar, and the underlying implementation is based on new, so it is still valid to replace obj2 with obj1.__proto__ === obj1.

function.proto(Functions are also objects)

All objects have a __proto__ attribute. Function is a subset of objects and an object. Function. So before we answer that question we need to understand how functions come from, since objects are new can functions also be new? Of course! This is where the distinction between function and function begins (case sensitive).

Function: The lowercase function is a joint word used to declare a function. Function: Uppercase Function is a built-in Function of JS, and all custom methods are instances of Function.

var fun1 = new Function('x', 'y', 'return x + y')
function fun2(){}
Copy the code

__proto__ === function. prototype; fun1 is still valid when replaced with fun2.

If Function can be declared as a Function by new, then by whom is Function new created, except Function itself? Function.__proto__ === Function. Prototype = Function.

__proto__ is a property on the instance object, and prototype is a property on the constructor, except for fun1, which is an instance of Function and can also be new as a constructor, so fun1 has both __proto__ and prototype.

And finally, how do these three things work out

fun.prototype ! == fun.__proto__ fun.prototype ! == Function.__proto__ fun.__proto__ === Function.__proto__

Prototype: fun.prototype === (new fun).__proto__; fun.prototype === (new fun).__proto__; Has nothing to do with fun.__proto__ Function.__proto__, equations 1 and 2 are true.

Fun.__proto__ === Function. Prototype, fun.__proto__ == Function. Prototype Function.__proto__ === Function. Prototype So fun.__proto__ === Function.

proto.proto(Prototype of prototype)

To summarize, an object has a __proto__ that points to a prototype, and a constructor has a prototype that points to a prototype, and a prototype is an object. What’s the __proto__ of a prototype?

function fun(){}
var objFun = new fun()
var obj1 = {a: 1}
var obj2 = new Object()
Copy the code

Prototype obj1.__proto__ === null obj1.__proto__.__proto__ === null obj1 obj2.__proto__.__proto__ === null Object.prototype.__proto__ === null

Function.proto__ === object.prototype Function.prototype.__proto__.__proto__ === null

The prototype has a look-up mechanism that returns null when it reaches the top of the prototype chain, i.e. the top of the prototype chain is NULL.

Instanceof (distinguish Array from Object)

When typeof is used to determine the reference type, the return value is object or function, which makes it impossible to distinguish the types such as Array and Date. You can use the instanceof command to distinguish the reference type. Instanceof is used to determine whether an instance belongs to a constructor.

Instanceof syntax is simple [] instanceof Array, judgment rule is along the [] proto lookup to see if this line and Array prototype having the same reference, this line will return true, Return false if the same reference has not been found.

function fun(){} var obj = new fun() fun instanceof Function // true obj instanceof fun // true obj instanceof Object //  true Array instanceof Function // true Array instanceof Object // true [] instanceof Array // true [] instanceof Object  // true Function instanceof Object // true new Date instanceof Object // true new Date instanceof Date // true Date instanceof Function // trueCopy the code

Instanceof Object returns true for any instanceof Object, since all internal instances of JS are based on Object.

Instance sharing stereotype method (Stereotype chain)

The prototype constructor also points to the prototype. We just need to attach the method common to the instance to the prototype and all its instances can access it. For example:

Object.prototype.logObj = function(){ console.log('logObj') }
Object.prototype.logMy = function(){ console.log('logMy_prototype') }
Function.prototype.logFun = function(){ console.log('logFun') }

var obj = {
  logMy: function(){ console.log('logMy') }
}
obj.logMy()   // logMy
obj.logObj()  // logObj
obj.logFun()  // err: not a function

function fun(){}
fun.logFun() // logFun
fun.logMy()  // logMy_prototype
Copy the code

In the case of obj.logMy and obj.logObj, when an object accesses a method (or property), it first looks for a private method of the object itself, such as obj.logMy. If it does not find one, it looks for a prototype method and property, such as obj.logObj. If it is not found, it will go up the __proto__ chain like fun.logMy until the top returns null, which is the prototype chain.

If Function. Prototype does not find the logMy attribute, it will then look up because Function. Prototype refers to an object, The Object is created based on Object so you’ll find object.prototype further down.

The common array methods like push, filter, and indexOf are implemented using this principle so that we can extend our own methods.

HasOwnProperty (the property itself is a stereotype)

Since instances can share properties and methods on stereotypes, how do you determine whether a property or method belongs to the instance itself or to the stereotype? Object. Prototype implements a built-in hasOwnProperty method to distinguish whether a property is from a prototype.

Object.prototype.a = 'a'
var obj = {b: 'b'}
obj.hasOwnProperty('a') // false
obj.hasOwnProperty('b') // true
Copy the code

inheritance

I’ve talked a lot about inheritance in the last seven, seven, eight, eight, and all of that, but there are a lot of different ways of inheritance that we’ll talk about later, but let’s see what inheritance is.

For example, there are 100 students in Class A. The similarities of the 100 students are that they all take classes in Class A, the courses they study are the same, the homeroom teacher is the same, etc. Of course, there are also differences such as name, age, gender, height, weight, etc. We use data to show how the 100 students should do? How to abstract out the same things as the parent class, each student’s personalized things as a subclass, subclass to inherit the parent class, so as to avoid repeating the code of each student’s information is complete and independent. Now, the problem with abstraction is that we need a parent class that has properties and methods, and a subclass that has properties and methods, and a subclass that has properties and methods of a parent class that doesn’t affect each other, and that’s inheritance.

The constructor implements inheritance

function fun() { this.name = 'fun' } fun.prototype.myLog = function() { console.log(1) } function obj() { fun.call(this)  this.type = 'child' } var O = new obj console.log(O.myLog) // undefinedCopy the code

Principle: The essence of inheritance via call is to change the “this” reference so that the “this” in the parent class refers to the context of the subclass, so that properties or methods set by “this” in the parent class are written to the subclass.

Disadvantages: Can only inherit properties and methods from the parent constructor, not from the parent prototype.

Inheritance is implemented through a chain of prototypes

function fun() { this.name = 'fun' this.arr = [1, 2, 3] } fun.prototype.myLog = function() { console.log(1) } function obj(type) { this.type = type } obj.prototype = new fun() var O1 = new obj('o1') var O2 = new obj('o2') O1.name = 'is O1' O1.arr.push('123') console.log(O1.myLog) // Log (O2.name) // fun console.log(O2.arr) // [1, 2, 3, '123']Copy the code

Principle: Obj. Prototype is assigned to an instance of its parent class. Prototype (fun instance) -> fun.prototype (obj instance) -> fun.prototype (obj instance) -> fun. You can also inherit properties from the parent stereotype.

Disadvantages: Because o1.proto === O2.proto, O1 and O2 will interact when changing properties on the parent constructor. In this example, O2.arr will change when o1.arr is changed. O1.name is changed o2. name is not changed because when setting the value, it will look up O1 itself first. No name attribute will set the name value on O1 itself. At this time, the name on proto is not affected at all. The values on O1 and O2 both on their own constructors and on their superclass constructors should be maintained independently and we don’t want them to interact.

Constructor + stereotype chain for inheritance

function fun() {
  this.name = 'fun'
  this.arr = [1, 2, 3]
}
fun.prototype.myLog = function() { console.log(1) }

function obj () {
  fun.call(this)
  this.type = 'obj'
}
obj.prototype = new fun()

var O1 = new obj()
var O2 = new obj()
O1.arr.push('123')

console.log(O1.arr)  // [1, 2, 3, '123']
console.log(O2.arr)  // [1, 2, 3]
Copy the code

Fun.call (this) : fun.call(this) : fun.call(this) : fun.call(this) : fun.call(this) Obj. Prototype = new Fun () implements attributes and methods on the parent class prototype.

Disadvantages: Call (this) and obj.prototype = new fun(), and properties on the parent constructor exist on both the subclass itself and the subclass’s prototype. Arr only deletes the ARR attribute on O1 itself. The prototype O1 still exists, and o1. arr is still accessible according to the prototype chain lookup mechanism.

function fun() {
  this.name = 'fun'
  this.arr = [1, 2, 3]
}
fun.prototype.myLog = function() { console.log(1) }

function obj () {
  fun.call(this)
  this.type = 'obj'
}
obj.prototype = new fun()

var O1 = new obj()
O1.arr.push('123')
console.log(O1.arr) // [1, 2, 3, "123"]
delete O1.arr
console.log(O1.arr) // [1, 2, 3]
Copy the code

Constructor + Prototype chain implementation inheritance (optimization)

function fun() { this.name = 'fun' this.arr = [1, 2, 3] } fun.prototype.myLog = function() { console.log(1) } function obj() { fun.call(this) this.type = 'obj' } Var O1 = new fun() var O2 = new obj() O1 instanceof obj // true O2 Instanceof obj // true (new fun()).__proto__. Constructor // Parent function (new obj()).__proto__. Constructor // parent functionCopy the code

Principle: this principle does not speak, above see understand this truth is the same.

Disadvantages: Because obj.prototype = fun.prototype, instances of parent and subclass cannot be distinguished.

Object. Create Implements inheritance

function fun() { this.name = 'fun' this.arr = [1, 2, 3] } fun.prototype.myLog = function() { console.log(1) } function obj() { fun.call(this) this.type = 'obj' } obj.prototype = Object.create(fun.prototype) obj.prototype.constructor = obj var O1 = new fun() var O2 = new obj() O1 // false O2 instanceof obj // true (new fun()).__proto__. Constructor // Parent fun() (new) __proto__. Constructor // Subclass function obj()Copy the code

How it works: Create an intermediate object using the create function to distinguish the two objects, because the prototype of the object created by create is the argument to the create function.

Advantages: the realization of inheritance, the realization of parent-child class isolation.

Object.assign(target, source, …) Assign the values of all enumerable attributes from one or more source objects to the target object; And returns the target object. Can be used to do object copy, when the target object has only the first level of attributes, no second level of attributes, this method is a deep copy, but when there are objects in the object, this method is a shallow copy after the second level of attributes.

Inherit multiple objects simultaneously

function fun1() { this.name1 = 'fun1' this.arr1 = [1, 2, 3] } fun1.prototype.myLog1 = function() { console.log(1) } function fun2() { this.name2 = 'fun2' this.arr2 = [11, 22, 33] } fun2.prototype.myLog2 = function() { console.log(2) } function obj() { fun1.call(this) fun2.call(this) this.type =  'obj' } obj.prototype = Object.assign(obj.prototype, fun1.prototype, fun2.prototype) obj.prototype.constructor = obj var O = new obj()Copy the code

Object.assign(target, … Sources) : This method is used to copy the values of all enumerable properties from one or more source objects to the target object, which will be returned.

The rest is completely consistent with the above explanation, so I won’t say more.

class

Class is new in ES6. Let’s look at how to use class to create an object and implement inheritance.

Class gets an object

Function Student(name) {this. Name = name} class. Prototype = function () { Console. log(' I am ${this.name}, my teacher is ${this.teacher}. Var xiaohong = new Student(' xiaohong ')Copy the code
Class Student {constructor(name) {// constructor(name) {this.teacher = '王 'this.name = name} hello() {// Define a function on the prototype object Console. log(' I am ${this.name}, my teacher is ${this.teacher}. }} var xiaoming = new Student(' xiaoming ') var xiaohong = new Student(' xiaoming ')Copy the code

If you use the class keyword to define a class, you need to create a new object. If you use the class keyword to define a class, you need to create a new object. If you use the class keyword, you need to create a new object.

The class inheritance

Class Base {constructor(name) {this.name = name this. School = 'xx university 'this. 'mathematics '] this. Teacher = '王 teacher'} modifyTeacher(tName) {this. Teacher = tName}} class extends Base { constructor(name) { super(name) this.time = new Date() } addCourse(course) { this.course.push(course) } } var xiaoming = New Student(' xiaohong ') var xiaohong = new Student(' xiaohong ')Copy the code

Extends: Extends extends extends extends extends from a parent class, which has properties and methods of the parent class. Extends means the prototype chain object comes from Base. Super () : Super is used to call the constructor of the parent class, otherwise the name property of the parent class will not be initialized properly.

Using the extends extends extends is much cleaner than using a prototype chain. Xiaoming and Xiaohong have properties such as time, name, school, course, and teacher. Have method modifyTeacher, addCourse, and do not affect each other.

What is the difference between the classes introduced in ES6 and the legacy JavaScript archetypal inheritance? In fact, there is no difference at all. The purpose of class is to let the JavaScript engine implement the prototype chain code that we would have written ourselves. In short, the benefit of using class is that it greatly simplifies the prototype chain code.