Home  >  Article  >  Web Front-end  >  In-depth understanding of JavaScript prototype concept code examples (picture)

In-depth understanding of JavaScript prototype concept code examples (picture)

黄舟
黄舟Original
2017-03-11 14:44:571534browse

Prototype is a difficult concept to understand in JavaScript. There are many attributes related to prototype. Objects have "[[prototype]]" attributes, function objects have "prototype" attributes, and prototype objects have "constructor" attributes.

In order to clarify the prototype and the attribute relationships related to the prototype, this article is written.

I believe that through this article, you will be able to clearly understand the prototype, and start the prototype journey now.

Understanding prototypes

Before starting the introduction of prototypes, let’s first understand what a prototype is?

In JavaScript, The prototype is also an object. The property inheritance of the object can be realized through the prototype. JavaScript objects all contain a "[[Prototype]]" internal property. This property The corresponding one is the prototype of the object.

"[[Prototype]]" as an internal property of the object cannot be directly accessed. So in order to conveniently view the prototype of an object, Firefox and Chrome provide the non-standard (not supported by all browsers) "__proto__" accessor (ECMA introduced the standard object prototype accessor "Object. getPrototype(object)").

Example Analysis

Let’s take a look at prototype-related concepts through an example:

function Person(name, age){
    this.name = name;
    this.age = age;

    this.getInfo = function(){
        console.log(this.name + " is " + this.age + " years old");
    };
}

var will = new Person("Will", 28);

In the above code, a will object is created through the Person constructor. Let’s use the will object to understand the prototype step by step.

Step 1: View the prototype of the object will

Through the following code, you can view the prototype of the object will:

console.log(will.__proto__);
console.log(will.constructor);

Result analysis:

  • The "Person {}" object is the prototype of the object will. You can see by expanding it in Chrome that "Person {}", as a prototype object, also has the "__proto__" attribute (corresponding to the prototype of the prototype).

  • In this code, the "constructor" attribute is also used. In JavaScript's prototype object , it also contains a "constructor" attribute, which corresponds to the constructor function that creates all instances pointing to the prototype.

    • Through the "constructor" attribute, we can determine whether an object is an array type

      function isArray(myArray) {
          return myArray.constructor.toString().indexOf("Array") > -1;
      }
    • Here, The will object itself does not have the "constructor" attribute, but through the prototype chain search, the "constructor" attribute of the will prototype (will.__proto__) was found, and the Person function was obtained.

Step 2: View the prototype of the object will (will.__proto__)

Since the prototype of will "Person {}" is also an object, we can also check the prototype of "will's prototype (will.__proto__)".

Run the following code:

console.log(will.__proto__ === Person.prototype);
console.log(Person.prototype.__proto__);
console.log(Person.prototype.constructor);
console.log(Person.prototype.constructor === Person);

