JS prototype, the prototype chain, classes, inheritance, class, extends, from shallow to deep

A constructor and prototype

1, constructors, static and instance members

Before for ES6, usually a special function called a constructor to define objects and their characteristics, and then use a constructor to create the object. Like other object-oriented languages, the attributes and methods of the abstract objects into the interior of the package.

function Person(uname, age) {
    this.uname = uname;
    this.age = age;
    this.say = function() {
        console.log('我叫' + this.uname + ',今年' + this.age + '岁。');
    }
}
var zhangsan = new Person('张三', 18);
zhangsan.say(); //输出:我叫张三,今年18岁。
var lisi = new Person('李四', 20);
lisi.say(); //输出:我叫李四,今年20岁。

When you create an object, use the new constructor always together (instead of calling). new creates a new object, then this points to the new object, so that we can assign to the new object by this, the code executes the function body, return to the new object (no need to write return).

Members (property / method), referred to as instance members (property / method) by an internal structure of this added function can only be accessed by an object instantiated, the constructor is no such member.

>> Person.uname
undefined

We can also add to the constructor of its own members, known as static members (properties / methods) can only be accessed by the constructor itself.

2, prototype

In the above example, we use constructor creates two objects zhangsan and lisi, they have their own independent properties and methods. For instance method, because the function is a complex data type, it will open up a dedicated memory storage function. Also, because the method zhangsan and lisi are independent, so say the method and the method say lisi of zhangsan respectively occupy two memory, even though they are the same set of code, do the same thing.

>> zhangsan.say === lisi.say
false

Just think, the more an instance method, the more objects you create, the more wasted space. To save space, we want all the objects to call the same method say. To achieve this purpose, it is necessary to use the prototype.

Each constructor has a prototype property, pointing to another object, called a prototype, as it is an object, also referred to as prototype object. (Hereinafter, in order not to create confusion, the constructor creates an object instance is called) on the other hand, there is an instance of the __proto__ property, by which also points to the prototype object. To distinguish, __ prototype proto__ pointing generally called objects, prototype called the prototype object.

There is also a prototype object constructor property that refers back to the constructor itself to record the prototype object reference from which constructor. In this way, constructors, prototypes and examples constitute a triangle.

Triangle relation

After the introduction of the prototype object constructor to be defined as follows:

function Person(uname, age) {
    this.uname = uname;
    this.age = age;
}
Person.prototype.say = function() {
    return '我叫' + this.uname + ',今年' + this.age + '岁。';
};

var zhangsan = new Person('张三', 18);
console.log(zhangsan.say());
var lisi = new Person('李四', 20);
console.log(lisi.say());
console.log(zhangsan.say === lisi.say); //输出:true

When looking for members of the object, first of all looking at the objects themselves, if they do not, they go to look for an object prototype by __proto__ on. By prototype object constructor function defined it is shared by all instances.

In general, we define the instance attribute to the constructor, the prototype object into the example method.

3, the prototype chain

Prototype object is an object that has its own prototype, point of Object.prototype Object prototype object.

>> Person.prototype.__proto__ === Object.prototype
true
>> Person.prototype.__proto__.constructor === Object
true

In other words, Person prototype object is created by the Object constructor.

Continue down the retroactive Object.prototype prototype:

>> Object.prototype.__proto__
null

Finally coming to an end. Looking back, we started retroactively from zhangsan this example:

zhangsan .__ prototype object of proto__ Person.

zhangsan .__ proto __.__ proto__ point to the prototype object of Object.prototype Object

zhangsan .__ therefore __.__ therefore __.__ proto__ 指向 null

This chain structure, called the prototype chain.

Prototype chain

Members of the object lookup mechanism relies on the prototype chain: When accessing an object's properties (or methods), first locate the object itself has no such property; if not to find its prototype; if it is not to find the prototype object prototype; to such push has been found so far null, this time returns undefined. __proto__ property offers a course for the lookup mechanism in one direction.

Once you have the concept of the prototype chain, and now new again review what has been done at the time of execution:

1. Create a new empty object in memory;

2. The property of the object point __proto__ prototype object constructor;

3. Let the constructor of this points to the new object;

4. Perform the constructor code to add members to the new object, and returns the new object.

Second, inheritance

ES6 before, how to achieve class inheritance? This use to call the method.

function.call(thisArg, arg1, arg2, ...)

thisArg: function specified in the function of this runtime values.

arg1, arg2, ...: to be passed to the function argument.

We know that inside there is a function called this property, pointing to the caller, window or undefined according to different scenarios. call this method allows us to specify another function is called.

var obj = {};
function f () {
    console.log(this === window, this === obj);
}
f(); //输出:true false
f.call(obj); //输出:false true

In the usual way calling function f, this point window; to call to call, this points to obj.

Call use, call the parent substructure constructor function, make this inner point by the parent class instance subclass instance, thereby completing the initialization part of the members in the parent constructor.

Examples of view, we have inherited a Student class from Person, not only have all the attributes and methods of Person, but also add a grade property indicates grades, a method used to test exam:

function Person(uname, age) {
    this.uname = uname;
    this.age = age;
}
Person.prototype.say = function() {
    return '我叫' + this.uname + ',今年' + this.age + '岁。';
};
function Student(uname, age, grade) {
    Person.call(this, uname, age);
    this.grade = grade;
}
Student.prototype.exam = function() {
    console.log('正在考试!');
};
var stu = new Student('张三', 16, '高一');
console.log(stu.uname, stu.age, stu.grade); //输出:张三 16 高一
stu.exam(); //输出:正在考试!

In the Student, call the Person function, so that it points to the inside of this Student of this, so uname and gave Student of this age, and finally to the prototype object added a exam method. Note that our goal is not to create an instance of a Person, so do not add new, just as the general constructor function call only.

Let's say we call the parent constructor method, to see if there is inherited.

>> stu.say()
TypeError: stu.say is not a function //报错了!
>> stu.say
undefined //stu实例并没有say这个成员

Oh, say Person.prototype is put in, but it did not produce stu contact and give change prototype chain. Also, because the prototype stu has hung up exam, can not directly change the point stu .__ proto__, had to revise down the prototype chain Student.prototype .__ proto__ point (it was originally directed Object.prototype):

>> Student.prototype.__proto__ = Person.prototype
>> stu.say()
"我叫张三,今年16岁。" //调用成功了!

inherit

say the implementation of the method, print out the name, age, but our Student constructor has added a grade, but also need to print out. This can make life difficult for the method say, after all that time when we define it is based on the Person, and no grade properties. So we have to override this method so that it can print grade, without affecting the original method say, and exam that is as linked to the Student.prototye.

>> Student.prototype.say = function() { return '我叫' + this.uname + ',今年' + this.age + '岁。' + this.grade + '学生。'; }
>> stu.say()
"我叫张三,今年16岁。高一学生。"

nailed it! We hit a wall several times, finally solved the question of succession.

He referred to the "parasitic combined inheritance", ideas and analysis above, as is the prototype object constructor + combination used in many materials. Except that only, not retained the original prototype object constructor sub, but it will point to another object () method by Object.create:

Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;

Object.create () method creates a new object, __proto__ this argument point to the new object passed as Person.prototype. Now designated as a prototype of another object, the constructor should refer back to the constructor.

In addition, there is another way of inheritance is also frequently mentioned, called "combined inheritance", have also changed the Student.prototype point:

Student.prototype = new Person(); //不用赋值,我们不关心原型里的uname和age
Student.prototype.constructor = Student;

Using the parent class instance as a value Student.prototype because __proto__ parent class prototype object instance certain point the parent constructor. Person drawbacks of doing so is to call a total of 2 times, and less than a part of the property in the presence of Student.prototype.

Now, one last question: the same code fragments and parent say there is method in the subclass method say, how to optimize this redundant? The answer is to call the constructor method say the father of the prototype:

Student.prototype.say = function() {
    return Person.prototype.say.call(this) + this.grade + '学生。';
};

Direct calls will only print out undefined, because this defaults to the caller, that Student.prototype, so use this call to modify the subclass instance.

Finally, attach a complete code, using the combined parasitic inheritance:

function Person(uname, age) {
    this.uname = uname;
    this.age = age;
}
Person.prototype.say = function() {
    return '我叫' + this.uname + ',今年' + this.age + '岁。';
};
function Student(uname, age, grade) {
    Person.call(this, uname, age);
    this.grade = grade;
}
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student; //别忘了把constructor指回来
//Student.__proto__ = Person; //这里挖个坑,后面填
Student.prototype.exam = function() {
    console.log('正在考试!');
};
Student.prototype.say = function() {
    return Person.prototype.say.call(this) + this.grade + '学生。';
};
var stu = new Student('张三', 16, '高一');
console.log(stu.say()); //输出:我叫张三,今年16岁。高一学生。

So the prototype chain increasingly grow:

2 prototype chain

To sum up the inherited ideas:

1. First, in daughter call the parent constructor method call the constructor, this refers to the modification, implementation instance of the parent class inherits attributes;

