js的原型与原型链


  一、原型与原型链名词解释

    1.什么是原型(原型为了实现对象之间的联系)

      在c++ 中,我们可以知道,类是事物的抽象,通过类可以生成一个个实例化的具体对象,类提供着生成对象的“模板”。在 JavaScript 中构造函数(constructor)就起着“模板”的作用,通过构造函数,我们可以生成实例化的对象。

      所以JS中一个可以被复制(克隆)的类,其通过复制原型可以创建一个一模一样新的对象,每一个对象都会从原型中“继承”属性,通俗的讲原型就相当于是对象模板。

    2.什么是原型链(原型链解决继承问题)

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

  二、prototype属性

    1、原型定义公用的属性和方法,利用原型创建出来的新对象共享原型的所有的属性和方法(严格模式下也是如此)

 1     // 创建原型
 2     var Person = function(name){
 3         this.name = name;
 4     };
 5     // 原型的方法
 6    Person.prototype.sayHello = function(){
 7        console.log(this.name+",hello");
 8    };
 9    // 实例化创建新的原型对象,新的原型对象会共享原型的属性和方法
10    var person1 = new Person("zhangsan");
11    var person2 = new Person("lisi");
12    person1.sayHello();   // zhangsan,hello
13    person2.sayHello();   // lisi,hello

  每一个实例对象都有自己的属性和方法的副本,由于无法做到数据共享,导致资源的浪费。所以,为了解决数据无法共享的问题,引入了prototype属性,prototype属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法,也就是说prototype是通过调用构造函数而创建的那个对象实例的原型对象。因此我们可以将实例对象需要共享的属性和方法都放在这个对象中。    

    2、通过原型创建新对象实例是相互独立的,为新对象实例添加的方法只有实例拥有这个方法,其他实例是不拥有这个方法

 1      // 创建原型
 2     var Person = function(name){
 3         this.name = name;
 4     };
 5     // 原型的方法
 6    Person.prototype.sayHello = function(){
 7        console.log(this.name+",hello");
 8    };
 9    // 实例化创建新的原型对象,新的原型对象会共享原型的属性和方法
10    var person1 = new Person("zhangsan");
11    var person2 = new Person("lisi");
12    person1.sayHello();  // zhangsan,hello
13    person2.sayHello();  // lisi,hello
14    // 为新对象实例添加方法
15    // 通过原型创建的新对象实例是相互独立的
16    person1.getName = function(){
17        console.log(this.name);
18    }
19    
20    person1.getName();  // zhangsan
21    
22    person2.getName();  // Uncaught TypeError: person2.getName is not a function

  三、__proto__属性

    1.原型链是原型对象创建过程的历史记录,当访问一个对象的某个属性时,会先在这个对象本身属性上查找,如果没有找到,则会去它的__proto__隐式原型上查找,即它的构造函数的prototype,如果还没有找到就会再在构造函数的prototype的__proto__中查找,这样一层一层向上查找就会形成一个链式结构

    2.JavaScript 的继承是基于原型链的,在原型链的任何位置设置属性,都能被对象访问到,原型的作用也是在此,它可以包含所有实例共享的属性和方法,就像该属性本来就在实例对象上一样,与其说是继承,不如说原型链建立了一个链条,可以顺藤摸瓜,实例对象可以访问这根链条上的属性和方法。

    3.基于原型链的继承其实随处可见,只是我们没有意识到。当你随手新建一个数组,是否想过它怎么会有 splice、indexOf 等方法,新建一个函数怎么可以直接使用 call 和 bind?其实数组都继承于 Array.prototype,函数都继承于 Function.prototype。

1 var a = ['hello', 'world']
2 
3 function f() {}
4 
5 console.log(a.__proto__ === Array.prototype)      // true
6 console.log(f.__proto__ === Function.prototype)   // true

  四、constructor属性

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

  五、如果再往上寻找呢?Object.prototype 的原型会是什么?

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

    JavaScript 中的所有对象都来自 Object,Object 位于原型链的最顶端,几乎所有 JavaScript 的实例对象都是基于 Object。null 没有原型,所以 Object.prototype 就是原型链的最顶端。