Result analysis:

  • First look at "will.__proto__ === Person.prototype", In JavaScript, each function has a prototype attribute. When a function is used as a constructor to create an instance, the prototype attribute value of the function will be assigned as a prototype to all object instances (that is, setting the instance's __proto__ attribute), that is, the prototypes of all instances refer to the prototype attribute of the function. After understanding the prototype attribute of the constructor, you will definitely understand why the first sentence results in true.

    • The prototype attribute is unique to function objects. If it is not a function object, there will not be such an attribute.

  • When you get the prototype of the will object prototype through the "Person.prototype.__proto__" statement, you will get the "Object {}" object, and you will see all the objects later. The prototype will be traced back to the "Object {}" object.

  • For the "constructor" of the prototype object "Person.prototype", according to the previous introduction, it will correspond to the Person function itself.

As you can see from the above, the "Person.prototype" object and the Person function object realize mutual reference through the "constructor" and "prototype" attributes (there will be pictures later This mutual reference relationship).

#Step 3: View the prototype of the object Object

As you can see from the previous part, the prototype of will’s prototype is “Object {}" object. In fact, in JavaScript, the prototype of all objects will be traced back to the "Object {}" object.

Let's take a look at the "Object {}" object through a piece of code:

console.log(Person.prototype.__proto__ === Object.prototype);
console.log(typeof Object);
console.log(Object);
console.log(Object.prototype);
console.log(Object.prototype.__proto__);
console.log(Object.prototype.constructor);

You can see it through the following code:

  • The Object object itself is A function object.

  • Since it is an Object function, there will definitely be a prototype attribute, so you can see that the value of "Object.prototype" is the prototype object "Object {}".

  • In turn, when accessing the "constructor" property of the "Object.prototype" object, you get the Object function.

  • 另外,当通过”Object.prototype.__proto__”获取Object原型的原型的时候,将会得到”null”,也就是说”Object {}”原型对象就是原型链的终点了。

Step 4: 查看对象Function的原型

在上面的例子中,Person是一个构造函数,在JavaScript中函数也是对象,所以,我们也可以通过”__proto__”属性来查找Person函数对象的原型。

console.log(Person.__proto__ === Function.prototype);
console.log(Person.constructor === Function)
console.log(typeof Function);
console.log(Function);
console.log(Function.prototype);
console.log(Function.prototype.__proto__);
console.log(Function.prototype.constructor);

结果分析 :

  • 在JavaScript中有个Function对象(类似Object),这个对象本身是个函数;所有的函数(包括Function,Object)的原型(__proto__)都是”Function.prototype”。

  • Function对象作为一个函数,就会有prototype属性,该属性将对应”function () {}”对象。

  • Function对象作为一个对象,就有”__proto__”属性,该属性对应”Function.prototype”,也就是说,”Function.__proto__ === Function.prototype”

  • 对于Function的原型对象”Function.prototype”,该原型对象的”__proto__”属性将对应”Object {}”

对比prototype和__proto__

对于”prototype”和”__proto__”这两个属性有的时候可能会弄混,”Person.prototype”和”Person.__proto__”是完全不同的。

在这里对”prototype”和”__proto__”进行简单的介绍:

  • 对于所有的对象,都有__proto__属性,这个属性对应该对象的原型

  • 对于函数对象,除了__proto__属性之外,还有prototype属性,当一个函数被用作构造函数来创建实例时,该函数的prototype属性值将被作为原型赋值给所有对象实例(也就是设置实例的__proto__属性)

图解实例

通过上面结合实例的分析,相信你一定了解了原型中的很多内容。

但是现在肯定对上面例子中的关系感觉很凌乱,一会儿原型,一会儿原型的原型,还有Function,Object,constructor,prototype等等关系。

现在就对上面的例子中分析得到的结果/关系进行图解,相信这张图可以让你豁然开朗。

对于上图的总结如下:

  • 所有的对象都有”__proto__”属性,该属性对应该对象的原型

  • 所有的函数对象都有”prototype”属性,该属性的值会被赋值给该函数创建的对象的”__proto__”属性

  • 所有的原型对象都有”constructor”属性,该属性对应创建所有指向该原型的实例的构造函数

  • 函数对象和原型对象通过”prototype”和”constructor”属性进行相互关联

通过原型改进例子

在上面例子中,”getInfo”方法是构造函数Person的一个成员,当通过Person构造两个实例的时候,每个实例都会包含一个”getInfo”方法。

var will = new Person("Will", 28);
var wilber = new Person("Wilber", 27);

前面了解到,原型就是为了方便实现属性的继承,所以可以将”getInfo”方法当作Person原型(Person.__proto__)的一个属性,这样所有的实例都可以通过原型继承的方式来使用”getInfo”这个方法了。

所以对例子进行如下修改:

function Person(name, age){
    this.name = name;
    this.age = age;
}

Person.prototype.getInfo = function(){
    console.log(this.name + " is " + this.age + " years old");
};

修改后的结果为:

原型链

因为每个对象和原型都有原型,对象的原型指向对象的父,而父的原型又指向父的父,这种原型层层连接起来的就构成了原型链。

在”理解JavaScript的作用域链“一文中,已经介绍了标识符和属性通过作用域链和原型链的查找。

这里就继续看一下基于原型链的属性查找。

属性查找

当查找一个对象的属性时,JavaScript 会向上遍历原型链,直到找到给定名称的属性为止,到查找到达原型链的顶部(也就是 “Object.prototype”), 如果仍然没有找到指定的属性,就会返回 undefined。

看一个例子:

function Person(name, age){
    this.name = name;
    this.age = age;
}

Person.prototype.MaxNumber = 9999;
Person.__proto__.MinNumber = -9999;

var will = new Person("Will", 28);

console.log(will.MaxNumber);
// 9999
console.log(will.MinNumber);
// undefined

在这个例子中分别给”Person.prototype “和” Person.__proto__”这两个原型对象添加了”MaxNumber “和”MinNumber”属性,这里就需要弄清”prototype”和”__proto__”的区别了。

“Person.prototype “对应的就是Person构造出来所有实例的原型,也就是说”Person.prototype “属于这些实例原型链的一部分,所以当这些实例进行属性查找时候,就会引用到”Person.prototype “中的属性。

属性隐藏

当通过原型链查找一个属性的时候,首先查找的是对象本身的属性,如果找不到才会继续按照原型链进行查找。

这样一来,如果想要覆盖原型链上的一些属性,我们就可以直接在对象中引入这些属性,达到属性隐藏的效果。

看一个简单的例子:

function Person(name, age){
    this.name = name;
    this.age = age;
}

Person.prototype.getInfo = function(){
    console.log(this.name + " is " + this.age + " years old");
};

var will = new Person("Will", 28);
will.getInfo = function(){
    console.log("getInfo method from will instead of prototype");
};

will.getInfo();
// getInfo method from will instead of prototype

对象创建方式影响原型链

会到本文开始的例子,will对象通过Person构造函数创建,所以will的原型(will.__proto__)就是”Person.prototype”。

同样,我们可以通过下面的方式创建一个对象:

var July = {
    name: "July",
    age: 28,
    getInfo: function(){
        console.log(this.name + " is " + this.age + " years old");
    },
}

console.log(July.getInfo());

当使用这种方式创建一个对象的时候,原型链就变成下图了,July对象的原型是”Object.prototype”也就是说对象的构建方式会影响原型链的形式。

hasOwnProperty

“hasOwnProperty”是”Object.prototype”的一个方法,该方法能判断一个对象是否包含自定义属性而不是原型链上的属性,因为”hasOwnProperty” 是 JavaScript 中唯一一个处理属性但是不查找原型链的函数。

相信你还记得文章最开始的例子中,通过will我们可以访问”constructor”这个属性,并得到will的构造函数Person。这里结合”hasOwnProperty”这个函数就可以看到,will对象并没有”constructor”这个属性。

从下面的输出可以看到,”constructor”是will的原型(will.__proto__)的属性,但是通过原型链的查找,will对象可以发现并使用”constructor”属性。

“hasOwnProperty”还有一个重要的使用场景,就是用来遍历对象的属性。

function Person(name, age){
    this.name = name;
    this.age = age;
}

Person.prototype.getInfo = function(){
    console.log(this.name + " is " + this.age + " years old");
};

var will = new Person("Will", 28);

for(var attr in will){
    console.log(attr);
}
// name
// age
// getInfo

for(var attr in will){
    if(will.hasOwnProperty(attr)){
        console.log(attr);
    }
}
// name
// age

总结

本文介绍了JavaScript中原型相关的概念,对于原型可以归纳出下面一些点:

  • 所有的对象都有”[[prototype]]”属性(通过__proto__访问),该属性对应对象的原型

  • 所有的函数对象都有”prototype”属性,该属性的值会被赋值给该函数创建的对象的”__proto__”属性

  • 所有的原型对象都有”constructor”属性,该属性对应创建所有指向该原型的实例的构造函数

  • 函数对象和原型对象通过”prototype”和”constructor”属性进行相互关联

还有要强调的是文章开始的例子,以及通过例子得到的一张”普通对象”,”函数对象”和”原型对象”之间的关系图,当你对原型的关系迷惑的时候,就想想这张图(或者重画一张当前对象的关系图),就可以理清这里面的复杂关系了。

通过这些介绍,相信一定可以对原型有个清晰的认识。

The above is the detailed content of In-depth understanding of JavaScript prototype concept code examples (picture). For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn