object htmldivelement什么意思_JavaScript原型链的意义是什么?

9ad268bc00e885b7e3a49aed6093740c.png

先说结论,JavaScript的原型链就是该编程语言为了实现面对对象编程的一种设计,基于原型链,可以让JavaScript对象拥有封装、继承和多态等众多面对对象特性。

如果你还单身着,或者你曾经单身过,那么你应该对计算机科学界中的面对对象编程有所耳闻。经验尚浅的杀杀觉得谈起找对象的话题来,Java和C#才是老大哥,当然C++也很的class也很经典,但今天它们都不是主角,今天要讲的是JavaScript在面对对象时的原型链设计。

0、序言

要讲雷锋塔,我们还是得先从雷锋开始说起(Java和JavaScript的关系与雷锋和雷锋塔之间的关系差不多)。

Java的面对对象风格是这样的:

//HelloWorld.java
public class HelloWorld{
 public static void main(String[] args){
  System.out.println("hello world");
 }
}

C#是这样的:

//HelloWorld.cs
public class HelloWorld
{
    public static void Main(string[] args)
    {
        System.Console.WriteLine(args[0]);
    }
}

差不多对吧?然后是C++的:

//HelloWorld.cpp
#include<iostream>
using namespace std;
class Hello
{
public:
    Hello()
    {
        baicout<<"Hello,";
    }
    ~Hello()
    {
        cout<<"world"<<endl;
    }
};
int main()
{
    Hello hello;
    return 0;
}

是不是觉得C#和Java的容易阅读一点?毕竟人家语言诞生的目的就是纯粹的面对对象编程,而C++只是是在C语言之上在增加了类和对象的特性,为了兼容C风格的代码,就还留着独立的入口main函数。

最后,JavaScript的对象一般是这样的:

//HelloWorld.cpp
var Hi = { hi:"HelloWorld",bye:"GoodByeWorld" };

我的天呐,这是一个对象?

对,看起来它好像一个python的字典对吧,但在雷锋塔中,它确实是一个叫做“对象”的妖怪。

在传统写法中,JavaScript生成实例对象的方法是通过构造函数:

function Point(x, y) {
  this.x = x;
  this.y = y;
}

Point.prototype.toString = function () {
  return '(' + this.x + ', ' + this.y + ')';
};

var p = new Point(1, 2);

也可以是这样:

person=new Object();
person.firstname="John";
person.lastname="Doe";
person.age=50;
person.eyecolor="blue";

但它还是长着妖怪的模样,所以ES6引入了class关键字这个语法糖:

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }
}

虽然看起来舒服多了,但它实际上只是相当于涂了点胭脂,好看了一些,“妖怪”的本质还是没有变化。那这个本质是什么呢?就是原型链。

所以,到底什么是JavaScript的原型链?

JavaScript的原型链就是该编程语言为了实现面对对象编程的一种设计,基于原型链,可以让JavaScript对象拥有封装、继承和多态等面对对象特性。

如果你是从Java或者C++开始学习编程的,那一定会对此感到混乱。所以接下来,就讲一下原型链是什么。

1、prototype

在JavaScript中,每个函数都有一个prototype属性,这个属性指向函数的原型对象。

function Person(age) {
    this.age = age       
}
Person.prototype.name = 'kavin'
var person1 = new Person()
var person2 = new Person()
console.log(person1.name) //kavin
console.log(person2.name)  //kavin

上述例子中,函数的prototype指向了一个对象,而这个对象正是调用构造函数时创建的实例的原型,也就是person1和person2的原型。

原型的概念:每一个javascript对象(除null外)创建的时候,就会与之关联另一个对象,这个对象就是我们所说的原型,每一个对象都会从原型中“继承”属性。

让我们用一张图表示构造函数和实例原型之间的关系:

7fbd5ec90941e624a49caad675025cc5.png

2、__proto__

这是每个对象(除null外)都会有的属性,叫做__proto__,这个属性会指向该对象的原型。

function Person() {

}
var person = new Person();
console.log(person.__proto__ === Person.prototype); // true

同样是用一张图表示:

e4c04ab877908b1885cba2faad44dcba.png

3、constructor

每个原型都有一个constructor属性,指向该关联的构造函数。

function Person() {

}
console.log(Person===Person.prototype.constructor)  //true

所以再更新下关系图:

aed6c234aadb6777294df688857c241c.png
function Person() {

}

var person = new Person();

console.log(person.__proto__ == Person.prototype) // true
console.log(Person.prototype.constructor == Person) // true

4、实例与原型

当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性,如果还查不到,就去找原型的原型,一直找到最顶层为止。

function Person() {

}

Person.prototype.name = 'Kevin';

var person = new Person();

person.name = 'Daisy';
console.log(person.name) // Daisy

delete person.name;
console.log(person.name) // Kevin

在这个例子中,我们给实例对象 person 添加了 name 属性,当我们打印 person.name 的时候,结果自然为 Daisy。

但是当我们删除了 person 的 name 属性时,读取 person.name,从 person 对象中找不到 name 属性就会从 person 的原型也就是 person.__proto__ ,也就是 Person.prototype中查找,幸运的是我们找到了 name 属性,结果为 Kevin。

但是万一还没有找到呢?原型的原型又是什么呢?

5.原型的原型

在前面,我们已经讲了原型也是一个对象,既然是对象,我们就可以用最原始的方式创建它,那就是:

var obj = new Object();
obj.name = 'Kevin'
console.log(obj.name) // Kevin

其实原型对象就是通过 Object 构造函数生成的,结合之前所讲,实例的 __proto__ 指向构造函数的 prototype ,所以我们再更新下关系图:

08bc80c4465afad4fe13d78c2a9d1947.png

6、原型链

简单的回顾一下构造函数、原型和实例的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。那么假如我们让原型对象等于另一个类型的实例,结果会怎样?显然,此时的原型对象将包含一个指向另一个原型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立。如此层层递进,就构成了实例与原型的链条。这就是所谓的原型链的基本概念。——摘自《javascript高级程序设计》

其实简单来说,就是上述4-5的过程。

继上述五中所说,那 Object.prototype 的原型呢?

console.log(Object.prototype.__proto__ === null) // true

null 表示“没有对象”,即该处不应该有值。

所以 Object.prototype.__proto__ 的值为 null 跟 Object.prototype 没有原型,其实表达了一个意思。

所以查找属性的时候查到 Object.prototype 就可以停止查找了。

最后一张关系图也可以更新为:

613e58a269f942ba1a0392bc22a16612.png

图中由相互关联的原型组成的链状结构就是原型链,也就是蓝色的这条线。

7、一张图秒懂JavaScript原型链

bced432fe1a71f7f29eee310e612f183.png

8、尾巴

相信上述1-7可以让你对JavaScript的原型链有所了解,但原型链存在的意义是什么呢?

让我们回到开头,再联系一下序幕中Java、C#、C++的面对对象原理,容易知道,JavaScript的原型链就是该编程语言为了实现面对对象编程的一种设计,基于原型链,可以让JavaScript对象拥有封装、继承和多态等众多面对对象特性。

就好像,继承关系在这里,变成了一条链表一样的数据结构,冥冥之中,影响着众多web开发者。