2. Modify and then point to prototype sub-constructors, both parasitic combined inheritance, succession or combined, or modify the way when our own to explore the nature of the prototype chain are linked to the parent handle class constructor prototype object on order to achieve the subclass inherits the instance of the parent class;

3. If a new instance of a subclass method, linked to the sub-constructor prototype;

4. If the method of Example subclass needs to call the parent class instance method, the parent by calling the constructor of the prototype, but to change this orientation.

+ Core is prototype object constructor function thereof. Only prototype object, a subclass can not inherit the parent class instance attributes; only the constructor, and the method can not inherit the prototype object. But after the two-pronged approach will be able to complement each. To make an inappropriate analogy, the Dragon rescue Xu Zhu Tian tong lao period, Tian tong lao broken leg mobility, but they have a certain magic; learn to run fast after Xu Zhu dodge, but he did not know how to use internal forces. They were successful at running the last. _ (: З "∠) _

Three, ES6 classes and inheritance

1, class

ES6 new concept in class, use the class keyword to define a class, grammar, and other object-oriented languages ​​are very similar.

class Person {
    constructor(uname, age) {
        this.uname = uname;
        this.age = age;
    }
    say() { //实例方法
        return `我叫${this.uname},今年${this.age}岁。`; //模板字符串
    }
    static staticMethod() { //静态方法
        console.log(`这是静态方法`);
    }
}
let zhangsan = new Person('张三', 18);
console.log(zhangsan.say());

important point:

1. Examples of attributes are defined in the constructor. Do not write constructor will be created by default.

2. The foregoing method does not require class plus function keyword, the methods do not need to be separated by commas.

3. Before adding the static keyword static methods, instance methods do not.

4.ES6 static property can not be defined within the class, or the need to use conventional Person.xxx Person [ 'xxx'].

5.class no variable lift must define a class, then the class is instantiated by the object.

2, inheritance

Implementation inheritance using the extends keyword:

class Person {
    constructor(uname, age) {
        this.uname = uname;
        this.age = age;
    }
    say() {
        return `我叫${this.uname},今年${this.age}岁。`;
    }
}
class Student extends Person {
    constructor (uname, age, grade) {
        super(uname, age);
        this.grade = grade;
    }
    say() {
        return `${super.say()}${this.grade}学生。`;
    }
    exam() {
        console.log('正在考试!');
    }
}
let stu = new Student('张三', 16, '高一');
console.log(stu.say());
stu.exam();

This code is an example of the ES6 front ES5 inherited version.

important point:

constructor 1. subclass, you must call the super method, otherwise it will error when creating a new instance.

2.constructor and say, although both used the super, but their significance is not the same, will speak later.

3, class nature

Let me talk Conclusion: class principle basically ES5 in the set, but the wording is more concise and clear.

Student use class definition is still a constructor, and before the prototype chain exactly the same:

>> typeof Person //class定义出来的仍然是一个函数
"function"
>> Person.prototype === (new Person()).__proto__
true
>> Person.prototype.constructor === Person //三角关系一模一样
true
>> stu.__proto__ instanceof Person //stu的原型是Person的实例
true
>> Object.getOwnPropertyNames(stu)
[ "uname", "age", "grade" ]
>> Object.getOwnPropertyNames(stu.__proto__)
[ "constructor", "say", "exam" ] //Student的say和exam挂在原型里

Object.getOwnPropertyNames () method to obtain the specified object names of all the properties and methods of hanging in his body (do not go looking for the prototype chain), which make up the name of the array returned. We can say, and exam visit by stu, because they hang in the prototype.

Other I will not try, directly to the conclusion:

1.class definition remains a constructor;

Examples of the method defined 2.class, hanging in the prototype object; static method, the constructor hanging himself;

3. There are two places subclass uses super, different meanings: constructor in, super function is viewed as a constructor super (uname, age) call the parent class representatives, the equivalent of Person.call (this, uname, age) in addition super () can only be used in the constructor; super.say say method () when the object is a super view, it points to the parent class prototype object Person.prototype, super.say () corresponds Person.protoype. say.call (this).

In fact, most of the classes of functions ES6, ES5 in both can be achieved. Of course, the introduction of class and extends so on JS more concise written, like other object-oriented programming language syntax. So ES6 is syntactic sugar in the class.

4, built-in objects inherit

The same can be inherited through the built-in objects extends:

class MyArray extends Array {
    constructor() {
        super();
    }
}
let a_es6 = new MyArray();
a_es6[1] = 'a';
console.log(a_es6.length); //输出:2

MyArray Array performance and almost exactly the same.

But if the practice ES5 want, for example, the combined inheritance:

function MyArray2() {
    Array.call(this);
}
MyArray2.prototype = new Array();
MyArray2.prototype.constructor = MyArray2;
var a_es5 = new MyArray2();
a_es5[1] = 'a';
console.log(a_es5.length); //输出:0

We give a_es5 position of the subscript assigned a value of 1, it is disappointing, length or 0.

Why these two classes of behavior is completely different? Because the combination of inheritance ES5, first create a subclass constructor the value of this, MyArray2 of this points to the newly created object, and then call the parent constructor so that members of the internal Array add to this, but this method can not be members of the internal Array. Look at the following example of simulation:

>> let o = {}
>> Object.getOwnPropertyNames(o)
[] //空列表
>> Array.call(o)
>> Object.getOwnPropertyNames(o)
[] //仍然是空列表

We tried Array.call (o) make empty object o acquire all of the properties Array, but failed, o What has not changed. "Inherited" from the Array a_es5 too, even its own length property are not, we can visit length is because it hanging on a prototype.

However, in the class ES6, this () created by the super first instance of the parent class Array point, followed by modify subclass values ​​on the basis of the parent class instance, and therefore in this ES6 can access the parent class instance.

Fourth, the function prototype

We know that, in addition to the function with the function and expression definition, you can also use new Function (parameter 1, parameter 2, ..., function body) way definition:

>> var f = new Function('a', 'b', 'console.log(a + b);')
>> f(1, 2)
3

In other words, all functions are Function this instance constructor, is an object, the internal function of both prototype property also has __proto__ property, the former pointing to own prototype object, which points to the prototype object Funtion. When we create a function (whether it be in what way to create ES5), new generally do these things:

1. Create an empty object in memory, here referred to as F;

2. make F .__ proto__ point Function.prototype;

3. () create another object with the new Object, referred to as proto;

4. Order proto.constructor point F.;

The proto so F.prototype point;

6. Return F.

In particular, the ES6 use extends inherit, subclasses __proto__ will point to the parent class to represent the inheritance relationship, rather than Function.prototype. (Remember dug a hole in the front of the parasitic modular code it inherited?)

Look at Function function. It also has a prototype object, Function.prototype. On the other hand, as an object, ES5 provisions Function of __proto__ attribute points to its own prototype object, namely Function .__ proto__ full equal Function.prototype.

Function.prototype are objects created by the new Object, therefore Function.prototype .__ proto__ point Object.prototype.

Now everything points Object.prototype, the prototype object that is Object, which is in addition to the null standing on top of the prototype chain of people, on top of it, Object.prototype .__ proto__ is null.

The ultimate prototype chain diagram:

Prototype chain 3

Fifth, the practical application of the prototype chain

In addition to inheritance and to find direction, the prototype chain can also be used in turn, blocks a built-in method described above do not want others to use. B to diffuse example, we want to save some sheets of comics down, first open reading comics page:

Read the comics page

See Figure 2 is two canvas. Extracts image information from the canvas, we thought toDataUrl and toBlob method. The former returned after a base64 encrypted data url, which returns the Blob object, no matter what, and finally can be converted into a picture file:

>> let c = document.getElementsByTagName('canvas')[0]
>> c.toDataUrl
undefined //没了
>> c.toBlob
undefined //这个也没了

Canvas class object to the HTMLCanvasElement, toDataUrl toBlob defined thereon, and the prototype object. By the lookup, JS code has a function of these two executed immediately attribute points to the undefined, in reader.xxxxxxxxxx.js this file inside (the 10 x is a placeholder for one of both digital and lowercase letters, For example, I now file named reader.8d59f9bef4.js).

……(前略)
function () {
  try {
    HTMLCanvasElement.prototype.toDataURL = void 0,
    HTMLCanvasElement.prototype.toBlob = void 0
  } catch (e) {}
}(),
……(后略)

Can not be extended ad block like this js file blocked, which would lead to the canvas element will not be generated, but you can use other methods to download the picture, is not the focus of this article, no details:

1.Chrome of sources put the Photo Gallery page directly out;

2. Firefox to canvas added a non-standard methods mozGetAsFile (), can be converted to File object, which is not sealed;

http requests and responses analyzed before and after 3, crawling with reptiles;

4. Replace the file fiddler local file in the local file course you can comment two lines.

Sixth, reference materials (extended reading)

1.ECMAScript

2. From the Object and Function of talk about the prototype chain JS

3.JavaScript(ES6) - Class

4. ES5 inheritance

Guess you like

Origin www.cnblogs.com/jushou233/p/11795943.html